diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-10-07 21:04:35 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-10-07 21:04:35 +0300 |
commit | 5d435a3f7b6cb1db566d0f56f5f8dc33be0dde69 (patch) | |
tree | 9633f2458c973fccc3ee0fa85b0b24cfe62aa6bf /drivers/staging | |
parent | 4078aa68509746d0c1a70c50ab22a761ad7c2e0d (diff) | |
parent | fbb6c848dd89786fe24856ee6b5e773910ded29c (diff) | |
download | linux-5d435a3f7b6cb1db566d0f56f5f8dc33be0dde69.tar.xz |
Merge tag 'media/v6.1-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab:
- New driver for Mediatek MDP V3
- New driver for NXP i.MX DW100 dewarper
- Zoran driver got promoted from staging
- Hantro and related drivers got promoted from staging
- Several VB1 drivers got moved to staging/deprecated (cpia2, fsl-viu,
meye, saa7146, av7110, stkwebcam, tm6000, vpfe_capture, davinci,
zr364xx)
- Usual set of driver fixes, improvements and cleanups
* tag 'media/v6.1-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (107 commits)
media: destage Hantro VPU driver
media: platform: mtk-mdp3: add MediaTek MDP3 driver
media: dt-binding: mediatek: add bindings for MediaTek CCORR and WDMA
media: dt-binding: mediatek: add bindings for MediaTek MDP3 components
media: xilinx: vipp: Fix refcount leak in xvip_graph_dma_init
media: xilinx: video: Add 1X12 greyscale format
media: xilinx: csi2rxss: Add 1X12 greyscale format
media: staging: media: imx: imx7-media-csi: Increase video mem limit
media: uvcvideo: Limit power line control for Sonix Technology
media: uvcvideo: Use entity get_cur in uvc_ctrl_set
media: uvcvideo: Fix typo 'the the' in comment
media: uvcvideo: Use indexed loops in uvc_ctrl_init_ctrl()
media: uvcvideo: Fix memory leak in uvc_gpio_parse
media: renesas: vsp1: Add support for RZ/G2L VSPD
media: renesas: vsp1: Add VSP1_HAS_NON_ZERO_LBA feature bit
media: renesas: vsp1: Add support for VSP software version
media: renesas: vsp1: Add support to deassert/assert reset line
media: dt-bindings: media: renesas,vsp1: Document RZ/G2L VSPD bindings
media: meson: vdec: add missing clk_disable_unprepare on error in vdec_hevc_start()
media: amphion: fix a bug that vpu core may not resume after suspend
...
Diffstat (limited to 'drivers/staging')
218 files changed, 37288 insertions, 19840 deletions
diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 421ce9dbf44c..d4f03b203ae5 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -22,10 +22,6 @@ if STAGING_MEDIA && MEDIA_SUPPORT # Please keep them in alphabetic order source "drivers/staging/media/atomisp/Kconfig" -source "drivers/staging/media/av7110/Kconfig" - -source "drivers/staging/media/hantro/Kconfig" - source "drivers/staging/media/imx/Kconfig" source "drivers/staging/media/ipu3/Kconfig" @@ -38,12 +34,31 @@ source "drivers/staging/media/omap4iss/Kconfig" source "drivers/staging/media/rkvdec/Kconfig" -source "drivers/staging/media/stkwebcam/Kconfig" - source "drivers/staging/media/sunxi/Kconfig" source "drivers/staging/media/tegra-video/Kconfig" -source "drivers/staging/media/zoran/Kconfig" +menuconfig STAGING_MEDIA_DEPRECATED + bool "Media staging drivers (DEPRECATED)" + default n + help + This option enables deprecated media drivers that are + scheduled for future removal from the kernel. + + If you wish to work on these drivers to prevent their removal, + then contact the linux-media@vger.kernel.org mailing list. + + If in doubt, say N here. + +if STAGING_MEDIA_DEPRECATED +source "drivers/staging/media/deprecated/cpia2/Kconfig" +source "drivers/staging/media/deprecated/fsl-viu/Kconfig" +source "drivers/staging/media/deprecated/meye/Kconfig" +source "drivers/staging/media/deprecated/saa7146/Kconfig" +source "drivers/staging/media/deprecated/stkwebcam/Kconfig" +source "drivers/staging/media/deprecated/tm6000/Kconfig" +source "drivers/staging/media/deprecated/vpfe_capture/Kconfig" +source "drivers/staging/media/deprecated/zr364xx/Kconfig" +endif endif diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 950e96f10aad..a387692b84f2 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -1,14 +1,18 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_INTEL_ATOMISP) += atomisp/ +obj-$(CONFIG_VIDEO_CPIA2) += deprecated/cpia2/ obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx/ obj-$(CONFIG_VIDEO_MAX96712) += max96712/ obj-$(CONFIG_VIDEO_MESON_VDEC) += meson/vdec/ +obj-$(CONFIG_VIDEO_MEYE) += deprecated/meye/ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ obj-$(CONFIG_VIDEO_ROCKCHIP_VDEC) += rkvdec/ -obj-$(CONFIG_VIDEO_STKWEBCAM) += stkwebcam/ +obj-$(CONFIG_VIDEO_STKWEBCAM) += deprecated/stkwebcam/ obj-$(CONFIG_VIDEO_SUNXI) += sunxi/ obj-$(CONFIG_VIDEO_TEGRA) += tegra-video/ -obj-$(CONFIG_VIDEO_HANTRO) += hantro/ obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3/ -obj-$(CONFIG_VIDEO_ZORAN) += zoran/ -obj-$(CONFIG_DVB_AV7110) += av7110/ +obj-$(CONFIG_VIDEO_TM6000) += deprecated/tm6000/ +obj-$(CONFIG_VIDEO_VIU) += deprecated/fsl-viu/ +obj-$(CONFIG_USB_ZR364XX) += deprecated/zr364xx/ +obj-y += deprecated/vpfe_capture/ +obj-y += deprecated/saa7146/ diff --git a/drivers/staging/media/av7110/TODO b/drivers/staging/media/av7110/TODO deleted file mode 100644 index 60062d8441b3..000000000000 --- a/drivers/staging/media/av7110/TODO +++ /dev/null @@ -1,3 +0,0 @@ -- This driver is too old and relies on a different API. - Drop it from Kernel on a couple of versions. -- Cleanup patches for the drivers here won't be accepted. diff --git a/drivers/staging/media/deprecated/cpia2/Kconfig b/drivers/staging/media/deprecated/cpia2/Kconfig new file mode 100644 index 000000000000..ee3b25a759d4 --- /dev/null +++ b/drivers/staging/media/deprecated/cpia2/Kconfig @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_CPIA2 + tristate "CPiA2 Video For Linux (DEPRECATED)" + depends on USB && VIDEO_DEV + help + This is the video4linux driver for cameras based on Vision's CPiA2 + (Colour Processor Interface ASIC), such as the Digital Blue QX5 + Microscope. If you have one of these cameras, say Y here + + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + + This driver is also available as a module (cpia2). diff --git a/drivers/staging/media/deprecated/cpia2/Makefile b/drivers/staging/media/deprecated/cpia2/Makefile new file mode 100644 index 000000000000..05664141f4d7 --- /dev/null +++ b/drivers/staging/media/deprecated/cpia2/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only +cpia2-objs := cpia2_v4l.o cpia2_usb.o cpia2_core.o + +obj-$(CONFIG_VIDEO_CPIA2) += cpia2.o diff --git a/drivers/staging/media/deprecated/cpia2/TODO b/drivers/staging/media/deprecated/cpia2/TODO new file mode 100644 index 000000000000..92ac8718d164 --- /dev/null +++ b/drivers/staging/media/deprecated/cpia2/TODO @@ -0,0 +1,6 @@ +The cpia2 driver does not use the vb2 framework for streaming +video, instead it implements this in the driver. + +To prevent removal of this driver early 2023 it has to be +converted to use vb2. Contact the linux-media@vger.kernel.org +mailing list if you want to do this. diff --git a/drivers/staging/media/deprecated/cpia2/cpia2.h b/drivers/staging/media/deprecated/cpia2/cpia2.h new file mode 100644 index 000000000000..57b7f1ea68da --- /dev/null +++ b/drivers/staging/media/deprecated/cpia2/cpia2.h @@ -0,0 +1,475 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/**************************************************************************** + * + * Filename: cpia2.h + * + * Copyright 2001, STMicrolectronics, Inc. + * + * Contact: steve.miller@st.com + * + * Description: + * This is a USB driver for CPiA2 based video cameras. + * + * This driver is modelled on the cpia usb driver by + * Jochen Scharrlach and Johannes Erdfeldt. + * + ****************************************************************************/ + +#ifndef __CPIA2_H__ +#define __CPIA2_H__ + +#include <linux/videodev2.h> +#include <linux/usb.h> +#include <linux/poll.h> +#include <media/v4l2-common.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> + +#include "cpia2_registers.h" + +/* define for verbose debug output */ +//#define _CPIA2_DEBUG_ + +/*** + * Image defines + ***/ + +/* Misc constants */ +#define ALLOW_CORRUPT 0 /* Causes collater to discard checksum */ + +/* USB Transfer mode */ +#define XFER_ISOC 0 +#define XFER_BULK 1 + +/* USB Alternates */ +#define USBIF_CMDONLY 0 +#define USBIF_BULK 1 +#define USBIF_ISO_1 2 /* 128 bytes/ms */ +#define USBIF_ISO_2 3 /* 384 bytes/ms */ +#define USBIF_ISO_3 4 /* 640 bytes/ms */ +#define USBIF_ISO_4 5 /* 768 bytes/ms */ +#define USBIF_ISO_5 6 /* 896 bytes/ms */ +#define USBIF_ISO_6 7 /* 1023 bytes/ms */ + +/* Flicker Modes */ +#define NEVER_FLICKER 0 +#define FLICKER_60 60 +#define FLICKER_50 50 + +/* Debug flags */ +#define DEBUG_NONE 0 +#define DEBUG_REG 0x00000001 +#define DEBUG_DUMP_PATCH 0x00000002 +#define DEBUG_DUMP_REGS 0x00000004 + +/*** + * Video frame sizes + ***/ +enum { + VIDEOSIZE_VGA = 0, /* 640x480 */ + VIDEOSIZE_CIF, /* 352x288 */ + VIDEOSIZE_QVGA, /* 320x240 */ + VIDEOSIZE_QCIF, /* 176x144 */ + VIDEOSIZE_288_216, + VIDEOSIZE_256_192, + VIDEOSIZE_224_168, + VIDEOSIZE_192_144, +}; + +#define STV_IMAGE_CIF_ROWS 288 +#define STV_IMAGE_CIF_COLS 352 + +#define STV_IMAGE_QCIF_ROWS 144 +#define STV_IMAGE_QCIF_COLS 176 + +#define STV_IMAGE_VGA_ROWS 480 +#define STV_IMAGE_VGA_COLS 640 + +#define STV_IMAGE_QVGA_ROWS 240 +#define STV_IMAGE_QVGA_COLS 320 + +#define JPEG_MARKER_COM (1<<6) /* Comment segment */ + +/*** + * Enums + ***/ +/* Sensor types available with cpia2 asics */ +enum sensors { + CPIA2_SENSOR_410, + CPIA2_SENSOR_500 +}; + +/* Asic types available in the CPiA2 architecture */ +#define CPIA2_ASIC_672 0x67 + +/* Device types (stv672, stv676, etc) */ +#define DEVICE_STV_672 0x0001 +#define DEVICE_STV_676 0x0002 + +enum frame_status { + FRAME_EMPTY, + FRAME_READING, /* In the process of being grabbed into */ + FRAME_READY, /* Ready to be read */ + FRAME_ERROR, +}; + +/*** + * Register access (for USB request byte) + ***/ +enum { + CAMERAACCESS_SYSTEM = 0, + CAMERAACCESS_VC, + CAMERAACCESS_VP, + CAMERAACCESS_IDATA +}; + +#define CAMERAACCESS_TYPE_BLOCK 0x00 +#define CAMERAACCESS_TYPE_RANDOM 0x04 +#define CAMERAACCESS_TYPE_MASK 0x08 +#define CAMERAACCESS_TYPE_REPEAT 0x0C + +#define TRANSFER_READ 0 +#define TRANSFER_WRITE 1 + +#define DEFAULT_ALT USBIF_ISO_6 +#define DEFAULT_BRIGHTNESS 0x46 +#define DEFAULT_CONTRAST 0x93 +#define DEFAULT_SATURATION 0x7f + +/* Power state */ +#define HI_POWER_MODE CPIA2_SYSTEM_CONTROL_HIGH_POWER +#define LO_POWER_MODE CPIA2_SYSTEM_CONTROL_LOW_POWER + + +/******** + * Commands + *******/ +enum { + CPIA2_CMD_NONE = 0, + CPIA2_CMD_GET_VERSION, + CPIA2_CMD_GET_PNP_ID, + CPIA2_CMD_GET_ASIC_TYPE, + CPIA2_CMD_GET_SENSOR, + CPIA2_CMD_GET_VP_DEVICE, + CPIA2_CMD_GET_VP_BRIGHTNESS, + CPIA2_CMD_SET_VP_BRIGHTNESS, + CPIA2_CMD_GET_CONTRAST, + CPIA2_CMD_SET_CONTRAST, + CPIA2_CMD_GET_VP_SATURATION, + CPIA2_CMD_SET_VP_SATURATION, + CPIA2_CMD_GET_VP_GPIO_DIRECTION, + CPIA2_CMD_SET_VP_GPIO_DIRECTION, + CPIA2_CMD_GET_VP_GPIO_DATA, + CPIA2_CMD_SET_VP_GPIO_DATA, + CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION, + CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION, + CPIA2_CMD_GET_VC_MP_GPIO_DATA, + CPIA2_CMD_SET_VC_MP_GPIO_DATA, + CPIA2_CMD_ENABLE_PACKET_CTRL, + CPIA2_CMD_GET_FLICKER_MODES, + CPIA2_CMD_SET_FLICKER_MODES, + CPIA2_CMD_RESET_FIFO, /* clear fifo and enable stream block */ + CPIA2_CMD_SET_HI_POWER, + CPIA2_CMD_SET_LOW_POWER, + CPIA2_CMD_CLEAR_V2W_ERR, + CPIA2_CMD_SET_USER_MODE, + CPIA2_CMD_GET_USER_MODE, + CPIA2_CMD_FRAMERATE_REQ, + CPIA2_CMD_SET_COMPRESSION_STATE, + CPIA2_CMD_GET_WAKEUP, + CPIA2_CMD_SET_WAKEUP, + CPIA2_CMD_GET_PW_CONTROL, + CPIA2_CMD_SET_PW_CONTROL, + CPIA2_CMD_GET_SYSTEM_CTRL, + CPIA2_CMD_SET_SYSTEM_CTRL, + CPIA2_CMD_GET_VP_SYSTEM_STATE, + CPIA2_CMD_GET_VP_SYSTEM_CTRL, + CPIA2_CMD_SET_VP_SYSTEM_CTRL, + CPIA2_CMD_GET_VP_EXP_MODES, + CPIA2_CMD_SET_VP_EXP_MODES, + CPIA2_CMD_GET_DEVICE_CONFIG, + CPIA2_CMD_SET_DEVICE_CONFIG, + CPIA2_CMD_SET_SERIAL_ADDR, + CPIA2_CMD_SET_SENSOR_CR1, + CPIA2_CMD_GET_VC_CONTROL, + CPIA2_CMD_SET_VC_CONTROL, + CPIA2_CMD_SET_TARGET_KB, + CPIA2_CMD_SET_DEF_JPEG_OPT, + CPIA2_CMD_REHASH_VP4, + CPIA2_CMD_GET_USER_EFFECTS, + CPIA2_CMD_SET_USER_EFFECTS +}; + +enum user_cmd { + COMMAND_NONE = 0x00000001, + COMMAND_SET_FPS = 0x00000002, + COMMAND_SET_COLOR_PARAMS = 0x00000004, + COMMAND_GET_COLOR_PARAMS = 0x00000008, + COMMAND_SET_FORMAT = 0x00000010, /* size, etc */ + COMMAND_SET_FLICKER = 0x00000020 +}; + +/*** + * Some defines specific to the 676 chip + ***/ +#define CAMACC_CIF 0x01 +#define CAMACC_VGA 0x02 +#define CAMACC_QCIF 0x04 +#define CAMACC_QVGA 0x08 + + +struct cpia2_register { + u8 index; + u8 value; +}; + +struct cpia2_reg_mask { + u8 index; + u8 and_mask; + u8 or_mask; + u8 fill; +}; + +struct cpia2_command { + u32 command; + u8 req_mode; /* (Block or random) | registerBank */ + u8 reg_count; + u8 direction; + u8 start; + union reg_types { + struct cpia2_register registers[32]; + struct cpia2_reg_mask masks[16]; + u8 block_data[64]; + u8 *patch_data; /* points to function defined block */ + } buffer; +}; + +struct camera_params { + struct { + u8 firmware_revision_hi; /* For system register set (bank 0) */ + u8 firmware_revision_lo; + u8 asic_id; /* Video Compressor set (bank 1) */ + u8 asic_rev; + u8 vp_device_hi; /* Video Processor set (bank 2) */ + u8 vp_device_lo; + u8 sensor_flags; + u8 sensor_rev; + } version; + + struct { + u32 device_type; /* enumerated from vendor/product ids. + * Currently, either STV_672 or STV_676 */ + u16 vendor; + u16 product; + u16 device_revision; + } pnp_id; + + struct { + u8 brightness; /* CPIA2_VP_EXPOSURE_TARGET */ + u8 contrast; /* Note: this is CPIA2_VP_YRANGE */ + u8 saturation; /* CPIA2_VP_SATURATION */ + } color_params; + + struct { + u8 cam_register; + u8 flicker_mode_req; /* 1 if flicker on, else never flicker */ + } flicker_control; + + struct { + u8 jpeg_options; + u8 creep_period; + u8 user_squeeze; + u8 inhibit_htables; + } compression; + + struct { + u8 ohsize; /* output image size */ + u8 ovsize; + u8 hcrop; /* cropping start_pos/4 */ + u8 vcrop; + u8 hphase; /* scaling registers */ + u8 vphase; + u8 hispan; + u8 vispan; + u8 hicrop; + u8 vicrop; + u8 hifraction; + u8 vifraction; + } image_size; + + struct { + int width; /* actual window width */ + int height; /* actual window height */ + } roi; + + struct { + u8 video_mode; + u8 frame_rate; + u8 video_size; /* Not a register, just a convenience for cropped sizes */ + u8 gpio_direction; + u8 gpio_data; + u8 system_ctrl; + u8 system_state; + u8 lowlight_boost; /* Bool: 0 = off, 1 = on */ + u8 device_config; + u8 exposure_modes; + u8 user_effects; + } vp_params; + + struct { + u8 pw_control; + u8 wakeup; + u8 vc_control; + u8 vc_mp_direction; + u8 vc_mp_data; + u8 quality; + } vc_params; + + struct { + u8 power_mode; + u8 system_ctrl; + u8 stream_mode; /* This is the current alternate for usb drivers */ + u8 allow_corrupt; + } camera_state; +}; + +#define NUM_SBUF 2 + +struct cpia2_sbuf { + char *data; + struct urb *urb; +}; + +struct framebuf { + u64 ts; + unsigned long seq; + int num; + int length; + int max_length; + volatile enum frame_status status; + u8 *data; + struct framebuf *next; +}; + +struct camera_data { + /* locks */ + struct v4l2_device v4l2_dev; + struct mutex v4l2_lock; /* serialize file operations */ + struct v4l2_ctrl_handler hdl; + struct { + /* Lights control cluster */ + struct v4l2_ctrl *top_light; + struct v4l2_ctrl *bottom_light; + }; + struct v4l2_ctrl *usb_alt; + + /* camera status */ + int first_image_seen; + enum sensors sensor_type; + u8 flush; + struct v4l2_fh *stream_fh; + u8 mmapped; + int streaming; /* 0 = no, 1 = yes */ + int xfer_mode; /* XFER_BULK or XFER_ISOC */ + struct camera_params params; /* camera settings */ + + /* v4l */ + int video_size; /* VIDEO_SIZE_ */ + struct video_device vdev; /* v4l videodev */ + u32 width; + u32 height; /* Its size */ + __u32 pixelformat; /* Format fourcc */ + + /* USB */ + struct usb_device *dev; + unsigned char iface; + unsigned int cur_alt; + unsigned int old_alt; + struct cpia2_sbuf sbuf[NUM_SBUF]; /* Double buffering */ + + wait_queue_head_t wq_stream; + + /* Buffering */ + u32 frame_size; + int num_frames; + unsigned long frame_count; + u8 *frame_buffer; /* frame buffer data */ + struct framebuf *buffers; + struct framebuf * volatile curbuff; + struct framebuf *workbuff; + + /* MJPEG Extension */ + int APPn; /* Number of APP segment to be written, must be 0..15 */ + int APP_len; /* Length of data in JPEG APPn segment */ + char APP_data[60]; /* Data in the JPEG APPn segment. */ + + int COM_len; /* Length of data in JPEG COM segment */ + char COM_data[60]; /* Data in JPEG COM segment */ +}; + +/* v4l */ +int cpia2_register_camera(struct camera_data *cam); +void cpia2_unregister_camera(struct camera_data *cam); +void cpia2_camera_release(struct v4l2_device *v4l2_dev); + +/* core */ +int cpia2_reset_camera(struct camera_data *cam); +int cpia2_set_low_power(struct camera_data *cam); +void cpia2_dbg_dump_registers(struct camera_data *cam); +int cpia2_match_video_size(int width, int height); +void cpia2_set_camera_state(struct camera_data *cam); +void cpia2_save_camera_state(struct camera_data *cam); +void cpia2_set_color_params(struct camera_data *cam); +void cpia2_set_brightness(struct camera_data *cam, unsigned char value); +void cpia2_set_contrast(struct camera_data *cam, unsigned char value); +void cpia2_set_saturation(struct camera_data *cam, unsigned char value); +int cpia2_set_flicker_mode(struct camera_data *cam, int mode); +void cpia2_set_format(struct camera_data *cam); +int cpia2_send_command(struct camera_data *cam, struct cpia2_command *cmd); +int cpia2_do_command(struct camera_data *cam, + unsigned int command, + unsigned char direction, unsigned char param); +void cpia2_deinit_camera_struct(struct camera_data *cam, struct usb_interface *intf); +struct camera_data *cpia2_init_camera_struct(struct usb_interface *intf); +int cpia2_init_camera(struct camera_data *cam); +int cpia2_allocate_buffers(struct camera_data *cam); +void cpia2_free_buffers(struct camera_data *cam); +long cpia2_read(struct camera_data *cam, + char __user *buf, unsigned long count, int noblock); +__poll_t cpia2_poll(struct camera_data *cam, + struct file *filp, poll_table *wait); +int cpia2_remap_buffer(struct camera_data *cam, struct vm_area_struct *vma); +void cpia2_set_property_flip(struct camera_data *cam, int prop_val); +void cpia2_set_property_mirror(struct camera_data *cam, int prop_val); +int cpia2_set_gpio(struct camera_data *cam, unsigned char setting); +int cpia2_set_fps(struct camera_data *cam, int framerate); + +/* usb */ +int cpia2_usb_init(void); +void cpia2_usb_cleanup(void); +int cpia2_usb_transfer_cmd(struct camera_data *cam, void *registers, + u8 request, u8 start, u8 count, u8 direction); +int cpia2_usb_stream_start(struct camera_data *cam, unsigned int alternate); +int cpia2_usb_stream_stop(struct camera_data *cam); +int cpia2_usb_stream_pause(struct camera_data *cam); +int cpia2_usb_stream_resume(struct camera_data *cam); +int cpia2_usb_change_streaming_alternate(struct camera_data *cam, + unsigned int alt); + + +/* ----------------------- debug functions ---------------------- */ +#ifdef _CPIA2_DEBUG_ +#define ALOG(lev, fmt, args...) printk(lev "%s:%d %s(): " fmt, __FILE__, __LINE__, __func__, ## args) +#define LOG(fmt, args...) ALOG(KERN_INFO, fmt, ## args) +#define ERR(fmt, args...) ALOG(KERN_ERR, fmt, ## args) +#define DBG(fmt, args...) ALOG(KERN_DEBUG, fmt, ## args) +#else +#define ALOG(fmt,args...) printk(fmt,##args) +#define LOG(fmt,args...) ALOG(KERN_INFO "cpia2: "fmt,##args) +#define ERR(fmt,args...) ALOG(KERN_ERR "cpia2: "fmt,##args) +#define DBG(fmn,args...) do {} while(0) +#endif +/* No function or lineno, for shorter lines */ +#define KINFO(fmt, args...) printk(KERN_INFO fmt,##args) + +#endif diff --git a/drivers/staging/media/deprecated/cpia2/cpia2_core.c b/drivers/staging/media/deprecated/cpia2/cpia2_core.c new file mode 100644 index 000000000000..b5a2d06fb356 --- /dev/null +++ b/drivers/staging/media/deprecated/cpia2/cpia2_core.c @@ -0,0 +1,2434 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/**************************************************************************** + * + * Filename: cpia2_core.c + * + * Copyright 2001, STMicrolectronics, Inc. + * Contact: steve.miller@st.com + * + * Description: + * This is a USB driver for CPia2 based video cameras. + * The infrastructure of this driver is based on the cpia usb driver by + * Jochen Scharrlach and Johannes Erdfeldt. + * + * Stripped of 2.4 stuff ready for main kernel submit by + * Alan Cox <alan@lxorguk.ukuu.org.uk> + * + ****************************************************************************/ + +#include "cpia2.h" + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/vmalloc.h> +#include <linux/firmware.h> +#include <linux/sched/signal.h> + +#define FIRMWARE "cpia2/stv0672_vp4.bin" +MODULE_FIRMWARE(FIRMWARE); + +/* #define _CPIA2_DEBUG_ */ + +#ifdef _CPIA2_DEBUG_ + +static const char *block_name[] = { + "System", + "VC", + "VP", + "IDATA" +}; +#endif + +static unsigned int debugs_on; /* default 0 - DEBUG_REG */ + + +/****************************************************************************** + * + * Forward Declarations + * + *****************************************************************************/ +static int apply_vp_patch(struct camera_data *cam); +static int set_default_user_mode(struct camera_data *cam); +static int set_vw_size(struct camera_data *cam, int size); +static int configure_sensor(struct camera_data *cam, + int reqwidth, int reqheight); +static int config_sensor_410(struct camera_data *cam, + int reqwidth, int reqheight); +static int config_sensor_500(struct camera_data *cam, + int reqwidth, int reqheight); +static int set_all_properties(struct camera_data *cam); +static void wake_system(struct camera_data *cam); +static void set_lowlight_boost(struct camera_data *cam); +static void reset_camera_struct(struct camera_data *cam); +static int cpia2_set_high_power(struct camera_data *cam); + +/* Here we want the physical address of the memory. + * This is used when initializing the contents of the + * area and marking the pages as reserved. + */ +static inline unsigned long kvirt_to_pa(unsigned long adr) +{ + unsigned long kva, ret; + + kva = (unsigned long) page_address(vmalloc_to_page((void *)adr)); + kva |= adr & (PAGE_SIZE-1); /* restore the offset */ + ret = __pa(kva); + return ret; +} + +static void *rvmalloc(unsigned long size) +{ + void *mem; + unsigned long adr; + + /* Round it off to PAGE_SIZE */ + 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 ((long)size > 0) { + SetPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + return mem; +} + +static void 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); +} + +/****************************************************************************** + * + * cpia2_do_command + * + * Send an arbitrary command to the camera. For commands that read from + * the camera, copy the buffers into the proper param structures. + *****************************************************************************/ +int cpia2_do_command(struct camera_data *cam, + u32 command, u8 direction, u8 param) +{ + int retval = 0; + struct cpia2_command cmd; + unsigned int device = cam->params.pnp_id.device_type; + + cmd.command = command; + cmd.reg_count = 2; /* default */ + cmd.direction = direction; + + /*** + * Set up the command. + ***/ + switch (command) { + case CPIA2_CMD_GET_VERSION: + cmd.req_mode = + CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; + cmd.start = CPIA2_SYSTEM_DEVICE_HI; + break; + case CPIA2_CMD_GET_PNP_ID: + cmd.req_mode = + CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; + cmd.reg_count = 8; + cmd.start = CPIA2_SYSTEM_DESCRIP_VID_HI; + break; + case CPIA2_CMD_GET_ASIC_TYPE: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; + cmd.start = CPIA2_VC_ASIC_ID; + break; + case CPIA2_CMD_GET_SENSOR: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.start = CPIA2_VP_SENSOR_FLAGS; + break; + case CPIA2_CMD_GET_VP_DEVICE: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.start = CPIA2_VP_DEVICEH; + break; + case CPIA2_CMD_SET_VP_BRIGHTNESS: + cmd.buffer.block_data[0] = param; + fallthrough; + case CPIA2_CMD_GET_VP_BRIGHTNESS: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + if (device == DEVICE_STV_672) + cmd.start = CPIA2_VP4_EXPOSURE_TARGET; + else + cmd.start = CPIA2_VP5_EXPOSURE_TARGET; + break; + case CPIA2_CMD_SET_CONTRAST: + cmd.buffer.block_data[0] = param; + fallthrough; + case CPIA2_CMD_GET_CONTRAST: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + cmd.start = CPIA2_VP_YRANGE; + break; + case CPIA2_CMD_SET_VP_SATURATION: + cmd.buffer.block_data[0] = param; + fallthrough; + case CPIA2_CMD_GET_VP_SATURATION: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + if (device == DEVICE_STV_672) + cmd.start = CPIA2_VP_SATURATION; + else + cmd.start = CPIA2_VP5_MCUVSATURATION; + break; + case CPIA2_CMD_SET_VP_GPIO_DATA: + cmd.buffer.block_data[0] = param; + fallthrough; + case CPIA2_CMD_GET_VP_GPIO_DATA: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + cmd.start = CPIA2_VP_GPIO_DATA; + break; + case CPIA2_CMD_SET_VP_GPIO_DIRECTION: + cmd.buffer.block_data[0] = param; + fallthrough; + case CPIA2_CMD_GET_VP_GPIO_DIRECTION: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + cmd.start = CPIA2_VP_GPIO_DIRECTION; + break; + case CPIA2_CMD_SET_VC_MP_GPIO_DATA: + cmd.buffer.block_data[0] = param; + fallthrough; + case CPIA2_CMD_GET_VC_MP_GPIO_DATA: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; + cmd.reg_count = 1; + cmd.start = CPIA2_VC_MP_DATA; + break; + case CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION: + cmd.buffer.block_data[0] = param; + fallthrough; + case CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; + cmd.reg_count = 1; + cmd.start = CPIA2_VC_MP_DIR; + break; + case CPIA2_CMD_ENABLE_PACKET_CTRL: + cmd.req_mode = + CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; + cmd.start = CPIA2_SYSTEM_INT_PACKET_CTRL; + cmd.reg_count = 1; + cmd.buffer.block_data[0] = param; + break; + case CPIA2_CMD_SET_FLICKER_MODES: + cmd.buffer.block_data[0] = param; + fallthrough; + case CPIA2_CMD_GET_FLICKER_MODES: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + cmd.start = CPIA2_VP_FLICKER_MODES; + break; + case CPIA2_CMD_RESET_FIFO: /* clear fifo and enable stream block */ + cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC; + cmd.reg_count = 2; + cmd.start = 0; + cmd.buffer.registers[0].index = CPIA2_VC_ST_CTRL; + cmd.buffer.registers[0].value = CPIA2_VC_ST_CTRL_SRC_VC | + CPIA2_VC_ST_CTRL_DST_USB | CPIA2_VC_ST_CTRL_EOF_DETECT; + cmd.buffer.registers[1].index = CPIA2_VC_ST_CTRL; + cmd.buffer.registers[1].value = CPIA2_VC_ST_CTRL_SRC_VC | + CPIA2_VC_ST_CTRL_DST_USB | + CPIA2_VC_ST_CTRL_EOF_DETECT | + CPIA2_VC_ST_CTRL_FIFO_ENABLE; + break; + case CPIA2_CMD_SET_HI_POWER: + cmd.req_mode = + CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_SYSTEM; + cmd.reg_count = 2; + cmd.buffer.registers[0].index = + CPIA2_SYSTEM_SYSTEM_CONTROL; + cmd.buffer.registers[1].index = + CPIA2_SYSTEM_SYSTEM_CONTROL; + cmd.buffer.registers[0].value = CPIA2_SYSTEM_CONTROL_CLEAR_ERR; + cmd.buffer.registers[1].value = + CPIA2_SYSTEM_CONTROL_HIGH_POWER; + break; + case CPIA2_CMD_SET_LOW_POWER: + cmd.req_mode = + CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; + cmd.reg_count = 1; + cmd.start = CPIA2_SYSTEM_SYSTEM_CONTROL; + cmd.buffer.block_data[0] = 0; + break; + case CPIA2_CMD_CLEAR_V2W_ERR: + cmd.req_mode = + CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; + cmd.reg_count = 1; + cmd.start = CPIA2_SYSTEM_SYSTEM_CONTROL; + cmd.buffer.block_data[0] = CPIA2_SYSTEM_CONTROL_CLEAR_ERR; + break; + case CPIA2_CMD_SET_USER_MODE: + cmd.buffer.block_data[0] = param; + fallthrough; + case CPIA2_CMD_GET_USER_MODE: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + if (device == DEVICE_STV_672) + cmd.start = CPIA2_VP4_USER_MODE; + else + cmd.start = CPIA2_VP5_USER_MODE; + break; + case CPIA2_CMD_FRAMERATE_REQ: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + if (device == DEVICE_STV_672) + cmd.start = CPIA2_VP4_FRAMERATE_REQUEST; + else + cmd.start = CPIA2_VP5_FRAMERATE_REQUEST; + cmd.buffer.block_data[0] = param; + break; + case CPIA2_CMD_SET_WAKEUP: + cmd.buffer.block_data[0] = param; + fallthrough; + case CPIA2_CMD_GET_WAKEUP: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; + cmd.reg_count = 1; + cmd.start = CPIA2_VC_WAKEUP; + break; + case CPIA2_CMD_SET_PW_CONTROL: + cmd.buffer.block_data[0] = param; + fallthrough; + case CPIA2_CMD_GET_PW_CONTROL: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; + cmd.reg_count = 1; + cmd.start = CPIA2_VC_PW_CTRL; + break; + case CPIA2_CMD_GET_VP_SYSTEM_STATE: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + cmd.start = CPIA2_VP_SYSTEMSTATE; + break; + case CPIA2_CMD_SET_SYSTEM_CTRL: + cmd.buffer.block_data[0] = param; + fallthrough; + case CPIA2_CMD_GET_SYSTEM_CTRL: + cmd.req_mode = + CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; + cmd.reg_count = 1; + cmd.start = CPIA2_SYSTEM_SYSTEM_CONTROL; + break; + case CPIA2_CMD_SET_VP_SYSTEM_CTRL: + cmd.buffer.block_data[0] = param; + fallthrough; + case CPIA2_CMD_GET_VP_SYSTEM_CTRL: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + cmd.start = CPIA2_VP_SYSTEMCTRL; + break; + case CPIA2_CMD_SET_VP_EXP_MODES: + cmd.buffer.block_data[0] = param; + fallthrough; + case CPIA2_CMD_GET_VP_EXP_MODES: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + cmd.start = CPIA2_VP_EXPOSURE_MODES; + break; + case CPIA2_CMD_SET_DEVICE_CONFIG: + cmd.buffer.block_data[0] = param; + fallthrough; + case CPIA2_CMD_GET_DEVICE_CONFIG: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + cmd.start = CPIA2_VP_DEVICE_CONFIG; + break; + case CPIA2_CMD_SET_SERIAL_ADDR: + cmd.buffer.block_data[0] = param; + cmd.req_mode = + CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; + cmd.reg_count = 1; + cmd.start = CPIA2_SYSTEM_VP_SERIAL_ADDR; + break; + case CPIA2_CMD_SET_SENSOR_CR1: + cmd.buffer.block_data[0] = param; + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + cmd.start = CPIA2_SENSOR_CR1; + break; + case CPIA2_CMD_SET_VC_CONTROL: + cmd.buffer.block_data[0] = param; + fallthrough; + case CPIA2_CMD_GET_VC_CONTROL: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; + cmd.reg_count = 1; + cmd.start = CPIA2_VC_VC_CTRL; + break; + case CPIA2_CMD_SET_TARGET_KB: + cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC; + cmd.reg_count = 1; + cmd.buffer.registers[0].index = CPIA2_VC_VC_TARGET_KB; + cmd.buffer.registers[0].value = param; + break; + case CPIA2_CMD_SET_DEF_JPEG_OPT: + cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC; + cmd.reg_count = 4; + cmd.buffer.registers[0].index = CPIA2_VC_VC_JPEG_OPT; + cmd.buffer.registers[0].value = + CPIA2_VC_VC_JPEG_OPT_DOUBLE_SQUEEZE; + cmd.buffer.registers[1].index = CPIA2_VC_VC_USER_SQUEEZE; + cmd.buffer.registers[1].value = 20; + cmd.buffer.registers[2].index = CPIA2_VC_VC_CREEP_PERIOD; + cmd.buffer.registers[2].value = 2; + cmd.buffer.registers[3].index = CPIA2_VC_VC_JPEG_OPT; + cmd.buffer.registers[3].value = CPIA2_VC_VC_JPEG_OPT_DEFAULT; + break; + case CPIA2_CMD_REHASH_VP4: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + cmd.start = CPIA2_VP_REHASH_VALUES; + cmd.buffer.block_data[0] = param; + break; + case CPIA2_CMD_SET_USER_EFFECTS: /* Note: Be careful with this as + this register can also affect + flicker modes */ + cmd.buffer.block_data[0] = param; + fallthrough; + case CPIA2_CMD_GET_USER_EFFECTS: + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 1; + if (device == DEVICE_STV_672) + cmd.start = CPIA2_VP4_USER_EFFECTS; + else + cmd.start = CPIA2_VP5_USER_EFFECTS; + break; + default: + LOG("DoCommand received invalid command\n"); + return -EINVAL; + } + + retval = cpia2_send_command(cam, &cmd); + if (retval) { + return retval; + } + + /*** + * Now copy any results from a read into the appropriate param struct. + ***/ + switch (command) { + case CPIA2_CMD_GET_VERSION: + cam->params.version.firmware_revision_hi = + cmd.buffer.block_data[0]; + cam->params.version.firmware_revision_lo = + cmd.buffer.block_data[1]; + break; + case CPIA2_CMD_GET_PNP_ID: + cam->params.pnp_id.vendor = (cmd.buffer.block_data[0] << 8) | + cmd.buffer.block_data[1]; + cam->params.pnp_id.product = (cmd.buffer.block_data[2] << 8) | + cmd.buffer.block_data[3]; + cam->params.pnp_id.device_revision = + (cmd.buffer.block_data[4] << 8) | + cmd.buffer.block_data[5]; + if (cam->params.pnp_id.vendor == 0x553) { + if (cam->params.pnp_id.product == 0x100) { + cam->params.pnp_id.device_type = DEVICE_STV_672; + } else if (cam->params.pnp_id.product == 0x140 || + cam->params.pnp_id.product == 0x151) { + cam->params.pnp_id.device_type = DEVICE_STV_676; + } + } + break; + case CPIA2_CMD_GET_ASIC_TYPE: + cam->params.version.asic_id = cmd.buffer.block_data[0]; + cam->params.version.asic_rev = cmd.buffer.block_data[1]; + break; + case CPIA2_CMD_GET_SENSOR: + cam->params.version.sensor_flags = cmd.buffer.block_data[0]; + cam->params.version.sensor_rev = cmd.buffer.block_data[1]; + break; + case CPIA2_CMD_GET_VP_DEVICE: + cam->params.version.vp_device_hi = cmd.buffer.block_data[0]; + cam->params.version.vp_device_lo = cmd.buffer.block_data[1]; + break; + case CPIA2_CMD_GET_VP_GPIO_DATA: + cam->params.vp_params.gpio_data = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_VP_GPIO_DIRECTION: + cam->params.vp_params.gpio_direction = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION: + cam->params.vc_params.vc_mp_direction =cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_VC_MP_GPIO_DATA: + cam->params.vc_params.vc_mp_data = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_FLICKER_MODES: + cam->params.flicker_control.cam_register = + cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_WAKEUP: + cam->params.vc_params.wakeup = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_PW_CONTROL: + cam->params.vc_params.pw_control = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_SYSTEM_CTRL: + cam->params.camera_state.system_ctrl = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_VP_SYSTEM_STATE: + cam->params.vp_params.system_state = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_VP_SYSTEM_CTRL: + cam->params.vp_params.system_ctrl = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_VP_EXP_MODES: + cam->params.vp_params.exposure_modes = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_DEVICE_CONFIG: + cam->params.vp_params.device_config = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_VC_CONTROL: + cam->params.vc_params.vc_control = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_USER_MODE: + cam->params.vp_params.video_mode = cmd.buffer.block_data[0]; + break; + case CPIA2_CMD_GET_USER_EFFECTS: + cam->params.vp_params.user_effects = cmd.buffer.block_data[0]; + break; + default: + break; + } + return retval; +} + +/****************************************************************************** + * + * cpia2_send_command + * + *****************************************************************************/ + +#define DIR(cmd) ((cmd->direction == TRANSFER_WRITE) ? "Write" : "Read") +#define BINDEX(cmd) (cmd->req_mode & 0x03) + +int cpia2_send_command(struct camera_data *cam, struct cpia2_command *cmd) +{ + u8 count; + u8 start; + u8 *buffer; + int retval; + + switch (cmd->req_mode & 0x0c) { + case CAMERAACCESS_TYPE_RANDOM: + count = cmd->reg_count * sizeof(struct cpia2_register); + start = 0; + buffer = (u8 *) & cmd->buffer; + if (debugs_on & DEBUG_REG) + DBG("%s Random: Register block %s\n", DIR(cmd), + block_name[BINDEX(cmd)]); + break; + case CAMERAACCESS_TYPE_BLOCK: + count = cmd->reg_count; + start = cmd->start; + buffer = cmd->buffer.block_data; + if (debugs_on & DEBUG_REG) + DBG("%s Block: Register block %s\n", DIR(cmd), + block_name[BINDEX(cmd)]); + break; + case CAMERAACCESS_TYPE_MASK: + count = cmd->reg_count * sizeof(struct cpia2_reg_mask); + start = 0; + buffer = (u8 *) & cmd->buffer; + if (debugs_on & DEBUG_REG) + DBG("%s Mask: Register block %s\n", DIR(cmd), + block_name[BINDEX(cmd)]); + break; + case CAMERAACCESS_TYPE_REPEAT: /* For patch blocks only */ + count = cmd->reg_count; + start = cmd->start; + buffer = cmd->buffer.block_data; + if (debugs_on & DEBUG_REG) + DBG("%s Repeat: Register block %s\n", DIR(cmd), + block_name[BINDEX(cmd)]); + break; + default: + LOG("%s: invalid request mode\n",__func__); + return -EINVAL; + } + + retval = cpia2_usb_transfer_cmd(cam, + buffer, + cmd->req_mode, + start, count, cmd->direction); +#ifdef _CPIA2_DEBUG_ + if (debugs_on & DEBUG_REG) { + int i; + for (i = 0; i < cmd->reg_count; i++) { + if((cmd->req_mode & 0x0c) == CAMERAACCESS_TYPE_BLOCK) + KINFO("%s Block: [0x%02X] = 0x%02X\n", + DIR(cmd), start + i, buffer[i]); + if((cmd->req_mode & 0x0c) == CAMERAACCESS_TYPE_RANDOM) + KINFO("%s Random: [0x%02X] = 0x%02X\n", + DIR(cmd), cmd->buffer.registers[i].index, + cmd->buffer.registers[i].value); + } + } +#endif + + return retval; +}; + +/************* + * Functions to implement camera functionality + *************/ +/****************************************************************************** + * + * cpia2_get_version_info + * + *****************************************************************************/ +static void cpia2_get_version_info(struct camera_data *cam) +{ + cpia2_do_command(cam, CPIA2_CMD_GET_VERSION, TRANSFER_READ, 0); + cpia2_do_command(cam, CPIA2_CMD_GET_PNP_ID, TRANSFER_READ, 0); + cpia2_do_command(cam, CPIA2_CMD_GET_ASIC_TYPE, TRANSFER_READ, 0); + cpia2_do_command(cam, CPIA2_CMD_GET_SENSOR, TRANSFER_READ, 0); + cpia2_do_command(cam, CPIA2_CMD_GET_VP_DEVICE, TRANSFER_READ, 0); +} + +/****************************************************************************** + * + * cpia2_reset_camera + * + * Called at least during the open process, sets up initial params. + *****************************************************************************/ +int cpia2_reset_camera(struct camera_data *cam) +{ + u8 tmp_reg; + int retval = 0; + int target_kb; + int i; + struct cpia2_command cmd; + + /*** + * VC setup + ***/ + retval = configure_sensor(cam, + cam->params.roi.width, + cam->params.roi.height); + if (retval < 0) { + ERR("Couldn't configure sensor, error=%d\n", retval); + return retval; + } + + /* Clear FIFO and route/enable stream block */ + cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC; + cmd.direction = TRANSFER_WRITE; + cmd.reg_count = 2; + cmd.buffer.registers[0].index = CPIA2_VC_ST_CTRL; + cmd.buffer.registers[0].value = CPIA2_VC_ST_CTRL_SRC_VC | + CPIA2_VC_ST_CTRL_DST_USB | CPIA2_VC_ST_CTRL_EOF_DETECT; + cmd.buffer.registers[1].index = CPIA2_VC_ST_CTRL; + cmd.buffer.registers[1].value = CPIA2_VC_ST_CTRL_SRC_VC | + CPIA2_VC_ST_CTRL_DST_USB | + CPIA2_VC_ST_CTRL_EOF_DETECT | CPIA2_VC_ST_CTRL_FIFO_ENABLE; + + cpia2_send_command(cam, &cmd); + + cpia2_set_high_power(cam); + + if (cam->params.pnp_id.device_type == DEVICE_STV_672) { + /* Enable button notification */ + cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_SYSTEM; + cmd.buffer.registers[0].index = CPIA2_SYSTEM_INT_PACKET_CTRL; + cmd.buffer.registers[0].value = + CPIA2_SYSTEM_INT_PACKET_CTRL_ENABLE_SW_XX; + cmd.reg_count = 1; + cpia2_send_command(cam, &cmd); + } + + schedule_timeout_interruptible(msecs_to_jiffies(100)); + + if (cam->params.pnp_id.device_type == DEVICE_STV_672) + retval = apply_vp_patch(cam); + + /* wait for vp to go to sleep */ + schedule_timeout_interruptible(msecs_to_jiffies(100)); + + /*** + * If this is a 676, apply VP5 fixes before we start streaming + ***/ + if (cam->params.pnp_id.device_type == DEVICE_STV_676) { + cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VP; + + /* The following writes improve the picture */ + cmd.buffer.registers[0].index = CPIA2_VP5_MYBLACK_LEVEL; + cmd.buffer.registers[0].value = 0; /* reduce from the default + * rec 601 pedestal of 16 */ + cmd.buffer.registers[1].index = CPIA2_VP5_MCYRANGE; + cmd.buffer.registers[1].value = 0x92; /* increase from 100% to + * (256/256 - 31) to fill + * available range */ + cmd.buffer.registers[2].index = CPIA2_VP5_MYCEILING; + cmd.buffer.registers[2].value = 0xFF; /* Increase from the + * default rec 601 ceiling + * of 240 */ + cmd.buffer.registers[3].index = CPIA2_VP5_MCUVSATURATION; + cmd.buffer.registers[3].value = 0xFF; /* Increase from the rec + * 601 100% level (128) + * to 145-192 */ + cmd.buffer.registers[4].index = CPIA2_VP5_ANTIFLKRSETUP; + cmd.buffer.registers[4].value = 0x80; /* Inhibit the + * anti-flicker */ + + /* The following 4 writes are a fix to allow QVGA to work at 30 fps */ + cmd.buffer.registers[5].index = CPIA2_VP_RAM_ADDR_H; + cmd.buffer.registers[5].value = 0x01; + cmd.buffer.registers[6].index = CPIA2_VP_RAM_ADDR_L; + cmd.buffer.registers[6].value = 0xE3; + cmd.buffer.registers[7].index = CPIA2_VP_RAM_DATA; + cmd.buffer.registers[7].value = 0x02; + cmd.buffer.registers[8].index = CPIA2_VP_RAM_DATA; + cmd.buffer.registers[8].value = 0xFC; + + cmd.direction = TRANSFER_WRITE; + cmd.reg_count = 9; + + cpia2_send_command(cam, &cmd); + } + + /* Activate all settings and start the data stream */ + /* Set user mode */ + set_default_user_mode(cam); + + /* Give VP time to wake up */ + schedule_timeout_interruptible(msecs_to_jiffies(100)); + + set_all_properties(cam); + + cpia2_do_command(cam, CPIA2_CMD_GET_USER_MODE, TRANSFER_READ, 0); + DBG("After SetAllProperties(cam), user mode is 0x%0X\n", + cam->params.vp_params.video_mode); + + /*** + * Set audio regulator off. This and the code to set the compresison + * state are too complex to form a CPIA2_CMD_, and seem to be somewhat + * intertwined. This stuff came straight from the windows driver. + ***/ + /* Turn AutoExposure off in VP and enable the serial bridge to the sensor */ + cpia2_do_command(cam, CPIA2_CMD_GET_VP_SYSTEM_CTRL, TRANSFER_READ, 0); + tmp_reg = cam->params.vp_params.system_ctrl; + cmd.buffer.registers[0].value = tmp_reg & + (tmp_reg & (CPIA2_VP_SYSTEMCTRL_HK_CONTROL ^ 0xFF)); + + cpia2_do_command(cam, CPIA2_CMD_GET_DEVICE_CONFIG, TRANSFER_READ, 0); + cmd.buffer.registers[1].value = cam->params.vp_params.device_config | + CPIA2_VP_DEVICE_CONFIG_SERIAL_BRIDGE; + cmd.buffer.registers[0].index = CPIA2_VP_SYSTEMCTRL; + cmd.buffer.registers[1].index = CPIA2_VP_DEVICE_CONFIG; + cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VP; + cmd.reg_count = 2; + cmd.direction = TRANSFER_WRITE; + cmd.start = 0; + cpia2_send_command(cam, &cmd); + + /* Set the correct I2C address in the CPiA-2 system register */ + cpia2_do_command(cam, + CPIA2_CMD_SET_SERIAL_ADDR, + TRANSFER_WRITE, + CPIA2_SYSTEM_VP_SERIAL_ADDR_SENSOR); + + /* Now have sensor access - set bit to turn the audio regulator off */ + cpia2_do_command(cam, + CPIA2_CMD_SET_SENSOR_CR1, + TRANSFER_WRITE, CPIA2_SENSOR_CR1_DOWN_AUDIO_REGULATOR); + + /* Set the correct I2C address in the CPiA-2 system register */ + if (cam->params.pnp_id.device_type == DEVICE_STV_672) + cpia2_do_command(cam, + CPIA2_CMD_SET_SERIAL_ADDR, + TRANSFER_WRITE, + CPIA2_SYSTEM_VP_SERIAL_ADDR_VP); // 0x88 + else + cpia2_do_command(cam, + CPIA2_CMD_SET_SERIAL_ADDR, + TRANSFER_WRITE, + CPIA2_SYSTEM_VP_SERIAL_ADDR_676_VP); // 0x8a + + /* increase signal drive strength */ + if (cam->params.pnp_id.device_type == DEVICE_STV_676) + cpia2_do_command(cam, + CPIA2_CMD_SET_VP_EXP_MODES, + TRANSFER_WRITE, + CPIA2_VP_EXPOSURE_MODES_COMPILE_EXP); + + /* Start autoexposure */ + cpia2_do_command(cam, CPIA2_CMD_GET_DEVICE_CONFIG, TRANSFER_READ, 0); + cmd.buffer.registers[0].value = cam->params.vp_params.device_config & + (CPIA2_VP_DEVICE_CONFIG_SERIAL_BRIDGE ^ 0xFF); + + cpia2_do_command(cam, CPIA2_CMD_GET_VP_SYSTEM_CTRL, TRANSFER_READ, 0); + cmd.buffer.registers[1].value = + cam->params.vp_params.system_ctrl | CPIA2_VP_SYSTEMCTRL_HK_CONTROL; + + cmd.buffer.registers[0].index = CPIA2_VP_DEVICE_CONFIG; + cmd.buffer.registers[1].index = CPIA2_VP_SYSTEMCTRL; + cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VP; + cmd.reg_count = 2; + cmd.direction = TRANSFER_WRITE; + + cpia2_send_command(cam, &cmd); + + /* Set compression state */ + cpia2_do_command(cam, CPIA2_CMD_GET_VC_CONTROL, TRANSFER_READ, 0); + if (cam->params.compression.inhibit_htables) { + tmp_reg = cam->params.vc_params.vc_control | + CPIA2_VC_VC_CTRL_INHIBIT_H_TABLES; + } else { + tmp_reg = cam->params.vc_params.vc_control & + ~CPIA2_VC_VC_CTRL_INHIBIT_H_TABLES; + } + cpia2_do_command(cam, CPIA2_CMD_SET_VC_CONTROL, TRANSFER_WRITE,tmp_reg); + + /* Set target size (kb) on vc + This is a heuristic based on the quality parameter and the raw + framesize in kB divided by 16 (the compression factor when the + quality is 100%) */ + target_kb = (cam->width * cam->height * 2 / 16384) * + cam->params.vc_params.quality / 100; + if (target_kb < 1) + target_kb = 1; + cpia2_do_command(cam, CPIA2_CMD_SET_TARGET_KB, + TRANSFER_WRITE, target_kb); + + /* Wiggle VC Reset */ + /*** + * First read and wait a bit. + ***/ + for (i = 0; i < 50; i++) { + cpia2_do_command(cam, CPIA2_CMD_GET_PW_CONTROL, + TRANSFER_READ, 0); + } + + tmp_reg = cam->params.vc_params.pw_control; + tmp_reg &= ~CPIA2_VC_PW_CTRL_VC_RESET_N; + + cpia2_do_command(cam, CPIA2_CMD_SET_PW_CONTROL, TRANSFER_WRITE,tmp_reg); + + tmp_reg |= CPIA2_VC_PW_CTRL_VC_RESET_N; + cpia2_do_command(cam, CPIA2_CMD_SET_PW_CONTROL, TRANSFER_WRITE,tmp_reg); + + cpia2_do_command(cam, CPIA2_CMD_SET_DEF_JPEG_OPT, TRANSFER_WRITE, 0); + + cpia2_do_command(cam, CPIA2_CMD_GET_USER_MODE, TRANSFER_READ, 0); + DBG("After VC RESET, user mode is 0x%0X\n", + cam->params.vp_params.video_mode); + + return retval; +} + +/****************************************************************************** + * + * cpia2_set_high_power + * + *****************************************************************************/ +static int cpia2_set_high_power(struct camera_data *cam) +{ + int i; + for (i = 0; i <= 50; i++) { + /* Read system status */ + cpia2_do_command(cam,CPIA2_CMD_GET_SYSTEM_CTRL,TRANSFER_READ,0); + + /* If there is an error, clear it */ + if(cam->params.camera_state.system_ctrl & + CPIA2_SYSTEM_CONTROL_V2W_ERR) + cpia2_do_command(cam, CPIA2_CMD_CLEAR_V2W_ERR, + TRANSFER_WRITE, 0); + + /* Try to set high power mode */ + cpia2_do_command(cam, CPIA2_CMD_SET_SYSTEM_CTRL, + TRANSFER_WRITE, 1); + + /* Try to read something in VP to check if everything is awake */ + cpia2_do_command(cam, CPIA2_CMD_GET_VP_SYSTEM_STATE, + TRANSFER_READ, 0); + if (cam->params.vp_params.system_state & + CPIA2_VP_SYSTEMSTATE_HK_ALIVE) { + break; + } else if (i == 50) { + cam->params.camera_state.power_mode = LO_POWER_MODE; + ERR("Camera did not wake up\n"); + return -EIO; + } + } + + DBG("System now in high power state\n"); + cam->params.camera_state.power_mode = HI_POWER_MODE; + return 0; +} + +/****************************************************************************** + * + * cpia2_set_low_power + * + *****************************************************************************/ +int cpia2_set_low_power(struct camera_data *cam) +{ + cam->params.camera_state.power_mode = LO_POWER_MODE; + cpia2_do_command(cam, CPIA2_CMD_SET_SYSTEM_CTRL, TRANSFER_WRITE, 0); + return 0; +} + +/****************************************************************************** + * + * apply_vp_patch + * + *****************************************************************************/ +static int cpia2_send_onebyte_command(struct camera_data *cam, + struct cpia2_command *cmd, + u8 start, u8 datum) +{ + cmd->buffer.block_data[0] = datum; + cmd->start = start; + cmd->reg_count = 1; + return cpia2_send_command(cam, cmd); +} + +static int apply_vp_patch(struct camera_data *cam) +{ + const struct firmware *fw; + const char fw_name[] = FIRMWARE; + int i, ret; + struct cpia2_command cmd; + + ret = request_firmware(&fw, fw_name, &cam->dev->dev); + if (ret) { + printk(KERN_ERR "cpia2: failed to load VP patch \"%s\"\n", + fw_name); + return ret; + } + + cmd.req_mode = CAMERAACCESS_TYPE_REPEAT | CAMERAACCESS_VP; + cmd.direction = TRANSFER_WRITE; + + /* First send the start address... */ + cpia2_send_onebyte_command(cam, &cmd, 0x0A, fw->data[0]); /* hi */ + cpia2_send_onebyte_command(cam, &cmd, 0x0B, fw->data[1]); /* lo */ + + /* ... followed by the data payload */ + for (i = 2; i < fw->size; i += 64) { + cmd.start = 0x0C; /* Data */ + cmd.reg_count = min_t(uint, 64, fw->size - i); + memcpy(cmd.buffer.block_data, &fw->data[i], cmd.reg_count); + cpia2_send_command(cam, &cmd); + } + + /* Next send the start address... */ + cpia2_send_onebyte_command(cam, &cmd, 0x0A, fw->data[0]); /* hi */ + cpia2_send_onebyte_command(cam, &cmd, 0x0B, fw->data[1]); /* lo */ + + /* ... followed by the 'goto' command */ + cpia2_send_onebyte_command(cam, &cmd, 0x0D, 1); + + release_firmware(fw); + return 0; +} + +/****************************************************************************** + * + * set_default_user_mode + * + *****************************************************************************/ +static int set_default_user_mode(struct camera_data *cam) +{ + unsigned char user_mode; + unsigned char frame_rate; + int width = cam->params.roi.width; + int height = cam->params.roi.height; + + switch (cam->params.version.sensor_flags) { + case CPIA2_VP_SENSOR_FLAGS_404: + case CPIA2_VP_SENSOR_FLAGS_407: + case CPIA2_VP_SENSOR_FLAGS_409: + case CPIA2_VP_SENSOR_FLAGS_410: + if ((width > STV_IMAGE_QCIF_COLS) + || (height > STV_IMAGE_QCIF_ROWS)) { + user_mode = CPIA2_VP_USER_MODE_CIF; + } else { + user_mode = CPIA2_VP_USER_MODE_QCIFDS; + } + frame_rate = CPIA2_VP_FRAMERATE_30; + break; + case CPIA2_VP_SENSOR_FLAGS_500: + if ((width > STV_IMAGE_CIF_COLS) + || (height > STV_IMAGE_CIF_ROWS)) { + user_mode = CPIA2_VP_USER_MODE_VGA; + } else { + user_mode = CPIA2_VP_USER_MODE_QVGADS; + } + if (cam->params.pnp_id.device_type == DEVICE_STV_672) + frame_rate = CPIA2_VP_FRAMERATE_15; + else + frame_rate = CPIA2_VP_FRAMERATE_30; + break; + default: + LOG("%s: Invalid sensor flag value 0x%0X\n",__func__, + cam->params.version.sensor_flags); + return -EINVAL; + } + + DBG("Sensor flag = 0x%0x, user mode = 0x%0x, frame rate = 0x%X\n", + cam->params.version.sensor_flags, user_mode, frame_rate); + cpia2_do_command(cam, CPIA2_CMD_SET_USER_MODE, TRANSFER_WRITE, + user_mode); + if(cam->params.vp_params.frame_rate > 0 && + frame_rate > cam->params.vp_params.frame_rate) + frame_rate = cam->params.vp_params.frame_rate; + + cpia2_set_fps(cam, frame_rate); + +// if (cam->params.pnp_id.device_type == DEVICE_STV_676) +// cpia2_do_command(cam, +// CPIA2_CMD_SET_VP_SYSTEM_CTRL, +// TRANSFER_WRITE, +// CPIA2_VP_SYSTEMCTRL_HK_CONTROL | +// CPIA2_VP_SYSTEMCTRL_POWER_CONTROL); + + return 0; +} + +/****************************************************************************** + * + * cpia2_match_video_size + * + * return the best match, where 'best' is as always + * the largest that is not bigger than what is requested. + *****************************************************************************/ +int cpia2_match_video_size(int width, int height) +{ + if (width >= STV_IMAGE_VGA_COLS && height >= STV_IMAGE_VGA_ROWS) + return VIDEOSIZE_VGA; + + if (width >= STV_IMAGE_CIF_COLS && height >= STV_IMAGE_CIF_ROWS) + return VIDEOSIZE_CIF; + + if (width >= STV_IMAGE_QVGA_COLS && height >= STV_IMAGE_QVGA_ROWS) + return VIDEOSIZE_QVGA; + + if (width >= 288 && height >= 216) + return VIDEOSIZE_288_216; + + if (width >= 256 && height >= 192) + return VIDEOSIZE_256_192; + + if (width >= 224 && height >= 168) + return VIDEOSIZE_224_168; + + if (width >= 192 && height >= 144) + return VIDEOSIZE_192_144; + + if (width >= STV_IMAGE_QCIF_COLS && height >= STV_IMAGE_QCIF_ROWS) + return VIDEOSIZE_QCIF; + + return -1; +} + +/****************************************************************************** + * + * SetVideoSize + * + *****************************************************************************/ +static int set_vw_size(struct camera_data *cam, int size) +{ + int retval = 0; + + cam->params.vp_params.video_size = size; + + switch (size) { + case VIDEOSIZE_VGA: + DBG("Setting size to VGA\n"); + cam->params.roi.width = STV_IMAGE_VGA_COLS; + cam->params.roi.height = STV_IMAGE_VGA_ROWS; + cam->width = STV_IMAGE_VGA_COLS; + cam->height = STV_IMAGE_VGA_ROWS; + break; + case VIDEOSIZE_CIF: + DBG("Setting size to CIF\n"); + cam->params.roi.width = STV_IMAGE_CIF_COLS; + cam->params.roi.height = STV_IMAGE_CIF_ROWS; + cam->width = STV_IMAGE_CIF_COLS; + cam->height = STV_IMAGE_CIF_ROWS; + break; + case VIDEOSIZE_QVGA: + DBG("Setting size to QVGA\n"); + cam->params.roi.width = STV_IMAGE_QVGA_COLS; + cam->params.roi.height = STV_IMAGE_QVGA_ROWS; + cam->width = STV_IMAGE_QVGA_COLS; + cam->height = STV_IMAGE_QVGA_ROWS; + break; + case VIDEOSIZE_288_216: + cam->params.roi.width = 288; + cam->params.roi.height = 216; + cam->width = 288; + cam->height = 216; + break; + case VIDEOSIZE_256_192: + cam->width = 256; + cam->height = 192; + cam->params.roi.width = 256; + cam->params.roi.height = 192; + break; + case VIDEOSIZE_224_168: + cam->width = 224; + cam->height = 168; + cam->params.roi.width = 224; + cam->params.roi.height = 168; + break; + case VIDEOSIZE_192_144: + cam->width = 192; + cam->height = 144; + cam->params.roi.width = 192; + cam->params.roi.height = 144; + break; + case VIDEOSIZE_QCIF: + DBG("Setting size to QCIF\n"); + cam->params.roi.width = STV_IMAGE_QCIF_COLS; + cam->params.roi.height = STV_IMAGE_QCIF_ROWS; + cam->width = STV_IMAGE_QCIF_COLS; + cam->height = STV_IMAGE_QCIF_ROWS; + break; + default: + retval = -EINVAL; + } + return retval; +} + +/****************************************************************************** + * + * configure_sensor + * + *****************************************************************************/ +static int configure_sensor(struct camera_data *cam, + int req_width, int req_height) +{ + int retval; + + switch (cam->params.version.sensor_flags) { + case CPIA2_VP_SENSOR_FLAGS_404: + case CPIA2_VP_SENSOR_FLAGS_407: + case CPIA2_VP_SENSOR_FLAGS_409: + case CPIA2_VP_SENSOR_FLAGS_410: + retval = config_sensor_410(cam, req_width, req_height); + break; + case CPIA2_VP_SENSOR_FLAGS_500: + retval = config_sensor_500(cam, req_width, req_height); + break; + default: + return -EINVAL; + } + + return retval; +} + +/****************************************************************************** + * + * config_sensor_410 + * + *****************************************************************************/ +static int config_sensor_410(struct camera_data *cam, + int req_width, int req_height) +{ + struct cpia2_command cmd; + int i = 0; + int image_size; + int image_type; + int width = req_width; + int height = req_height; + + /*** + * Make sure size doesn't exceed CIF. + ***/ + if (width > STV_IMAGE_CIF_COLS) + width = STV_IMAGE_CIF_COLS; + if (height > STV_IMAGE_CIF_ROWS) + height = STV_IMAGE_CIF_ROWS; + + image_size = cpia2_match_video_size(width, height); + + DBG("Config 410: width = %d, height = %d\n", width, height); + DBG("Image size returned is %d\n", image_size); + if (image_size >= 0) { + set_vw_size(cam, image_size); + width = cam->params.roi.width; + height = cam->params.roi.height; + + DBG("After set_vw_size(), width = %d, height = %d\n", + width, height); + if (width <= 176 && height <= 144) { + DBG("image type = VIDEOSIZE_QCIF\n"); + image_type = VIDEOSIZE_QCIF; + } + else if (width <= 320 && height <= 240) { + DBG("image type = VIDEOSIZE_QVGA\n"); + image_type = VIDEOSIZE_QVGA; + } + else { + DBG("image type = VIDEOSIZE_CIF\n"); + image_type = VIDEOSIZE_CIF; + } + } else { + ERR("ConfigSensor410 failed\n"); + return -EINVAL; + } + + cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC; + cmd.direction = TRANSFER_WRITE; + + /* VC Format */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_FORMAT; + if (image_type == VIDEOSIZE_CIF) { + cmd.buffer.registers[i++].value = + (u8) (CPIA2_VC_VC_FORMAT_UFIRST | + CPIA2_VC_VC_FORMAT_SHORTLINE); + } else { + cmd.buffer.registers[i++].value = + (u8) CPIA2_VC_VC_FORMAT_UFIRST; + } + + /* VC Clocks */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_CLOCKS; + if (image_type == VIDEOSIZE_QCIF) { + if (cam->params.pnp_id.device_type == DEVICE_STV_672) { + cmd.buffer.registers[i++].value= + (u8)(CPIA2_VC_VC_672_CLOCKS_CIF_DIV_BY_3 | + CPIA2_VC_VC_672_CLOCKS_SCALING | + CPIA2_VC_VC_CLOCKS_LOGDIV2); + DBG("VC_Clocks (0xc4) should be B\n"); + } + else { + cmd.buffer.registers[i++].value= + (u8)(CPIA2_VC_VC_676_CLOCKS_CIF_DIV_BY_3 | + CPIA2_VC_VC_CLOCKS_LOGDIV2); + } + } else { + if (cam->params.pnp_id.device_type == DEVICE_STV_672) { + cmd.buffer.registers[i++].value = + (u8) (CPIA2_VC_VC_672_CLOCKS_CIF_DIV_BY_3 | + CPIA2_VC_VC_CLOCKS_LOGDIV0); + } + else { + cmd.buffer.registers[i++].value = + (u8) (CPIA2_VC_VC_676_CLOCKS_CIF_DIV_BY_3 | + CPIA2_VC_VC_676_CLOCKS_SCALING | + CPIA2_VC_VC_CLOCKS_LOGDIV0); + } + } + DBG("VC_Clocks (0xc4) = 0x%0X\n", cmd.buffer.registers[i-1].value); + + /* Input reqWidth from VC */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_IHSIZE_LO; + if (image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = + (u8) (STV_IMAGE_QCIF_COLS / 4); + else + cmd.buffer.registers[i++].value = + (u8) (STV_IMAGE_CIF_COLS / 4); + + /* Timings */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_XLIM_HI; + if (image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = (u8) 0; + else + cmd.buffer.registers[i++].value = (u8) 1; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_XLIM_LO; + if (image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = (u8) 208; + else + cmd.buffer.registers[i++].value = (u8) 160; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_YLIM_HI; + if (image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = (u8) 0; + else + cmd.buffer.registers[i++].value = (u8) 1; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_YLIM_LO; + if (image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = (u8) 160; + else + cmd.buffer.registers[i++].value = (u8) 64; + + /* Output Image Size */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_OHSIZE; + cmd.buffer.registers[i++].value = cam->params.roi.width / 4; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_OVSIZE; + cmd.buffer.registers[i++].value = cam->params.roi.height / 4; + + /* Cropping */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_HCROP; + if (image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_QCIF_COLS / 4) - (width / 4)) / 2); + else + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_CIF_COLS / 4) - (width / 4)) / 2); + + cmd.buffer.registers[i].index = CPIA2_VC_VC_VCROP; + if (image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_QCIF_ROWS / 4) - (height / 4)) / 2); + else + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_CIF_ROWS / 4) - (height / 4)) / 2); + + /* Scaling registers (defaults) */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_HPHASE; + cmd.buffer.registers[i++].value = (u8) 0; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_VPHASE; + cmd.buffer.registers[i++].value = (u8) 0; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_HISPAN; + cmd.buffer.registers[i++].value = (u8) 31; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_VISPAN; + cmd.buffer.registers[i++].value = (u8) 31; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_HICROP; + cmd.buffer.registers[i++].value = (u8) 0; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_VICROP; + cmd.buffer.registers[i++].value = (u8) 0; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_HFRACT; + cmd.buffer.registers[i++].value = (u8) 0x81; /* = 8/1 = 8 (HIBYTE/LOBYTE) */ + + cmd.buffer.registers[i].index = CPIA2_VC_VC_VFRACT; + cmd.buffer.registers[i++].value = (u8) 0x81; /* = 8/1 = 8 (HIBYTE/LOBYTE) */ + + cmd.reg_count = i; + + cpia2_send_command(cam, &cmd); + + return i; +} + + +/****************************************************************************** + * + * config_sensor_500(cam) + * + *****************************************************************************/ +static int config_sensor_500(struct camera_data *cam, + int req_width, int req_height) +{ + struct cpia2_command cmd; + int i = 0; + int image_size = VIDEOSIZE_CIF; + int image_type = VIDEOSIZE_VGA; + int width = req_width; + int height = req_height; + unsigned int device = cam->params.pnp_id.device_type; + + image_size = cpia2_match_video_size(width, height); + + if (width > STV_IMAGE_CIF_COLS || height > STV_IMAGE_CIF_ROWS) + image_type = VIDEOSIZE_VGA; + else if (width > STV_IMAGE_QVGA_COLS || height > STV_IMAGE_QVGA_ROWS) + image_type = VIDEOSIZE_CIF; + else if (width > STV_IMAGE_QCIF_COLS || height > STV_IMAGE_QCIF_ROWS) + image_type = VIDEOSIZE_QVGA; + else + image_type = VIDEOSIZE_QCIF; + + if (image_size >= 0) { + set_vw_size(cam, image_size); + width = cam->params.roi.width; + height = cam->params.roi.height; + } else { + ERR("ConfigSensor500 failed\n"); + return -EINVAL; + } + + DBG("image_size = %d, width = %d, height = %d, type = %d\n", + image_size, width, height, image_type); + + cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC; + cmd.direction = TRANSFER_WRITE; + i = 0; + + /* VC Format */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_FORMAT; + cmd.buffer.registers[i].value = (u8) CPIA2_VC_VC_FORMAT_UFIRST; + if (image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i].value |= (u8) CPIA2_VC_VC_FORMAT_DECIMATING; + i++; + + /* VC Clocks */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_CLOCKS; + if (device == DEVICE_STV_672) { + if (image_type == VIDEOSIZE_VGA) + cmd.buffer.registers[i].value = + (u8)CPIA2_VC_VC_CLOCKS_LOGDIV1; + else + cmd.buffer.registers[i].value = + (u8)(CPIA2_VC_VC_672_CLOCKS_SCALING | + CPIA2_VC_VC_CLOCKS_LOGDIV3); + } else { + if (image_type == VIDEOSIZE_VGA) + cmd.buffer.registers[i].value = + (u8)CPIA2_VC_VC_CLOCKS_LOGDIV0; + else + cmd.buffer.registers[i].value = + (u8)(CPIA2_VC_VC_676_CLOCKS_SCALING | + CPIA2_VC_VC_CLOCKS_LOGDIV2); + } + i++; + + DBG("VC_CLOCKS = 0x%X\n", cmd.buffer.registers[i-1].value); + + /* Input width from VP */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_IHSIZE_LO; + if (image_type == VIDEOSIZE_VGA) + cmd.buffer.registers[i].value = + (u8) (STV_IMAGE_VGA_COLS / 4); + else + cmd.buffer.registers[i].value = + (u8) (STV_IMAGE_QVGA_COLS / 4); + i++; + DBG("Input width = %d\n", cmd.buffer.registers[i-1].value); + + /* Timings */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_XLIM_HI; + if (image_type == VIDEOSIZE_VGA) + cmd.buffer.registers[i++].value = (u8) 2; + else + cmd.buffer.registers[i++].value = (u8) 1; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_XLIM_LO; + if (image_type == VIDEOSIZE_VGA) + cmd.buffer.registers[i++].value = (u8) 250; + else if (image_type == VIDEOSIZE_QVGA) + cmd.buffer.registers[i++].value = (u8) 125; + else + cmd.buffer.registers[i++].value = (u8) 160; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_YLIM_HI; + if (image_type == VIDEOSIZE_VGA) + cmd.buffer.registers[i++].value = (u8) 2; + else + cmd.buffer.registers[i++].value = (u8) 1; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_YLIM_LO; + if (image_type == VIDEOSIZE_VGA) + cmd.buffer.registers[i++].value = (u8) 12; + else if (image_type == VIDEOSIZE_QVGA) + cmd.buffer.registers[i++].value = (u8) 64; + else + cmd.buffer.registers[i++].value = (u8) 6; + + /* Output Image Size */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_OHSIZE; + if (image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = STV_IMAGE_CIF_COLS / 4; + else + cmd.buffer.registers[i++].value = width / 4; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_OVSIZE; + if (image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = STV_IMAGE_CIF_ROWS / 4; + else + cmd.buffer.registers[i++].value = height / 4; + + /* Cropping */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_HCROP; + if (image_type == VIDEOSIZE_VGA) + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_VGA_COLS / 4) - (width / 4)) / 2); + else if (image_type == VIDEOSIZE_QVGA) + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_QVGA_COLS / 4) - (width / 4)) / 2); + else if (image_type == VIDEOSIZE_CIF) + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_CIF_COLS / 4) - (width / 4)) / 2); + else /*if (image_type == VIDEOSIZE_QCIF)*/ + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_QCIF_COLS / 4) - (width / 4)) / 2); + + cmd.buffer.registers[i].index = CPIA2_VC_VC_VCROP; + if (image_type == VIDEOSIZE_VGA) + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_VGA_ROWS / 4) - (height / 4)) / 2); + else if (image_type == VIDEOSIZE_QVGA) + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_QVGA_ROWS / 4) - (height / 4)) / 2); + else if (image_type == VIDEOSIZE_CIF) + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_CIF_ROWS / 4) - (height / 4)) / 2); + else /*if (image_type == VIDEOSIZE_QCIF)*/ + cmd.buffer.registers[i++].value = + (u8) (((STV_IMAGE_QCIF_ROWS / 4) - (height / 4)) / 2); + + /* Scaling registers (defaults) */ + cmd.buffer.registers[i].index = CPIA2_VC_VC_HPHASE; + if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = (u8) 36; + else + cmd.buffer.registers[i++].value = (u8) 0; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_VPHASE; + if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = (u8) 32; + else + cmd.buffer.registers[i++].value = (u8) 0; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_HISPAN; + if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = (u8) 26; + else + cmd.buffer.registers[i++].value = (u8) 31; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_VISPAN; + if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = (u8) 21; + else + cmd.buffer.registers[i++].value = (u8) 31; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_HICROP; + cmd.buffer.registers[i++].value = (u8) 0; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_VICROP; + cmd.buffer.registers[i++].value = (u8) 0; + + cmd.buffer.registers[i].index = CPIA2_VC_VC_HFRACT; + if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = (u8) 0x2B; /* 2/11 */ + else + cmd.buffer.registers[i++].value = (u8) 0x81; /* 8/1 */ + + cmd.buffer.registers[i].index = CPIA2_VC_VC_VFRACT; + if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF) + cmd.buffer.registers[i++].value = (u8) 0x13; /* 1/3 */ + else + cmd.buffer.registers[i++].value = (u8) 0x81; /* 8/1 */ + + cmd.reg_count = i; + + cpia2_send_command(cam, &cmd); + + return i; +} + + +/****************************************************************************** + * + * setallproperties + * + * This sets all user changeable properties to the values in cam->params. + *****************************************************************************/ +static int set_all_properties(struct camera_data *cam) +{ + /** + * Don't set target_kb here, it will be set later. + * framerate and user_mode were already set (set_default_user_mode). + **/ + + cpia2_usb_change_streaming_alternate(cam, + cam->params.camera_state.stream_mode); + + cpia2_do_command(cam, + CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION, + TRANSFER_WRITE, cam->params.vp_params.gpio_direction); + cpia2_do_command(cam, CPIA2_CMD_SET_VC_MP_GPIO_DATA, TRANSFER_WRITE, + cam->params.vp_params.gpio_data); + + v4l2_ctrl_handler_setup(&cam->hdl); + + wake_system(cam); + + set_lowlight_boost(cam); + + return 0; +} + +/****************************************************************************** + * + * cpia2_save_camera_state + * + *****************************************************************************/ +void cpia2_save_camera_state(struct camera_data *cam) +{ + cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS, TRANSFER_READ, 0); + cpia2_do_command(cam, CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION, TRANSFER_READ, + 0); + cpia2_do_command(cam, CPIA2_CMD_GET_VC_MP_GPIO_DATA, TRANSFER_READ, 0); + /* Don't get framerate or target_kb. Trust the values we already have */ +} + + +/****************************************************************************** + * + * cpia2_set_flicker_mode + * + *****************************************************************************/ +int cpia2_set_flicker_mode(struct camera_data *cam, int mode) +{ + unsigned char cam_reg; + int err = 0; + + if(cam->params.pnp_id.device_type != DEVICE_STV_672) + return -EINVAL; + + /* Set the appropriate bits in FLICKER_MODES, preserving the rest */ + if((err = cpia2_do_command(cam, CPIA2_CMD_GET_FLICKER_MODES, + TRANSFER_READ, 0))) + return err; + cam_reg = cam->params.flicker_control.cam_register; + + switch(mode) { + case NEVER_FLICKER: + cam_reg |= CPIA2_VP_FLICKER_MODES_NEVER_FLICKER; + cam_reg &= ~CPIA2_VP_FLICKER_MODES_50HZ; + break; + case FLICKER_60: + cam_reg &= ~CPIA2_VP_FLICKER_MODES_NEVER_FLICKER; + cam_reg &= ~CPIA2_VP_FLICKER_MODES_50HZ; + break; + case FLICKER_50: + cam_reg &= ~CPIA2_VP_FLICKER_MODES_NEVER_FLICKER; + cam_reg |= CPIA2_VP_FLICKER_MODES_50HZ; + break; + default: + return -EINVAL; + } + + if((err = cpia2_do_command(cam, CPIA2_CMD_SET_FLICKER_MODES, + TRANSFER_WRITE, cam_reg))) + return err; + + /* Set the appropriate bits in EXP_MODES, preserving the rest */ + if((err = cpia2_do_command(cam, CPIA2_CMD_GET_VP_EXP_MODES, + TRANSFER_READ, 0))) + return err; + cam_reg = cam->params.vp_params.exposure_modes; + + if (mode == NEVER_FLICKER) { + cam_reg |= CPIA2_VP_EXPOSURE_MODES_INHIBIT_FLICKER; + } else { + cam_reg &= ~CPIA2_VP_EXPOSURE_MODES_INHIBIT_FLICKER; + } + + if((err = cpia2_do_command(cam, CPIA2_CMD_SET_VP_EXP_MODES, + TRANSFER_WRITE, cam_reg))) + return err; + + if((err = cpia2_do_command(cam, CPIA2_CMD_REHASH_VP4, + TRANSFER_WRITE, 1))) + return err; + + switch(mode) { + case NEVER_FLICKER: + case FLICKER_60: + case FLICKER_50: + cam->params.flicker_control.flicker_mode_req = mode; + break; + default: + err = -EINVAL; + } + + return err; +} + +/****************************************************************************** + * + * cpia2_set_property_flip + * + *****************************************************************************/ +void cpia2_set_property_flip(struct camera_data *cam, int prop_val) +{ + unsigned char cam_reg; + + cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS, TRANSFER_READ, 0); + cam_reg = cam->params.vp_params.user_effects; + + if (prop_val) + { + cam_reg |= CPIA2_VP_USER_EFFECTS_FLIP; + } + else + { + cam_reg &= ~CPIA2_VP_USER_EFFECTS_FLIP; + } + cam->params.vp_params.user_effects = cam_reg; + cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE, + cam_reg); +} + +/****************************************************************************** + * + * cpia2_set_property_mirror + * + *****************************************************************************/ +void cpia2_set_property_mirror(struct camera_data *cam, int prop_val) +{ + unsigned char cam_reg; + + cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS, TRANSFER_READ, 0); + cam_reg = cam->params.vp_params.user_effects; + + if (prop_val) + { + cam_reg |= CPIA2_VP_USER_EFFECTS_MIRROR; + } + else + { + cam_reg &= ~CPIA2_VP_USER_EFFECTS_MIRROR; + } + cam->params.vp_params.user_effects = cam_reg; + cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE, + cam_reg); +} + +/****************************************************************************** + * + * cpia2_set_gpio + * + *****************************************************************************/ +int cpia2_set_gpio(struct camera_data *cam, unsigned char setting) +{ + int ret; + + /* Set the microport direction (register 0x90, should be defined + * already) to 1 (user output), and set the microport data (0x91) to + * the value in the ioctl argument. + */ + + ret = cpia2_do_command(cam, + CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION, + CPIA2_VC_MP_DIR_OUTPUT, + 255); + if (ret < 0) + return ret; + cam->params.vp_params.gpio_direction = 255; + + ret = cpia2_do_command(cam, + CPIA2_CMD_SET_VC_MP_GPIO_DATA, + CPIA2_VC_MP_DIR_OUTPUT, + setting); + if (ret < 0) + return ret; + cam->params.vp_params.gpio_data = setting; + + return 0; +} + +/****************************************************************************** + * + * cpia2_set_fps + * + *****************************************************************************/ +int cpia2_set_fps(struct camera_data *cam, int framerate) +{ + int retval; + + switch(framerate) { + case CPIA2_VP_FRAMERATE_30: + case CPIA2_VP_FRAMERATE_25: + if(cam->params.pnp_id.device_type == DEVICE_STV_672 && + cam->params.version.sensor_flags == + CPIA2_VP_SENSOR_FLAGS_500) { + return -EINVAL; + } + fallthrough; + case CPIA2_VP_FRAMERATE_15: + case CPIA2_VP_FRAMERATE_12_5: + case CPIA2_VP_FRAMERATE_7_5: + case CPIA2_VP_FRAMERATE_6_25: + break; + default: + return -EINVAL; + } + + if (cam->params.pnp_id.device_type == DEVICE_STV_672 && + framerate == CPIA2_VP_FRAMERATE_15) + framerate = 0; /* Work around bug in VP4 */ + + retval = cpia2_do_command(cam, + CPIA2_CMD_FRAMERATE_REQ, + TRANSFER_WRITE, + framerate); + + if(retval == 0) + cam->params.vp_params.frame_rate = framerate; + + return retval; +} + +/****************************************************************************** + * + * cpia2_set_brightness + * + *****************************************************************************/ +void cpia2_set_brightness(struct camera_data *cam, unsigned char value) +{ + /*** + * Don't let the register be set to zero - bug in VP4 - flash of full + * brightness + ***/ + if (cam->params.pnp_id.device_type == DEVICE_STV_672 && value == 0) + value++; + DBG("Setting brightness to %d (0x%0x)\n", value, value); + cpia2_do_command(cam, CPIA2_CMD_SET_VP_BRIGHTNESS, TRANSFER_WRITE, value); +} + +/****************************************************************************** + * + * cpia2_set_contrast + * + *****************************************************************************/ +void cpia2_set_contrast(struct camera_data *cam, unsigned char value) +{ + DBG("Setting contrast to %d (0x%0x)\n", value, value); + cpia2_do_command(cam, CPIA2_CMD_SET_CONTRAST, TRANSFER_WRITE, value); +} + +/****************************************************************************** + * + * cpia2_set_saturation + * + *****************************************************************************/ +void cpia2_set_saturation(struct camera_data *cam, unsigned char value) +{ + DBG("Setting saturation to %d (0x%0x)\n", value, value); + cpia2_do_command(cam,CPIA2_CMD_SET_VP_SATURATION, TRANSFER_WRITE,value); +} + +/****************************************************************************** + * + * wake_system + * + *****************************************************************************/ +static void wake_system(struct camera_data *cam) +{ + cpia2_do_command(cam, CPIA2_CMD_SET_WAKEUP, TRANSFER_WRITE, 0); +} + +/****************************************************************************** + * + * set_lowlight_boost + * + * Valid for STV500 sensor only + *****************************************************************************/ +static void set_lowlight_boost(struct camera_data *cam) +{ + struct cpia2_command cmd; + + if (cam->params.pnp_id.device_type != DEVICE_STV_672 || + cam->params.version.sensor_flags != CPIA2_VP_SENSOR_FLAGS_500) + return; + + cmd.direction = TRANSFER_WRITE; + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 3; + cmd.start = CPIA2_VP_RAM_ADDR_H; + + cmd.buffer.block_data[0] = 0; /* High byte of address to write to */ + cmd.buffer.block_data[1] = 0x59; /* Low byte of address to write to */ + cmd.buffer.block_data[2] = 0; /* High byte of data to write */ + + cpia2_send_command(cam, &cmd); + + if (cam->params.vp_params.lowlight_boost) { + cmd.buffer.block_data[0] = 0x02; /* Low byte data to write */ + } else { + cmd.buffer.block_data[0] = 0x06; + } + cmd.start = CPIA2_VP_RAM_DATA; + cmd.reg_count = 1; + cpia2_send_command(cam, &cmd); + + /* Rehash the VP4 values */ + cpia2_do_command(cam, CPIA2_CMD_REHASH_VP4, TRANSFER_WRITE, 1); +} + +/****************************************************************************** + * + * cpia2_set_format + * + * Assumes that new size is already set in param struct. + *****************************************************************************/ +void cpia2_set_format(struct camera_data *cam) +{ + cam->flush = true; + + cpia2_usb_stream_pause(cam); + + /* reset camera to new size */ + cpia2_set_low_power(cam); + cpia2_reset_camera(cam); + cam->flush = false; + + cpia2_dbg_dump_registers(cam); + + cpia2_usb_stream_resume(cam); +} + +/****************************************************************************** + * + * cpia2_dbg_dump_registers + * + *****************************************************************************/ +void cpia2_dbg_dump_registers(struct camera_data *cam) +{ +#ifdef _CPIA2_DEBUG_ + struct cpia2_command cmd; + + if (!(debugs_on & DEBUG_DUMP_REGS)) + return; + + cmd.direction = TRANSFER_READ; + + /* Start with bank 0 (SYSTEM) */ + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM; + cmd.reg_count = 3; + cmd.start = 0; + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "System Device Hi = 0x%X\n", + cmd.buffer.block_data[0]); + printk(KERN_DEBUG "System Device Lo = 0x%X\n", + cmd.buffer.block_data[1]); + printk(KERN_DEBUG "System_system control = 0x%X\n", + cmd.buffer.block_data[2]); + + /* Bank 1 (VC) */ + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; + cmd.reg_count = 4; + cmd.start = 0x80; + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "ASIC_ID = 0x%X\n", + cmd.buffer.block_data[0]); + printk(KERN_DEBUG "ASIC_REV = 0x%X\n", + cmd.buffer.block_data[1]); + printk(KERN_DEBUG "PW_CONTRL = 0x%X\n", + cmd.buffer.block_data[2]); + printk(KERN_DEBUG "WAKEUP = 0x%X\n", + cmd.buffer.block_data[3]); + + cmd.start = 0xA0; /* ST_CTRL */ + cmd.reg_count = 1; + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "Stream ctrl = 0x%X\n", + cmd.buffer.block_data[0]); + + cmd.start = 0xA4; /* Stream status */ + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "Stream status = 0x%X\n", + cmd.buffer.block_data[0]); + + cmd.start = 0xA8; /* USB status */ + cmd.reg_count = 3; + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "USB_CTRL = 0x%X\n", + cmd.buffer.block_data[0]); + printk(KERN_DEBUG "USB_STRM = 0x%X\n", + cmd.buffer.block_data[1]); + printk(KERN_DEBUG "USB_STATUS = 0x%X\n", + cmd.buffer.block_data[2]); + + cmd.start = 0xAF; /* USB settings */ + cmd.reg_count = 1; + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "USB settings = 0x%X\n", + cmd.buffer.block_data[0]); + + cmd.start = 0xC0; /* VC stuff */ + cmd.reg_count = 26; + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "VC Control = 0x%0X\n", + cmd.buffer.block_data[0]); + printk(KERN_DEBUG "VC Format = 0x%0X\n", + cmd.buffer.block_data[3]); + printk(KERN_DEBUG "VC Clocks = 0x%0X\n", + cmd.buffer.block_data[4]); + printk(KERN_DEBUG "VC IHSize = 0x%0X\n", + cmd.buffer.block_data[5]); + printk(KERN_DEBUG "VC Xlim Hi = 0x%0X\n", + cmd.buffer.block_data[6]); + printk(KERN_DEBUG "VC XLim Lo = 0x%0X\n", + cmd.buffer.block_data[7]); + printk(KERN_DEBUG "VC YLim Hi = 0x%0X\n", + cmd.buffer.block_data[8]); + printk(KERN_DEBUG "VC YLim Lo = 0x%0X\n", + cmd.buffer.block_data[9]); + printk(KERN_DEBUG "VC OHSize = 0x%0X\n", + cmd.buffer.block_data[10]); + printk(KERN_DEBUG "VC OVSize = 0x%0X\n", + cmd.buffer.block_data[11]); + printk(KERN_DEBUG "VC HCrop = 0x%0X\n", + cmd.buffer.block_data[12]); + printk(KERN_DEBUG "VC VCrop = 0x%0X\n", + cmd.buffer.block_data[13]); + printk(KERN_DEBUG "VC HPhase = 0x%0X\n", + cmd.buffer.block_data[14]); + printk(KERN_DEBUG "VC VPhase = 0x%0X\n", + cmd.buffer.block_data[15]); + printk(KERN_DEBUG "VC HIspan = 0x%0X\n", + cmd.buffer.block_data[16]); + printk(KERN_DEBUG "VC VIspan = 0x%0X\n", + cmd.buffer.block_data[17]); + printk(KERN_DEBUG "VC HiCrop = 0x%0X\n", + cmd.buffer.block_data[18]); + printk(KERN_DEBUG "VC ViCrop = 0x%0X\n", + cmd.buffer.block_data[19]); + printk(KERN_DEBUG "VC HiFract = 0x%0X\n", + cmd.buffer.block_data[20]); + printk(KERN_DEBUG "VC ViFract = 0x%0X\n", + cmd.buffer.block_data[21]); + printk(KERN_DEBUG "VC JPeg Opt = 0x%0X\n", + cmd.buffer.block_data[22]); + printk(KERN_DEBUG "VC Creep Per = 0x%0X\n", + cmd.buffer.block_data[23]); + printk(KERN_DEBUG "VC User Sq. = 0x%0X\n", + cmd.buffer.block_data[24]); + printk(KERN_DEBUG "VC Target KB = 0x%0X\n", + cmd.buffer.block_data[25]); + + /*** VP ***/ + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP; + cmd.reg_count = 14; + cmd.start = 0; + cpia2_send_command(cam, &cmd); + + printk(KERN_DEBUG "VP Dev Hi = 0x%0X\n", + cmd.buffer.block_data[0]); + printk(KERN_DEBUG "VP Dev Lo = 0x%0X\n", + cmd.buffer.block_data[1]); + printk(KERN_DEBUG "VP Sys State = 0x%0X\n", + cmd.buffer.block_data[2]); + printk(KERN_DEBUG "VP Sys Ctrl = 0x%0X\n", + cmd.buffer.block_data[3]); + printk(KERN_DEBUG "VP Sensor flg = 0x%0X\n", + cmd.buffer.block_data[5]); + printk(KERN_DEBUG "VP Sensor Rev = 0x%0X\n", + cmd.buffer.block_data[6]); + printk(KERN_DEBUG "VP Dev Config = 0x%0X\n", + cmd.buffer.block_data[7]); + printk(KERN_DEBUG "VP GPIO_DIR = 0x%0X\n", + cmd.buffer.block_data[8]); + printk(KERN_DEBUG "VP GPIO_DATA = 0x%0X\n", + cmd.buffer.block_data[9]); + printk(KERN_DEBUG "VP Ram ADDR H = 0x%0X\n", + cmd.buffer.block_data[10]); + printk(KERN_DEBUG "VP Ram ADDR L = 0x%0X\n", + cmd.buffer.block_data[11]); + printk(KERN_DEBUG "VP RAM Data = 0x%0X\n", + cmd.buffer.block_data[12]); + printk(KERN_DEBUG "Do Call = 0x%0X\n", + cmd.buffer.block_data[13]); + + if (cam->params.pnp_id.device_type == DEVICE_STV_672) { + cmd.reg_count = 9; + cmd.start = 0x0E; + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "VP Clock Ctrl = 0x%0X\n", + cmd.buffer.block_data[0]); + printk(KERN_DEBUG "VP Patch Rev = 0x%0X\n", + cmd.buffer.block_data[1]); + printk(KERN_DEBUG "VP Vid Mode = 0x%0X\n", + cmd.buffer.block_data[2]); + printk(KERN_DEBUG "VP Framerate = 0x%0X\n", + cmd.buffer.block_data[3]); + printk(KERN_DEBUG "VP UserEffect = 0x%0X\n", + cmd.buffer.block_data[4]); + printk(KERN_DEBUG "VP White Bal = 0x%0X\n", + cmd.buffer.block_data[5]); + printk(KERN_DEBUG "VP WB thresh = 0x%0X\n", + cmd.buffer.block_data[6]); + printk(KERN_DEBUG "VP Exp Modes = 0x%0X\n", + cmd.buffer.block_data[7]); + printk(KERN_DEBUG "VP Exp Target = 0x%0X\n", + cmd.buffer.block_data[8]); + + cmd.reg_count = 1; + cmd.start = 0x1B; + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "VP FlickerMds = 0x%0X\n", + cmd.buffer.block_data[0]); + } else { + cmd.reg_count = 8 ; + cmd.start = 0x0E; + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "VP Clock Ctrl = 0x%0X\n", + cmd.buffer.block_data[0]); + printk(KERN_DEBUG "VP Patch Rev = 0x%0X\n", + cmd.buffer.block_data[1]); + printk(KERN_DEBUG "VP Vid Mode = 0x%0X\n", + cmd.buffer.block_data[5]); + printk(KERN_DEBUG "VP Framerate = 0x%0X\n", + cmd.buffer.block_data[6]); + printk(KERN_DEBUG "VP UserEffect = 0x%0X\n", + cmd.buffer.block_data[7]); + + cmd.reg_count = 1; + cmd.start = CPIA2_VP5_EXPOSURE_TARGET; + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "VP5 Exp Target= 0x%0X\n", + cmd.buffer.block_data[0]); + + cmd.reg_count = 4; + cmd.start = 0x3A; + cpia2_send_command(cam, &cmd); + printk(KERN_DEBUG "VP5 MY Black = 0x%0X\n", + cmd.buffer.block_data[0]); + printk(KERN_DEBUG "VP5 MCY Range = 0x%0X\n", + cmd.buffer.block_data[1]); + printk(KERN_DEBUG "VP5 MYCEILING = 0x%0X\n", + cmd.buffer.block_data[2]); + printk(KERN_DEBUG "VP5 MCUV Sat = 0x%0X\n", + cmd.buffer.block_data[3]); + } +#endif +} + +/****************************************************************************** + * + * reset_camera_struct + * + * Sets all values to the defaults + *****************************************************************************/ +static void reset_camera_struct(struct camera_data *cam) +{ + /*** + * The following parameter values are the defaults from the register map. + ***/ + cam->params.vp_params.lowlight_boost = 0; + + /* FlickerModes */ + cam->params.flicker_control.flicker_mode_req = NEVER_FLICKER; + + /* jpeg params */ + cam->params.compression.jpeg_options = CPIA2_VC_VC_JPEG_OPT_DEFAULT; + cam->params.compression.creep_period = 2; + cam->params.compression.user_squeeze = 20; + cam->params.compression.inhibit_htables = false; + + /* gpio params */ + cam->params.vp_params.gpio_direction = 0; /* write, the default safe mode */ + cam->params.vp_params.gpio_data = 0; + + /* Target kb params */ + cam->params.vc_params.quality = 100; + + /*** + * Set Sensor FPS as fast as possible. + ***/ + if(cam->params.pnp_id.device_type == DEVICE_STV_672) { + if(cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500) + cam->params.vp_params.frame_rate = CPIA2_VP_FRAMERATE_15; + else + cam->params.vp_params.frame_rate = CPIA2_VP_FRAMERATE_30; + } else { + cam->params.vp_params.frame_rate = CPIA2_VP_FRAMERATE_30; + } + + /*** + * Set default video mode as large as possible : + * for vga sensor set to vga, for cif sensor set to CIF. + ***/ + if (cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500) { + cam->sensor_type = CPIA2_SENSOR_500; + cam->video_size = VIDEOSIZE_VGA; + cam->params.roi.width = STV_IMAGE_VGA_COLS; + cam->params.roi.height = STV_IMAGE_VGA_ROWS; + } else { + cam->sensor_type = CPIA2_SENSOR_410; + cam->video_size = VIDEOSIZE_CIF; + cam->params.roi.width = STV_IMAGE_CIF_COLS; + cam->params.roi.height = STV_IMAGE_CIF_ROWS; + } + + cam->width = cam->params.roi.width; + cam->height = cam->params.roi.height; +} + +/****************************************************************************** + * + * cpia2_init_camera_struct + * + * Deinitialize camera struct + *****************************************************************************/ +void cpia2_deinit_camera_struct(struct camera_data *cam, struct usb_interface *intf) +{ + v4l2_device_unregister(&cam->v4l2_dev); + kfree(cam); +} + +/****************************************************************************** + * + * cpia2_init_camera_struct + * + * Initializes camera struct, does not call reset to fill in defaults. + *****************************************************************************/ +struct camera_data *cpia2_init_camera_struct(struct usb_interface *intf) +{ + struct camera_data *cam; + + cam = kzalloc(sizeof(*cam), GFP_KERNEL); + + if (!cam) { + ERR("couldn't kmalloc cpia2 struct\n"); + return NULL; + } + + cam->v4l2_dev.release = cpia2_camera_release; + if (v4l2_device_register(&intf->dev, &cam->v4l2_dev) < 0) { + v4l2_err(&cam->v4l2_dev, "couldn't register v4l2_device\n"); + kfree(cam); + return NULL; + } + + mutex_init(&cam->v4l2_lock); + init_waitqueue_head(&cam->wq_stream); + + return cam; +} + +/****************************************************************************** + * + * cpia2_init_camera + * + * Initializes camera. + *****************************************************************************/ +int cpia2_init_camera(struct camera_data *cam) +{ + DBG("Start\n"); + + cam->mmapped = false; + + /* Get sensor and asic types before reset. */ + cpia2_set_high_power(cam); + cpia2_get_version_info(cam); + if (cam->params.version.asic_id != CPIA2_ASIC_672) { + ERR("Device IO error (asicID has incorrect value of 0x%X\n", + cam->params.version.asic_id); + return -ENODEV; + } + + /* Set GPIO direction and data to a safe state. */ + cpia2_do_command(cam, CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION, + TRANSFER_WRITE, 0); + cpia2_do_command(cam, CPIA2_CMD_SET_VC_MP_GPIO_DATA, + TRANSFER_WRITE, 0); + + /* resetting struct requires version info for sensor and asic types */ + reset_camera_struct(cam); + + cpia2_set_low_power(cam); + + DBG("End\n"); + + return 0; +} + +/****************************************************************************** + * + * cpia2_allocate_buffers + * + *****************************************************************************/ +int cpia2_allocate_buffers(struct camera_data *cam) +{ + int i; + + if(!cam->buffers) { + u32 size = cam->num_frames*sizeof(struct framebuf); + cam->buffers = kmalloc(size, GFP_KERNEL); + if(!cam->buffers) { + ERR("couldn't kmalloc frame buffer structures\n"); + return -ENOMEM; + } + } + + if(!cam->frame_buffer) { + cam->frame_buffer = rvmalloc(cam->frame_size*cam->num_frames); + if (!cam->frame_buffer) { + ERR("couldn't vmalloc frame buffer data area\n"); + kfree(cam->buffers); + cam->buffers = NULL; + return -ENOMEM; + } + } + + for(i=0; i<cam->num_frames-1; ++i) { + cam->buffers[i].next = &cam->buffers[i+1]; + cam->buffers[i].data = cam->frame_buffer +i*cam->frame_size; + cam->buffers[i].status = FRAME_EMPTY; + cam->buffers[i].length = 0; + cam->buffers[i].max_length = 0; + cam->buffers[i].num = i; + } + cam->buffers[i].next = cam->buffers; + cam->buffers[i].data = cam->frame_buffer +i*cam->frame_size; + cam->buffers[i].status = FRAME_EMPTY; + cam->buffers[i].length = 0; + cam->buffers[i].max_length = 0; + cam->buffers[i].num = i; + cam->curbuff = cam->buffers; + cam->workbuff = cam->curbuff->next; + DBG("buffers=%p, curbuff=%p, workbuff=%p\n", cam->buffers, cam->curbuff, + cam->workbuff); + return 0; +} + +/****************************************************************************** + * + * cpia2_free_buffers + * + *****************************************************************************/ +void cpia2_free_buffers(struct camera_data *cam) +{ + if(cam->buffers) { + kfree(cam->buffers); + cam->buffers = NULL; + } + if(cam->frame_buffer) { + rvfree(cam->frame_buffer, cam->frame_size*cam->num_frames); + cam->frame_buffer = NULL; + } +} + +/****************************************************************************** + * + * cpia2_read + * + *****************************************************************************/ +long cpia2_read(struct camera_data *cam, + char __user *buf, unsigned long count, int noblock) +{ + struct framebuf *frame; + + if (!count) + return 0; + + if (!buf) { + ERR("%s: buffer NULL\n",__func__); + return -EINVAL; + } + + if (!cam) { + ERR("%s: Internal error, camera_data NULL!\n",__func__); + return -EINVAL; + } + + if (!cam->streaming) { + /* Start streaming */ + cpia2_usb_stream_start(cam, + cam->params.camera_state.stream_mode); + } + + /* Copy cam->curbuff in case it changes while we're processing */ + frame = cam->curbuff; + if (noblock && frame->status != FRAME_READY) { + return -EAGAIN; + } + + if (frame->status != FRAME_READY) { + mutex_unlock(&cam->v4l2_lock); + wait_event_interruptible(cam->wq_stream, + !video_is_registered(&cam->vdev) || + (frame = cam->curbuff)->status == FRAME_READY); + mutex_lock(&cam->v4l2_lock); + if (signal_pending(current)) + return -ERESTARTSYS; + if (!video_is_registered(&cam->vdev)) + return 0; + } + + /* copy data to user space */ + if (frame->length > count) + return -EFAULT; + if (copy_to_user(buf, frame->data, frame->length)) + return -EFAULT; + + count = frame->length; + + frame->status = FRAME_EMPTY; + + return count; +} + +/****************************************************************************** + * + * cpia2_poll + * + *****************************************************************************/ +__poll_t cpia2_poll(struct camera_data *cam, struct file *filp, + poll_table *wait) +{ + __poll_t status = v4l2_ctrl_poll(filp, wait); + + if ((poll_requested_events(wait) & (EPOLLIN | EPOLLRDNORM)) && + !cam->streaming) { + /* Start streaming */ + cpia2_usb_stream_start(cam, + cam->params.camera_state.stream_mode); + } + + poll_wait(filp, &cam->wq_stream, wait); + + if (cam->curbuff->status == FRAME_READY) + status |= EPOLLIN | EPOLLRDNORM; + + return status; +} + +/****************************************************************************** + * + * cpia2_remap_buffer + * + *****************************************************************************/ +int cpia2_remap_buffer(struct camera_data *cam, struct vm_area_struct *vma) +{ + const char *adr = (const char *)vma->vm_start; + unsigned long size = vma->vm_end-vma->vm_start; + unsigned long start_offset = vma->vm_pgoff << PAGE_SHIFT; + unsigned long start = (unsigned long) adr; + unsigned long page, pos; + + DBG("mmap offset:%ld size:%ld\n", start_offset, size); + + if (!video_is_registered(&cam->vdev)) + return -ENODEV; + + if (size > cam->frame_size*cam->num_frames || + (start_offset % cam->frame_size) != 0 || + (start_offset+size > cam->frame_size*cam->num_frames)) + return -EINVAL; + + pos = ((unsigned long) (cam->frame_buffer)) + start_offset; + while (size > 0) { + page = kvirt_to_pa(pos); + if (remap_pfn_range(vma, start, page >> PAGE_SHIFT, PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + cam->mmapped = true; + return 0; +} diff --git a/drivers/staging/media/deprecated/cpia2/cpia2_registers.h b/drivers/staging/media/deprecated/cpia2/cpia2_registers.h new file mode 100644 index 000000000000..8c73812a15c9 --- /dev/null +++ b/drivers/staging/media/deprecated/cpia2/cpia2_registers.h @@ -0,0 +1,463 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/**************************************************************************** + * + * Filename: cpia2registers.h + * + * Copyright 2001, STMicrolectronics, Inc. + * + * Description: + * Definitions for the CPia2 register set + * + ****************************************************************************/ + +#ifndef CPIA2_REGISTER_HEADER +#define CPIA2_REGISTER_HEADER + +/*** + * System register set (Bank 0) + ***/ +#define CPIA2_SYSTEM_DEVICE_HI 0x00 +#define CPIA2_SYSTEM_DEVICE_LO 0x01 + +#define CPIA2_SYSTEM_SYSTEM_CONTROL 0x02 +#define CPIA2_SYSTEM_CONTROL_LOW_POWER 0x00 +#define CPIA2_SYSTEM_CONTROL_HIGH_POWER 0x01 +#define CPIA2_SYSTEM_CONTROL_SUSPEND 0x02 +#define CPIA2_SYSTEM_CONTROL_V2W_ERR 0x10 +#define CPIA2_SYSTEM_CONTROL_RB_ERR 0x10 +#define CPIA2_SYSTEM_CONTROL_CLEAR_ERR 0x80 + +#define CPIA2_SYSTEM_INT_PACKET_CTRL 0x04 +#define CPIA2_SYSTEM_INT_PACKET_CTRL_ENABLE_SW_XX 0x01 +#define CPIA2_SYSTEM_INT_PACKET_CTRL_ENABLE_EOF 0x02 +#define CPIA2_SYSTEM_INT_PACKET_CTRL_ENABLE_INT1 0x04 + +#define CPIA2_SYSTEM_CACHE_CTRL 0x05 +#define CPIA2_SYSTEM_CACHE_CTRL_CACHE_RESET 0x01 +#define CPIA2_SYSTEM_CACHE_CTRL_CACHE_FLUSH 0x02 + +#define CPIA2_SYSTEM_SERIAL_CTRL 0x06 +#define CPIA2_SYSTEM_SERIAL_CTRL_NULL_CMD 0x00 +#define CPIA2_SYSTEM_SERIAL_CTRL_START_CMD 0x01 +#define CPIA2_SYSTEM_SERIAL_CTRL_STOP_CMD 0x02 +#define CPIA2_SYSTEM_SERIAL_CTRL_WRITE_CMD 0x03 +#define CPIA2_SYSTEM_SERIAL_CTRL_READ_ACK_CMD 0x04 +#define CPIA2_SYSTEM_SERIAL_CTRL_READ_NACK_CMD 0x05 + +#define CPIA2_SYSTEM_SERIAL_DATA 0x07 + +#define CPIA2_SYSTEM_VP_SERIAL_ADDR 0x08 + +/*** + * I2C addresses for various devices in CPiA2 + ***/ +#define CPIA2_SYSTEM_VP_SERIAL_ADDR_SENSOR 0x20 +#define CPIA2_SYSTEM_VP_SERIAL_ADDR_VP 0x88 +#define CPIA2_SYSTEM_VP_SERIAL_ADDR_676_VP 0x8A + +#define CPIA2_SYSTEM_SPARE_REG1 0x09 +#define CPIA2_SYSTEM_SPARE_REG2 0x0A +#define CPIA2_SYSTEM_SPARE_REG3 0x0B + +#define CPIA2_SYSTEM_MC_PORT_0 0x0C +#define CPIA2_SYSTEM_MC_PORT_1 0x0D +#define CPIA2_SYSTEM_MC_PORT_2 0x0E +#define CPIA2_SYSTEM_MC_PORT_3 0x0F + +#define CPIA2_SYSTEM_STATUS_PKT 0x20 +#define CPIA2_SYSTEM_STATUS_PKT_END 0x27 + +#define CPIA2_SYSTEM_DESCRIP_VID_HI 0x30 +#define CPIA2_SYSTEM_DESCRIP_VID_LO 0x31 +#define CPIA2_SYSTEM_DESCRIP_PID_HI 0x32 +#define CPIA2_SYSTEM_DESCRIP_PID_LO 0x33 + +#define CPIA2_SYSTEM_FW_VERSION_HI 0x34 +#define CPIA2_SYSTEM_FW_VERSION_LO 0x35 + +#define CPIA2_SYSTEM_CACHE_START_INDEX 0x80 +#define CPIA2_SYSTEM_CACHE_MAX_WRITES 0x10 + +/*** + * VC register set (Bank 1) + ***/ +#define CPIA2_VC_ASIC_ID 0x80 + +#define CPIA2_VC_ASIC_REV 0x81 + +#define CPIA2_VC_PW_CTRL 0x82 +#define CPIA2_VC_PW_CTRL_COLDSTART 0x01 +#define CPIA2_VC_PW_CTRL_CP_CLK_EN 0x02 +#define CPIA2_VC_PW_CTRL_VP_RESET_N 0x04 +#define CPIA2_VC_PW_CTRL_VC_CLK_EN 0x08 +#define CPIA2_VC_PW_CTRL_VC_RESET_N 0x10 +#define CPIA2_VC_PW_CTRL_GOTO_SUSPEND 0x20 +#define CPIA2_VC_PW_CTRL_UDC_SUSPEND 0x40 +#define CPIA2_VC_PW_CTRL_PWR_DOWN 0x80 + +#define CPIA2_VC_WAKEUP 0x83 +#define CPIA2_VC_WAKEUP_SW_ENABLE 0x01 +#define CPIA2_VC_WAKEUP_XX_ENABLE 0x02 +#define CPIA2_VC_WAKEUP_SW_ATWAKEUP 0x04 +#define CPIA2_VC_WAKEUP_XX_ATWAKEUP 0x08 + +#define CPIA2_VC_CLOCK_CTRL 0x84 +#define CPIA2_VC_CLOCK_CTRL_TESTUP72 0x01 + +#define CPIA2_VC_INT_ENABLE 0x88 +#define CPIA2_VC_INT_ENABLE_XX_IE 0x01 +#define CPIA2_VC_INT_ENABLE_SW_IE 0x02 +#define CPIA2_VC_INT_ENABLE_VC_IE 0x04 +#define CPIA2_VC_INT_ENABLE_USBDATA_IE 0x08 +#define CPIA2_VC_INT_ENABLE_USBSETUP_IE 0x10 +#define CPIA2_VC_INT_ENABLE_USBCFG_IE 0x20 + +#define CPIA2_VC_INT_FLAG 0x89 +#define CPIA2_VC_INT_ENABLE_XX_FLAG 0x01 +#define CPIA2_VC_INT_ENABLE_SW_FLAG 0x02 +#define CPIA2_VC_INT_ENABLE_VC_FLAG 0x04 +#define CPIA2_VC_INT_ENABLE_USBDATA_FLAG 0x08 +#define CPIA2_VC_INT_ENABLE_USBSETUP_FLAG 0x10 +#define CPIA2_VC_INT_ENABLE_USBCFG_FLAG 0x20 +#define CPIA2_VC_INT_ENABLE_SET_RESET_BIT 0x80 + +#define CPIA2_VC_INT_STATE 0x8A +#define CPIA2_VC_INT_STATE_XX_STATE 0x01 +#define CPIA2_VC_INT_STATE_SW_STATE 0x02 + +#define CPIA2_VC_MP_DIR 0x90 +#define CPIA2_VC_MP_DIR_INPUT 0x00 +#define CPIA2_VC_MP_DIR_OUTPUT 0x01 + +#define CPIA2_VC_MP_DATA 0x91 + +#define CPIA2_VC_DP_CTRL 0x98 +#define CPIA2_VC_DP_CTRL_MODE_0 0x00 +#define CPIA2_VC_DP_CTRL_MODE_A 0x01 +#define CPIA2_VC_DP_CTRL_MODE_B 0x02 +#define CPIA2_VC_DP_CTRL_MODE_C 0x03 +#define CPIA2_VC_DP_CTRL_FAKE_FST 0x04 + +#define CPIA2_VC_AD_CTRL 0x99 +#define CPIA2_VC_AD_CTRL_SRC_0 0x00 +#define CPIA2_VC_AD_CTRL_SRC_DIGI_A 0x01 +#define CPIA2_VC_AD_CTRL_SRC_REG 0x02 +#define CPIA2_VC_AD_CTRL_DST_USB 0x00 +#define CPIA2_VC_AD_CTRL_DST_REG 0x04 + +#define CPIA2_VC_AD_TEST_IN 0x9B + +#define CPIA2_VC_AD_TEST_OUT 0x9C + +#define CPIA2_VC_AD_STATUS 0x9D +#define CPIA2_VC_AD_STATUS_EMPTY 0x01 +#define CPIA2_VC_AD_STATUS_FULL 0x02 + +#define CPIA2_VC_DP_DATA 0x9E + +#define CPIA2_VC_ST_CTRL 0xA0 +#define CPIA2_VC_ST_CTRL_SRC_VC 0x00 +#define CPIA2_VC_ST_CTRL_SRC_DP 0x01 +#define CPIA2_VC_ST_CTRL_SRC_REG 0x02 + +#define CPIA2_VC_ST_CTRL_RAW_SELECT 0x04 + +#define CPIA2_VC_ST_CTRL_DST_USB 0x00 +#define CPIA2_VC_ST_CTRL_DST_DP 0x08 +#define CPIA2_VC_ST_CTRL_DST_REG 0x10 + +#define CPIA2_VC_ST_CTRL_FIFO_ENABLE 0x20 +#define CPIA2_VC_ST_CTRL_EOF_DETECT 0x40 + +#define CPIA2_VC_ST_TEST 0xA1 +#define CPIA2_VC_ST_TEST_MODE_MANUAL 0x00 +#define CPIA2_VC_ST_TEST_MODE_INCREMENT 0x02 + +#define CPIA2_VC_ST_TEST_AUTO_FILL 0x08 + +#define CPIA2_VC_ST_TEST_REPEAT_FIFO 0x10 + +#define CPIA2_VC_ST_TEST_IN 0xA2 + +#define CPIA2_VC_ST_TEST_OUT 0xA3 + +#define CPIA2_VC_ST_STATUS 0xA4 +#define CPIA2_VC_ST_STATUS_EMPTY 0x01 +#define CPIA2_VC_ST_STATUS_FULL 0x02 + +#define CPIA2_VC_ST_FRAME_DETECT_1 0xA5 + +#define CPIA2_VC_ST_FRAME_DETECT_2 0xA6 + +#define CPIA2_VC_USB_CTRL 0xA8 +#define CPIA2_VC_USB_CTRL_CMD_STALLED 0x01 +#define CPIA2_VC_USB_CTRL_CMD_READY 0x02 +#define CPIA2_VC_USB_CTRL_CMD_STATUS 0x04 +#define CPIA2_VC_USB_CTRL_CMD_STATUS_DIR 0x08 +#define CPIA2_VC_USB_CTRL_CMD_NO_CLASH 0x10 +#define CPIA2_VC_USB_CTRL_CMD_MICRO_ACCESS 0x80 + +#define CPIA2_VC_USB_STRM 0xA9 +#define CPIA2_VC_USB_STRM_ISO_ENABLE 0x01 +#define CPIA2_VC_USB_STRM_BLK_ENABLE 0x02 +#define CPIA2_VC_USB_STRM_INT_ENABLE 0x04 +#define CPIA2_VC_USB_STRM_AUD_ENABLE 0x08 + +#define CPIA2_VC_USB_STATUS 0xAA +#define CPIA2_VC_USB_STATUS_CMD_IN_PROGRESS 0x01 +#define CPIA2_VC_USB_STATUS_CMD_STATUS_STALL 0x02 +#define CPIA2_VC_USB_STATUS_CMD_HANDSHAKE 0x04 +#define CPIA2_VC_USB_STATUS_CMD_OVERRIDE 0x08 +#define CPIA2_VC_USB_STATUS_CMD_FIFO_BUSY 0x10 +#define CPIA2_VC_USB_STATUS_BULK_REPEAT_TXN 0x20 +#define CPIA2_VC_USB_STATUS_CONFIG_DONE 0x40 +#define CPIA2_VC_USB_STATUS_USB_SUSPEND 0x80 + +#define CPIA2_VC_USB_CMDW 0xAB + +#define CPIA2_VC_USB_DATARW 0xAC + +#define CPIA2_VC_USB_INFO 0xAD + +#define CPIA2_VC_USB_CONFIG 0xAE + +#define CPIA2_VC_USB_SETTINGS 0xAF +#define CPIA2_VC_USB_SETTINGS_CONFIG_MASK 0x03 +#define CPIA2_VC_USB_SETTINGS_INTERFACE_MASK 0x0C +#define CPIA2_VC_USB_SETTINGS_ALTERNATE_MASK 0x70 + +#define CPIA2_VC_USB_ISOLIM 0xB0 + +#define CPIA2_VC_USB_ISOFAILS 0xB1 + +#define CPIA2_VC_USB_ISOMAXPKTHI 0xB2 + +#define CPIA2_VC_USB_ISOMAXPKTLO 0xB3 + +#define CPIA2_VC_V2W_CTRL 0xB8 +#define CPIA2_VC_V2W_SELECT 0x01 + +#define CPIA2_VC_V2W_SCL 0xB9 + +#define CPIA2_VC_V2W_SDA 0xBA + +#define CPIA2_VC_VC_CTRL 0xC0 +#define CPIA2_VC_VC_CTRL_RUN 0x01 +#define CPIA2_VC_VC_CTRL_SINGLESHOT 0x02 +#define CPIA2_VC_VC_CTRL_IDLING 0x04 +#define CPIA2_VC_VC_CTRL_INHIBIT_H_TABLES 0x10 +#define CPIA2_VC_VC_CTRL_INHIBIT_Q_TABLES 0x20 +#define CPIA2_VC_VC_CTRL_INHIBIT_PRIVATE 0x40 + +#define CPIA2_VC_VC_RESTART_IVAL_HI 0xC1 + +#define CPIA2_VC_VC_RESTART_IVAL_LO 0xC2 + +#define CPIA2_VC_VC_FORMAT 0xC3 +#define CPIA2_VC_VC_FORMAT_UFIRST 0x01 +#define CPIA2_VC_VC_FORMAT_MONO 0x02 +#define CPIA2_VC_VC_FORMAT_DECIMATING 0x04 +#define CPIA2_VC_VC_FORMAT_SHORTLINE 0x08 +#define CPIA2_VC_VC_FORMAT_SELFTEST 0x10 + +#define CPIA2_VC_VC_CLOCKS 0xC4 +#define CPIA2_VC_VC_CLOCKS_CLKDIV_MASK 0x03 +#define CPIA2_VC_VC_672_CLOCKS_CIF_DIV_BY_3 0x04 +#define CPIA2_VC_VC_672_CLOCKS_SCALING 0x08 +#define CPIA2_VC_VC_CLOCKS_LOGDIV0 0x00 +#define CPIA2_VC_VC_CLOCKS_LOGDIV1 0x01 +#define CPIA2_VC_VC_CLOCKS_LOGDIV2 0x02 +#define CPIA2_VC_VC_CLOCKS_LOGDIV3 0x03 +#define CPIA2_VC_VC_676_CLOCKS_CIF_DIV_BY_3 0x08 +#define CPIA2_VC_VC_676_CLOCKS_SCALING 0x10 + +#define CPIA2_VC_VC_IHSIZE_LO 0xC5 + +#define CPIA2_VC_VC_XLIM_HI 0xC6 + +#define CPIA2_VC_VC_XLIM_LO 0xC7 + +#define CPIA2_VC_VC_YLIM_HI 0xC8 + +#define CPIA2_VC_VC_YLIM_LO 0xC9 + +#define CPIA2_VC_VC_OHSIZE 0xCA + +#define CPIA2_VC_VC_OVSIZE 0xCB + +#define CPIA2_VC_VC_HCROP 0xCC + +#define CPIA2_VC_VC_VCROP 0xCD + +#define CPIA2_VC_VC_HPHASE 0xCE + +#define CPIA2_VC_VC_VPHASE 0xCF + +#define CPIA2_VC_VC_HISPAN 0xD0 + +#define CPIA2_VC_VC_VISPAN 0xD1 + +#define CPIA2_VC_VC_HICROP 0xD2 + +#define CPIA2_VC_VC_VICROP 0xD3 + +#define CPIA2_VC_VC_HFRACT 0xD4 +#define CPIA2_VC_VC_HFRACT_DEN_MASK 0x0F +#define CPIA2_VC_VC_HFRACT_NUM_MASK 0xF0 + +#define CPIA2_VC_VC_VFRACT 0xD5 +#define CPIA2_VC_VC_VFRACT_DEN_MASK 0x0F +#define CPIA2_VC_VC_VFRACT_NUM_MASK 0xF0 + +#define CPIA2_VC_VC_JPEG_OPT 0xD6 +#define CPIA2_VC_VC_JPEG_OPT_DOUBLE_SQUEEZE 0x01 +#define CPIA2_VC_VC_JPEG_OPT_NO_DC_AUTO_SQUEEZE 0x02 +#define CPIA2_VC_VC_JPEG_OPT_AUTO_SQUEEZE 0x04 +#define CPIA2_VC_VC_JPEG_OPT_DEFAULT (CPIA2_VC_VC_JPEG_OPT_DOUBLE_SQUEEZE|\ + CPIA2_VC_VC_JPEG_OPT_AUTO_SQUEEZE) + + +#define CPIA2_VC_VC_CREEP_PERIOD 0xD7 +#define CPIA2_VC_VC_USER_SQUEEZE 0xD8 +#define CPIA2_VC_VC_TARGET_KB 0xD9 + +#define CPIA2_VC_VC_AUTO_SQUEEZE 0xE6 + + +/*** + * VP register set (Bank 2) + ***/ +#define CPIA2_VP_DEVICEH 0 +#define CPIA2_VP_DEVICEL 1 + +#define CPIA2_VP_SYSTEMSTATE 0x02 +#define CPIA2_VP_SYSTEMSTATE_HK_ALIVE 0x01 + +#define CPIA2_VP_SYSTEMCTRL 0x03 +#define CPIA2_VP_SYSTEMCTRL_REQ_CLEAR_ERROR 0x80 +#define CPIA2_VP_SYSTEMCTRL_POWER_DOWN_PLL 0x20 +#define CPIA2_VP_SYSTEMCTRL_REQ_SUSPEND_STATE 0x10 +#define CPIA2_VP_SYSTEMCTRL_REQ_SERIAL_WAKEUP 0x08 +#define CPIA2_VP_SYSTEMCTRL_REQ_AUTOLOAD 0x04 +#define CPIA2_VP_SYSTEMCTRL_HK_CONTROL 0x02 +#define CPIA2_VP_SYSTEMCTRL_POWER_CONTROL 0x01 + +#define CPIA2_VP_SENSOR_FLAGS 0x05 +#define CPIA2_VP_SENSOR_FLAGS_404 0x01 +#define CPIA2_VP_SENSOR_FLAGS_407 0x02 +#define CPIA2_VP_SENSOR_FLAGS_409 0x04 +#define CPIA2_VP_SENSOR_FLAGS_410 0x08 +#define CPIA2_VP_SENSOR_FLAGS_500 0x10 + +#define CPIA2_VP_SENSOR_REV 0x06 + +#define CPIA2_VP_DEVICE_CONFIG 0x07 +#define CPIA2_VP_DEVICE_CONFIG_SERIAL_BRIDGE 0x01 + +#define CPIA2_VP_GPIO_DIRECTION 0x08 +#define CPIA2_VP_GPIO_READ 0xFF +#define CPIA2_VP_GPIO_WRITE 0x00 + +#define CPIA2_VP_GPIO_DATA 0x09 + +#define CPIA2_VP_RAM_ADDR_H 0x0A +#define CPIA2_VP_RAM_ADDR_L 0x0B +#define CPIA2_VP_RAM_DATA 0x0C + +#define CPIA2_VP_PATCH_REV 0x0F + +#define CPIA2_VP4_USER_MODE 0x10 +#define CPIA2_VP5_USER_MODE 0x13 +#define CPIA2_VP_USER_MODE_CIF 0x01 +#define CPIA2_VP_USER_MODE_QCIFDS 0x02 +#define CPIA2_VP_USER_MODE_QCIFPTC 0x04 +#define CPIA2_VP_USER_MODE_QVGADS 0x08 +#define CPIA2_VP_USER_MODE_QVGAPTC 0x10 +#define CPIA2_VP_USER_MODE_VGA 0x20 + +#define CPIA2_VP4_FRAMERATE_REQUEST 0x11 +#define CPIA2_VP5_FRAMERATE_REQUEST 0x14 +#define CPIA2_VP_FRAMERATE_60 0x80 +#define CPIA2_VP_FRAMERATE_50 0x40 +#define CPIA2_VP_FRAMERATE_30 0x20 +#define CPIA2_VP_FRAMERATE_25 0x10 +#define CPIA2_VP_FRAMERATE_15 0x08 +#define CPIA2_VP_FRAMERATE_12_5 0x04 +#define CPIA2_VP_FRAMERATE_7_5 0x02 +#define CPIA2_VP_FRAMERATE_6_25 0x01 + +#define CPIA2_VP4_USER_EFFECTS 0x12 +#define CPIA2_VP5_USER_EFFECTS 0x15 +#define CPIA2_VP_USER_EFFECTS_COLBARS 0x01 +#define CPIA2_VP_USER_EFFECTS_COLBARS_GRAD 0x02 +#define CPIA2_VP_USER_EFFECTS_MIRROR 0x04 +#define CPIA2_VP_USER_EFFECTS_FLIP 0x40 // VP5 only + +/* NOTE: CPIA2_VP_EXPOSURE_MODES shares the same register as VP5 User + * Effects */ +#define CPIA2_VP_EXPOSURE_MODES 0x15 +#define CPIA2_VP_EXPOSURE_MODES_INHIBIT_FLICKER 0x20 +#define CPIA2_VP_EXPOSURE_MODES_COMPILE_EXP 0x10 + +#define CPIA2_VP4_EXPOSURE_TARGET 0x16 // VP4 +#define CPIA2_VP5_EXPOSURE_TARGET 0x20 // VP5 + +#define CPIA2_VP_FLICKER_MODES 0x1B +#define CPIA2_VP_FLICKER_MODES_50HZ 0x80 +#define CPIA2_VP_FLICKER_MODES_CUSTOM_FLT_FFREQ 0x40 +#define CPIA2_VP_FLICKER_MODES_NEVER_FLICKER 0x20 +#define CPIA2_VP_FLICKER_MODES_INHIBIT_RUB 0x10 +#define CPIA2_VP_FLICKER_MODES_ADJUST_LINE_FREQ 0x08 +#define CPIA2_VP_FLICKER_MODES_CUSTOM_INT_FFREQ 0x04 + +#define CPIA2_VP_UMISC 0x1D +#define CPIA2_VP_UMISC_FORCE_MONO 0x80 +#define CPIA2_VP_UMISC_FORCE_ID_MASK 0x40 +#define CPIA2_VP_UMISC_INHIBIT_AUTO_FGS 0x20 +#define CPIA2_VP_UMISC_INHIBIT_AUTO_DIMS 0x08 +#define CPIA2_VP_UMISC_OPT_FOR_SENSOR_DS 0x04 +#define CPIA2_VP_UMISC_INHIBIT_AUTO_MODE_INT 0x02 + +#define CPIA2_VP5_ANTIFLKRSETUP 0x22 //34 + +#define CPIA2_VP_INTERPOLATION 0x24 +#define CPIA2_VP_INTERPOLATION_EVEN_FIRST 0x40 +#define CPIA2_VP_INTERPOLATION_HJOG 0x20 +#define CPIA2_VP_INTERPOLATION_VJOG 0x10 + +#define CPIA2_VP_GAMMA 0x25 +#define CPIA2_VP_DEFAULT_GAMMA 0x10 + +#define CPIA2_VP_YRANGE 0x26 + +#define CPIA2_VP_SATURATION 0x27 + +#define CPIA2_VP5_MYBLACK_LEVEL 0x3A //58 +#define CPIA2_VP5_MCYRANGE 0x3B //59 +#define CPIA2_VP5_MYCEILING 0x3C //60 +#define CPIA2_VP5_MCUVSATURATION 0x3D //61 + + +#define CPIA2_VP_REHASH_VALUES 0x60 + + +/*** + * Common sensor registers + ***/ +#define CPIA2_SENSOR_DEVICE_H 0x00 +#define CPIA2_SENSOR_DEVICE_L 0x01 + +#define CPIA2_SENSOR_DATA_FORMAT 0x16 +#define CPIA2_SENSOR_DATA_FORMAT_HMIRROR 0x08 +#define CPIA2_SENSOR_DATA_FORMAT_VMIRROR 0x10 + +#define CPIA2_SENSOR_CR1 0x76 +#define CPIA2_SENSOR_CR1_STAND_BY 0x01 +#define CPIA2_SENSOR_CR1_DOWN_RAMP_GEN 0x02 +#define CPIA2_SENSOR_CR1_DOWN_COLUMN_ADC 0x04 +#define CPIA2_SENSOR_CR1_DOWN_CAB_REGULATOR 0x08 +#define CPIA2_SENSOR_CR1_DOWN_AUDIO_REGULATOR 0x10 +#define CPIA2_SENSOR_CR1_DOWN_VRT_AMP 0x20 +#define CPIA2_SENSOR_CR1_DOWN_BAND_GAP 0x40 + +#endif diff --git a/drivers/staging/media/deprecated/cpia2/cpia2_usb.c b/drivers/staging/media/deprecated/cpia2/cpia2_usb.c new file mode 100644 index 000000000000..cba03b286473 --- /dev/null +++ b/drivers/staging/media/deprecated/cpia2/cpia2_usb.c @@ -0,0 +1,966 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/**************************************************************************** + * + * Filename: cpia2_usb.c + * + * Copyright 2001, STMicrolectronics, Inc. + * Contact: steve.miller@st.com + * + * Description: + * This is a USB driver for CPia2 based video cameras. + * The infrastructure of this driver is based on the cpia usb driver by + * Jochen Scharrlach and Johannes Erdfeldt. + * + * Stripped of 2.4 stuff ready for main kernel submit by + * Alan Cox <alan@lxorguk.ukuu.org.uk> + ****************************************************************************/ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/usb.h> +#include <linux/module.h> + +#include "cpia2.h" + +static int frame_sizes[] = { + 0, // USBIF_CMDONLY + 0, // USBIF_BULK + 128, // USBIF_ISO_1 + 384, // USBIF_ISO_2 + 640, // USBIF_ISO_3 + 768, // USBIF_ISO_4 + 896, // USBIF_ISO_5 + 1023, // USBIF_ISO_6 +}; + +#define FRAMES_PER_DESC 10 +#define FRAME_SIZE_PER_DESC frame_sizes[cam->cur_alt] + +static void process_frame(struct camera_data *cam); +static void cpia2_usb_complete(struct urb *urb); +static int cpia2_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id); +static void cpia2_usb_disconnect(struct usb_interface *intf); +static int cpia2_usb_suspend(struct usb_interface *intf, pm_message_t message); +static int cpia2_usb_resume(struct usb_interface *intf); + +static void free_sbufs(struct camera_data *cam); +static void add_APPn(struct camera_data *cam); +static void add_COM(struct camera_data *cam); +static int submit_urbs(struct camera_data *cam); +static int set_alternate(struct camera_data *cam, unsigned int alt); +static int configure_transfer_mode(struct camera_data *cam, unsigned int alt); + +static const struct usb_device_id cpia2_id_table[] = { + {USB_DEVICE(0x0553, 0x0100)}, + {USB_DEVICE(0x0553, 0x0140)}, + {USB_DEVICE(0x0553, 0x0151)}, /* STV0676 */ + {} /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, cpia2_id_table); + +static struct usb_driver cpia2_driver = { + .name = "cpia2", + .probe = cpia2_usb_probe, + .disconnect = cpia2_usb_disconnect, + .suspend = cpia2_usb_suspend, + .resume = cpia2_usb_resume, + .reset_resume = cpia2_usb_resume, + .id_table = cpia2_id_table +}; + + +/****************************************************************************** + * + * process_frame + * + *****************************************************************************/ +static void process_frame(struct camera_data *cam) +{ + static int frame_count; + + unsigned char *inbuff = cam->workbuff->data; + + DBG("Processing frame #%d, current:%d\n", + cam->workbuff->num, cam->curbuff->num); + + if(cam->workbuff->length > cam->workbuff->max_length) + cam->workbuff->max_length = cam->workbuff->length; + + if ((inbuff[0] == 0xFF) && (inbuff[1] == 0xD8)) { + frame_count++; + } else { + cam->workbuff->status = FRAME_ERROR; + DBG("Start of frame not found\n"); + return; + } + + /*** + * Now the output buffer should have a JPEG image in it. + ***/ + if(!cam->first_image_seen) { + /* Always skip the first image after streaming + * starts. It is almost certainly corrupt. */ + cam->first_image_seen = 1; + cam->workbuff->status = FRAME_EMPTY; + return; + } + if (cam->workbuff->length > 3) { + if(cam->mmapped && + cam->workbuff->length < cam->workbuff->max_length) { + /* No junk in the buffers */ + memset(cam->workbuff->data+cam->workbuff->length, + 0, cam->workbuff->max_length- + cam->workbuff->length); + } + cam->workbuff->max_length = cam->workbuff->length; + cam->workbuff->status = FRAME_READY; + + if(!cam->mmapped && cam->num_frames > 2) { + /* During normal reading, the most recent + * frame will be read. If the current frame + * hasn't started reading yet, it will never + * be read, so mark it empty. If the buffer is + * mmapped, or we have few buffers, we need to + * wait for the user to free the buffer. + * + * NOTE: This is not entirely foolproof with 3 + * buffers, but it would take an EXTREMELY + * overloaded system to cause problems (possible + * image data corruption). Basically, it would + * need to take more time to execute cpia2_read + * than it would for the camera to send + * cam->num_frames-2 frames before problems + * could occur. + */ + cam->curbuff->status = FRAME_EMPTY; + } + cam->curbuff = cam->workbuff; + cam->workbuff = cam->workbuff->next; + DBG("Changed buffers, work:%d, current:%d\n", + cam->workbuff->num, cam->curbuff->num); + return; + } else { + DBG("Not enough data for an image.\n"); + } + + cam->workbuff->status = FRAME_ERROR; + return; +} + +/****************************************************************************** + * + * add_APPn + * + * Adds a user specified APPn record + *****************************************************************************/ +static void add_APPn(struct camera_data *cam) +{ + if(cam->APP_len > 0) { + cam->workbuff->data[cam->workbuff->length++] = 0xFF; + cam->workbuff->data[cam->workbuff->length++] = 0xE0+cam->APPn; + cam->workbuff->data[cam->workbuff->length++] = 0; + cam->workbuff->data[cam->workbuff->length++] = cam->APP_len+2; + memcpy(cam->workbuff->data+cam->workbuff->length, + cam->APP_data, cam->APP_len); + cam->workbuff->length += cam->APP_len; + } +} + +/****************************************************************************** + * + * add_COM + * + * Adds a user specified COM record + *****************************************************************************/ +static void add_COM(struct camera_data *cam) +{ + if(cam->COM_len > 0) { + cam->workbuff->data[cam->workbuff->length++] = 0xFF; + cam->workbuff->data[cam->workbuff->length++] = 0xFE; + cam->workbuff->data[cam->workbuff->length++] = 0; + cam->workbuff->data[cam->workbuff->length++] = cam->COM_len+2; + memcpy(cam->workbuff->data+cam->workbuff->length, + cam->COM_data, cam->COM_len); + cam->workbuff->length += cam->COM_len; + } +} + +/****************************************************************************** + * + * cpia2_usb_complete + * + * callback when incoming packet is received + *****************************************************************************/ +static void cpia2_usb_complete(struct urb *urb) +{ + int i; + unsigned char *cdata; + static bool frame_ready = false; + struct camera_data *cam = (struct camera_data *) urb->context; + + if (urb->status!=0) { + if (!(urb->status == -ENOENT || + urb->status == -ECONNRESET || + urb->status == -ESHUTDOWN)) + { + DBG("urb->status = %d!\n", urb->status); + } + DBG("Stopping streaming\n"); + return; + } + + if (!cam->streaming || !video_is_registered(&cam->vdev)) { + LOG("Will now stop the streaming: streaming = %d, present=%d\n", + cam->streaming, video_is_registered(&cam->vdev)); + return; + } + + /*** + * Packet collater + ***/ + //DBG("Collating %d packets\n", urb->number_of_packets); + for (i = 0; i < urb->number_of_packets; i++) { + u16 checksum, iso_checksum; + int j; + int n = urb->iso_frame_desc[i].actual_length; + int st = urb->iso_frame_desc[i].status; + + if(cam->workbuff->status == FRAME_READY) { + struct framebuf *ptr; + /* Try to find an available buffer */ + DBG("workbuff full, searching\n"); + for (ptr = cam->workbuff->next; + ptr != cam->workbuff; + ptr = ptr->next) + { + if (ptr->status == FRAME_EMPTY) { + ptr->status = FRAME_READING; + ptr->length = 0; + break; + } + } + if (ptr == cam->workbuff) + break; /* No READING or EMPTY buffers left */ + + cam->workbuff = ptr; + } + + if (cam->workbuff->status == FRAME_EMPTY || + cam->workbuff->status == FRAME_ERROR) { + cam->workbuff->status = FRAME_READING; + cam->workbuff->length = 0; + } + + //DBG(" Packet %d length = %d, status = %d\n", i, n, st); + cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + + if (st) { + LOG("cpia2 data error: [%d] len=%d, status = %d\n", + i, n, st); + if(!ALLOW_CORRUPT) + cam->workbuff->status = FRAME_ERROR; + continue; + } + + if(n<=2) + continue; + + checksum = 0; + for(j=0; j<n-2; ++j) + checksum += cdata[j]; + iso_checksum = cdata[j] + cdata[j+1]*256; + if(checksum != iso_checksum) { + LOG("checksum mismatch: [%d] len=%d, calculated = %x, checksum = %x\n", + i, n, (int)checksum, (int)iso_checksum); + if(!ALLOW_CORRUPT) { + cam->workbuff->status = FRAME_ERROR; + continue; + } + } + n -= 2; + + if(cam->workbuff->status != FRAME_READING) { + if((0xFF == cdata[0] && 0xD8 == cdata[1]) || + (0xD8 == cdata[0] && 0xFF == cdata[1] && + 0 != cdata[2])) { + /* frame is skipped, but increment total + * frame count anyway */ + cam->frame_count++; + } + DBG("workbuff not reading, status=%d\n", + cam->workbuff->status); + continue; + } + + if (cam->frame_size < cam->workbuff->length + n) { + ERR("buffer overflow! length: %d, n: %d\n", + cam->workbuff->length, n); + cam->workbuff->status = FRAME_ERROR; + if(cam->workbuff->length > cam->workbuff->max_length) + cam->workbuff->max_length = + cam->workbuff->length; + continue; + } + + if (cam->workbuff->length == 0) { + int data_offset; + if ((0xD8 == cdata[0]) && (0xFF == cdata[1])) { + data_offset = 1; + } else if((0xFF == cdata[0]) && (0xD8 == cdata[1]) + && (0xFF == cdata[2])) { + data_offset = 2; + } else { + DBG("Ignoring packet, not beginning!\n"); + continue; + } + DBG("Start of frame pattern found\n"); + cam->workbuff->ts = ktime_get_ns(); + cam->workbuff->seq = cam->frame_count++; + cam->workbuff->data[0] = 0xFF; + cam->workbuff->data[1] = 0xD8; + cam->workbuff->length = 2; + add_APPn(cam); + add_COM(cam); + memcpy(cam->workbuff->data+cam->workbuff->length, + cdata+data_offset, n-data_offset); + cam->workbuff->length += n-data_offset; + } else if (cam->workbuff->length > 0) { + memcpy(cam->workbuff->data + cam->workbuff->length, + cdata, n); + cam->workbuff->length += n; + } + + if ((cam->workbuff->length >= 3) && + (cam->workbuff->data[cam->workbuff->length - 3] == 0xFF) && + (cam->workbuff->data[cam->workbuff->length - 2] == 0xD9) && + (cam->workbuff->data[cam->workbuff->length - 1] == 0xFF)) { + frame_ready = true; + cam->workbuff->data[cam->workbuff->length - 1] = 0; + cam->workbuff->length -= 1; + } else if ((cam->workbuff->length >= 2) && + (cam->workbuff->data[cam->workbuff->length - 2] == 0xFF) && + (cam->workbuff->data[cam->workbuff->length - 1] == 0xD9)) { + frame_ready = true; + } + + if (frame_ready) { + DBG("Workbuff image size = %d\n",cam->workbuff->length); + process_frame(cam); + + frame_ready = false; + + if (waitqueue_active(&cam->wq_stream)) + wake_up_interruptible(&cam->wq_stream); + } + } + + if(cam->streaming) { + /* resubmit */ + urb->dev = cam->dev; + if ((i = usb_submit_urb(urb, GFP_ATOMIC)) != 0) + ERR("%s: usb_submit_urb ret %d!\n", __func__, i); + } +} + +/****************************************************************************** + * + * configure_transfer_mode + * + *****************************************************************************/ +static int configure_transfer_mode(struct camera_data *cam, unsigned int alt) +{ + static unsigned char iso_regs[8][4] = { + {0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00}, + {0xB9, 0x00, 0x00, 0x7E}, + {0xB9, 0x00, 0x01, 0x7E}, + {0xB9, 0x00, 0x02, 0x7E}, + {0xB9, 0x00, 0x02, 0xFE}, + {0xB9, 0x00, 0x03, 0x7E}, + {0xB9, 0x00, 0x03, 0xFD} + }; + struct cpia2_command cmd; + unsigned char reg; + + if (!video_is_registered(&cam->vdev)) + return -ENODEV; + + /*** + * Write the isoc registers according to the alternate selected + ***/ + cmd.direction = TRANSFER_WRITE; + cmd.buffer.block_data[0] = iso_regs[alt][0]; + cmd.buffer.block_data[1] = iso_regs[alt][1]; + cmd.buffer.block_data[2] = iso_regs[alt][2]; + cmd.buffer.block_data[3] = iso_regs[alt][3]; + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; + cmd.start = CPIA2_VC_USB_ISOLIM; + cmd.reg_count = 4; + cpia2_send_command(cam, &cmd); + + /*** + * Enable relevant streams before starting polling. + * First read USB Stream Config Register. + ***/ + cmd.direction = TRANSFER_READ; + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; + cmd.start = CPIA2_VC_USB_STRM; + cmd.reg_count = 1; + cpia2_send_command(cam, &cmd); + reg = cmd.buffer.block_data[0]; + + /* Clear iso, bulk, and int */ + reg &= ~(CPIA2_VC_USB_STRM_BLK_ENABLE | + CPIA2_VC_USB_STRM_ISO_ENABLE | + CPIA2_VC_USB_STRM_INT_ENABLE); + + if (alt == USBIF_BULK) { + DBG("Enabling bulk xfer\n"); + reg |= CPIA2_VC_USB_STRM_BLK_ENABLE; /* Enable Bulk */ + cam->xfer_mode = XFER_BULK; + } else if (alt >= USBIF_ISO_1) { + DBG("Enabling ISOC xfer\n"); + reg |= CPIA2_VC_USB_STRM_ISO_ENABLE; + cam->xfer_mode = XFER_ISOC; + } + + cmd.buffer.block_data[0] = reg; + cmd.direction = TRANSFER_WRITE; + cmd.start = CPIA2_VC_USB_STRM; + cmd.reg_count = 1; + cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC; + cpia2_send_command(cam, &cmd); + + return 0; +} + +/****************************************************************************** + * + * cpia2_usb_change_streaming_alternate + * + *****************************************************************************/ +int cpia2_usb_change_streaming_alternate(struct camera_data *cam, + unsigned int alt) +{ + int ret = 0; + + if(alt < USBIF_ISO_1 || alt > USBIF_ISO_6) + return -EINVAL; + + if(alt == cam->params.camera_state.stream_mode) + return 0; + + cpia2_usb_stream_pause(cam); + + configure_transfer_mode(cam, alt); + + cam->params.camera_state.stream_mode = alt; + + /* Reset the camera to prevent image quality degradation */ + cpia2_reset_camera(cam); + + cpia2_usb_stream_resume(cam); + + return ret; +} + +/****************************************************************************** + * + * set_alternate + * + *****************************************************************************/ +static int set_alternate(struct camera_data *cam, unsigned int alt) +{ + int ret = 0; + + if(alt == cam->cur_alt) + return 0; + + if (cam->cur_alt != USBIF_CMDONLY) { + DBG("Changing from alt %d to %d\n", cam->cur_alt, USBIF_CMDONLY); + ret = usb_set_interface(cam->dev, cam->iface, USBIF_CMDONLY); + if (ret != 0) + return ret; + } + if (alt != USBIF_CMDONLY) { + DBG("Changing from alt %d to %d\n", USBIF_CMDONLY, alt); + ret = usb_set_interface(cam->dev, cam->iface, alt); + if (ret != 0) + return ret; + } + + cam->old_alt = cam->cur_alt; + cam->cur_alt = alt; + + return ret; +} + +/****************************************************************************** + * + * free_sbufs + * + * Free all cam->sbuf[]. All non-NULL .data and .urb members that are non-NULL + * are assumed to be allocated. Non-NULL .urb members are also assumed to be + * submitted (and must therefore be killed before they are freed). + *****************************************************************************/ +static void free_sbufs(struct camera_data *cam) +{ + int i; + + for (i = 0; i < NUM_SBUF; i++) { + if(cam->sbuf[i].urb) { + usb_kill_urb(cam->sbuf[i].urb); + usb_free_urb(cam->sbuf[i].urb); + cam->sbuf[i].urb = NULL; + } + if(cam->sbuf[i].data) { + kfree(cam->sbuf[i].data); + cam->sbuf[i].data = NULL; + } + } +} + +/******* +* Convenience functions +*******/ +/**************************************************************************** + * + * write_packet + * + ***************************************************************************/ +static int write_packet(struct usb_device *udev, + u8 request, u8 * registers, u16 start, size_t size) +{ + unsigned char *buf; + int ret; + + if (!registers || size <= 0) + return -EINVAL; + + buf = kmemdup(registers, size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + request, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + start, /* value */ + 0, /* index */ + buf, /* buffer */ + size, + 1000); + + kfree(buf); + return ret; +} + +/**************************************************************************** + * + * read_packet + * + ***************************************************************************/ +static int read_packet(struct usb_device *udev, + u8 request, u8 * registers, u16 start, size_t size) +{ + unsigned char *buf; + int ret; + + if (!registers || size <= 0) + return -EINVAL; + + buf = kmalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = usb_control_msg(udev, + usb_rcvctrlpipe(udev, 0), + request, + USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_DEVICE, + start, /* value */ + 0, /* index */ + buf, /* buffer */ + size, + 1000); + + if (ret >= 0) + memcpy(registers, buf, size); + + kfree(buf); + + return ret; +} + +/****************************************************************************** + * + * cpia2_usb_transfer_cmd + * + *****************************************************************************/ +int cpia2_usb_transfer_cmd(struct camera_data *cam, + void *registers, + u8 request, u8 start, u8 count, u8 direction) +{ + int err = 0; + struct usb_device *udev = cam->dev; + + if (!udev) { + ERR("%s: Internal driver error: udev is NULL\n", __func__); + return -EINVAL; + } + + if (!registers) { + ERR("%s: Internal driver error: register array is NULL\n", __func__); + return -EINVAL; + } + + if (direction == TRANSFER_READ) { + err = read_packet(udev, request, (u8 *)registers, start, count); + if (err > 0) + err = 0; + } else if (direction == TRANSFER_WRITE) { + err =write_packet(udev, request, (u8 *)registers, start, count); + if (err < 0) { + LOG("Control message failed, err val = %d\n", err); + LOG("Message: request = 0x%0X, start = 0x%0X\n", + request, start); + LOG("Message: count = %d, register[0] = 0x%0X\n", + count, ((unsigned char *) registers)[0]); + } else + err=0; + } else { + LOG("Unexpected first byte of direction: %d\n", + direction); + return -EINVAL; + } + + if(err != 0) + LOG("Unexpected error: %d\n", err); + return err; +} + + +/****************************************************************************** + * + * submit_urbs + * + *****************************************************************************/ +static int submit_urbs(struct camera_data *cam) +{ + struct urb *urb; + int fx, err, i, j; + + for(i=0; i<NUM_SBUF; ++i) { + if (cam->sbuf[i].data) + continue; + cam->sbuf[i].data = + kmalloc_array(FRAME_SIZE_PER_DESC, FRAMES_PER_DESC, + GFP_KERNEL); + if (!cam->sbuf[i].data) { + while (--i >= 0) { + kfree(cam->sbuf[i].data); + cam->sbuf[i].data = NULL; + } + return -ENOMEM; + } + } + + /* We double buffer the Isoc lists, and also know the polling + * interval is every frame (1 == (1 << (bInterval -1))). + */ + for(i=0; i<NUM_SBUF; ++i) { + if(cam->sbuf[i].urb) { + continue; + } + urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); + if (!urb) { + for (j = 0; j < i; j++) + usb_free_urb(cam->sbuf[j].urb); + for (j = 0; j < NUM_SBUF; j++) { + kfree(cam->sbuf[j].data); + cam->sbuf[j].data = NULL; + } + return -ENOMEM; + } + + cam->sbuf[i].urb = urb; + urb->dev = cam->dev; + urb->context = cam; + urb->pipe = usb_rcvisocpipe(cam->dev, 1 /*ISOC endpoint*/); + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = cam->sbuf[i].data; + urb->complete = cpia2_usb_complete; + urb->number_of_packets = FRAMES_PER_DESC; + urb->interval = 1; + urb->transfer_buffer_length = + FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; + + for (fx = 0; fx < FRAMES_PER_DESC; fx++) { + urb->iso_frame_desc[fx].offset = + FRAME_SIZE_PER_DESC * fx; + urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; + } + } + + + /* Queue the ISO urbs, and resubmit in the completion handler */ + for(i=0; i<NUM_SBUF; ++i) { + err = usb_submit_urb(cam->sbuf[i].urb, GFP_KERNEL); + if (err) { + ERR("usb_submit_urb[%d]() = %d\n", i, err); + return err; + } + } + + return 0; +} + +/****************************************************************************** + * + * cpia2_usb_stream_start + * + *****************************************************************************/ +int cpia2_usb_stream_start(struct camera_data *cam, unsigned int alternate) +{ + int ret; + int old_alt; + + if(cam->streaming) + return 0; + + if (cam->flush) { + int i; + DBG("Flushing buffers\n"); + for(i=0; i<cam->num_frames; ++i) { + cam->buffers[i].status = FRAME_EMPTY; + cam->buffers[i].length = 0; + } + cam->curbuff = &cam->buffers[0]; + cam->workbuff = cam->curbuff->next; + cam->flush = false; + } + + old_alt = cam->params.camera_state.stream_mode; + cam->params.camera_state.stream_mode = 0; + ret = cpia2_usb_change_streaming_alternate(cam, alternate); + if (ret < 0) { + int ret2; + ERR("cpia2_usb_change_streaming_alternate() = %d!\n", ret); + cam->params.camera_state.stream_mode = old_alt; + ret2 = set_alternate(cam, USBIF_CMDONLY); + if (ret2 < 0) { + ERR("cpia2_usb_change_streaming_alternate(%d) =%d has already failed. Then tried to call set_alternate(USBIF_CMDONLY) = %d.\n", + alternate, ret, ret2); + } + } else { + cam->frame_count = 0; + cam->streaming = 1; + ret = cpia2_usb_stream_resume(cam); + } + return ret; +} + +/****************************************************************************** + * + * cpia2_usb_stream_pause + * + *****************************************************************************/ +int cpia2_usb_stream_pause(struct camera_data *cam) +{ + int ret = 0; + if(cam->streaming) { + free_sbufs(cam); + ret = set_alternate(cam, USBIF_CMDONLY); + } + return ret; +} + +/****************************************************************************** + * + * cpia2_usb_stream_resume + * + *****************************************************************************/ +int cpia2_usb_stream_resume(struct camera_data *cam) +{ + int ret = 0; + if(cam->streaming) { + cam->first_image_seen = 0; + ret = set_alternate(cam, cam->params.camera_state.stream_mode); + if(ret == 0) { + /* for some reason the user effects need to be set + again when starting streaming. */ + cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE, + cam->params.vp_params.user_effects); + ret = submit_urbs(cam); + } + } + return ret; +} + +/****************************************************************************** + * + * cpia2_usb_stream_stop + * + *****************************************************************************/ +int cpia2_usb_stream_stop(struct camera_data *cam) +{ + int ret; + + ret = cpia2_usb_stream_pause(cam); + cam->streaming = 0; + configure_transfer_mode(cam, 0); + return ret; +} + +/****************************************************************************** + * + * cpia2_usb_probe + * + * Probe and initialize. + *****************************************************************************/ +static int cpia2_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct usb_interface_descriptor *interface; + struct camera_data *cam; + int ret; + + /* A multi-config CPiA2 camera? */ + if (udev->descriptor.bNumConfigurations != 1) + return -ENODEV; + interface = &intf->cur_altsetting->desc; + + /* If we get to this point, we found a CPiA2 camera */ + LOG("CPiA2 USB camera found\n"); + + cam = cpia2_init_camera_struct(intf); + if (cam == NULL) + return -ENOMEM; + + cam->dev = udev; + cam->iface = interface->bInterfaceNumber; + + ret = set_alternate(cam, USBIF_CMDONLY); + if (ret < 0) { + ERR("%s: usb_set_interface error (ret = %d)\n", __func__, ret); + goto alt_err; + } + + + if((ret = cpia2_init_camera(cam)) < 0) { + ERR("%s: failed to initialize cpia2 camera (ret = %d)\n", __func__, ret); + goto alt_err; + } + LOG(" CPiA Version: %d.%02d (%d.%d)\n", + cam->params.version.firmware_revision_hi, + cam->params.version.firmware_revision_lo, + cam->params.version.asic_id, + cam->params.version.asic_rev); + LOG(" CPiA PnP-ID: %04x:%04x:%04x\n", + cam->params.pnp_id.vendor, + cam->params.pnp_id.product, + cam->params.pnp_id.device_revision); + LOG(" SensorID: %d.(version %d)\n", + cam->params.version.sensor_flags, + cam->params.version.sensor_rev); + + usb_set_intfdata(intf, cam); + + ret = cpia2_register_camera(cam); + if (ret < 0) { + ERR("%s: Failed to register cpia2 camera (ret = %d)\n", __func__, ret); + goto alt_err; + } + + return 0; + +alt_err: + cpia2_deinit_camera_struct(cam, intf); + return ret; +} + +/****************************************************************************** + * + * cpia2_disconnect + * + *****************************************************************************/ +static void cpia2_usb_disconnect(struct usb_interface *intf) +{ + struct camera_data *cam = usb_get_intfdata(intf); + usb_set_intfdata(intf, NULL); + + DBG("Stopping stream\n"); + cpia2_usb_stream_stop(cam); + + mutex_lock(&cam->v4l2_lock); + DBG("Unregistering camera\n"); + cpia2_unregister_camera(cam); + v4l2_device_disconnect(&cam->v4l2_dev); + mutex_unlock(&cam->v4l2_lock); + + if(cam->buffers) { + DBG("Wakeup waiting processes\n"); + cam->curbuff->status = FRAME_READY; + cam->curbuff->length = 0; + wake_up_interruptible(&cam->wq_stream); + } + + v4l2_device_put(&cam->v4l2_dev); + + LOG("CPiA2 camera disconnected.\n"); +} + +static int cpia2_usb_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct camera_data *cam = usb_get_intfdata(intf); + + mutex_lock(&cam->v4l2_lock); + if (cam->streaming) { + cpia2_usb_stream_stop(cam); + cam->streaming = 1; + } + mutex_unlock(&cam->v4l2_lock); + + dev_info(&intf->dev, "going into suspend..\n"); + return 0; +} + +/* Resume device - start device. */ +static int cpia2_usb_resume(struct usb_interface *intf) +{ + struct camera_data *cam = usb_get_intfdata(intf); + + mutex_lock(&cam->v4l2_lock); + v4l2_ctrl_handler_setup(&cam->hdl); + if (cam->streaming) { + cam->streaming = 0; + cpia2_usb_stream_start(cam, + cam->params.camera_state.stream_mode); + } + mutex_unlock(&cam->v4l2_lock); + + dev_info(&intf->dev, "coming out of suspend..\n"); + return 0; +} + +/****************************************************************************** + * + * usb_cpia2_init + * + *****************************************************************************/ +int cpia2_usb_init(void) +{ + return usb_register(&cpia2_driver); +} + +/****************************************************************************** + * + * usb_cpia_cleanup + * + *****************************************************************************/ +void cpia2_usb_cleanup(void) +{ + schedule_timeout(2 * HZ); + usb_deregister(&cpia2_driver); +} diff --git a/drivers/staging/media/deprecated/cpia2/cpia2_v4l.c b/drivers/staging/media/deprecated/cpia2/cpia2_v4l.c new file mode 100644 index 000000000000..926ecfc9b64a --- /dev/null +++ b/drivers/staging/media/deprecated/cpia2/cpia2_v4l.c @@ -0,0 +1,1226 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/**************************************************************************** + * + * Filename: cpia2_v4l.c + * + * Copyright 2001, STMicrolectronics, Inc. + * Contact: steve.miller@st.com + * Copyright 2001,2005, Scott J. Bertin <scottbertin@yahoo.com> + * + * Description: + * This is a USB driver for CPia2 based video cameras. + * The infrastructure of this driver is based on the cpia usb driver by + * Jochen Scharrlach and Johannes Erdfeldt. + * + * Stripped of 2.4 stuff ready for main kernel submit by + * Alan Cox <alan@lxorguk.ukuu.org.uk> + ****************************************************************************/ + +#define CPIA_VERSION "3.0.1" + +#include <linux/module.h> +#include <linux/time.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/videodev2.h> +#include <linux/stringify.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-event.h> + +#include "cpia2.h" + +static int video_nr = -1; +module_param(video_nr, int, 0); +MODULE_PARM_DESC(video_nr, "video device to register (0=/dev/video0, etc)"); + +static int buffer_size = 68 * 1024; +module_param(buffer_size, int, 0); +MODULE_PARM_DESC(buffer_size, "Size for each frame buffer in bytes (default 68k)"); + +static int num_buffers = 3; +module_param(num_buffers, int, 0); +MODULE_PARM_DESC(num_buffers, "Number of frame buffers (1-" + __stringify(VIDEO_MAX_FRAME) ", default 3)"); + +static int alternate = DEFAULT_ALT; +module_param(alternate, int, 0); +MODULE_PARM_DESC(alternate, "USB Alternate (" __stringify(USBIF_ISO_1) "-" + __stringify(USBIF_ISO_6) ", default " + __stringify(DEFAULT_ALT) ")"); + +static int flicker_mode; +module_param(flicker_mode, int, 0); +MODULE_PARM_DESC(flicker_mode, "Flicker frequency (0 (disabled), " __stringify(50) " or " + __stringify(60) ", default 0)"); + +MODULE_AUTHOR("Steve Miller (STMicroelectronics) <steve.miller@st.com>"); +MODULE_DESCRIPTION("V4L-driver for STMicroelectronics CPiA2 based cameras"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(CPIA_VERSION); + +#define ABOUT "V4L-Driver for Vision CPiA2 based cameras" +#define CPIA2_CID_USB_ALT (V4L2_CID_USER_BASE | 0xf000) + +/****************************************************************************** + * + * cpia2_open + * + *****************************************************************************/ +static int cpia2_open(struct file *file) +{ + struct camera_data *cam = video_drvdata(file); + int retval; + + if (mutex_lock_interruptible(&cam->v4l2_lock)) + return -ERESTARTSYS; + retval = v4l2_fh_open(file); + if (retval) + goto open_unlock; + + if (v4l2_fh_is_singular_file(file)) { + if (cpia2_allocate_buffers(cam)) { + v4l2_fh_release(file); + retval = -ENOMEM; + goto open_unlock; + } + + /* reset the camera */ + if (cpia2_reset_camera(cam) < 0) { + v4l2_fh_release(file); + retval = -EIO; + goto open_unlock; + } + + cam->APP_len = 0; + cam->COM_len = 0; + } + + cpia2_dbg_dump_registers(cam); +open_unlock: + mutex_unlock(&cam->v4l2_lock); + return retval; +} + +/****************************************************************************** + * + * cpia2_close + * + *****************************************************************************/ +static int cpia2_close(struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct camera_data *cam = video_get_drvdata(dev); + + mutex_lock(&cam->v4l2_lock); + if (video_is_registered(&cam->vdev) && v4l2_fh_is_singular_file(file)) { + cpia2_usb_stream_stop(cam); + + /* save camera state for later open */ + cpia2_save_camera_state(cam); + + cpia2_set_low_power(cam); + cpia2_free_buffers(cam); + } + + if (cam->stream_fh == file->private_data) { + cam->stream_fh = NULL; + cam->mmapped = 0; + } + mutex_unlock(&cam->v4l2_lock); + return v4l2_fh_release(file); +} + +/****************************************************************************** + * + * cpia2_v4l_read + * + *****************************************************************************/ +static ssize_t cpia2_v4l_read(struct file *file, char __user *buf, size_t count, + loff_t *off) +{ + struct camera_data *cam = video_drvdata(file); + int noblock = file->f_flags & O_NONBLOCK; + ssize_t ret; + + if (!cam) + return -EINVAL; + + if (mutex_lock_interruptible(&cam->v4l2_lock)) + return -ERESTARTSYS; + ret = cpia2_read(cam, buf, count, noblock); + mutex_unlock(&cam->v4l2_lock); + return ret; +} + +/****************************************************************************** + * + * cpia2_v4l_poll + * + *****************************************************************************/ +static __poll_t cpia2_v4l_poll(struct file *filp, struct poll_table_struct *wait) +{ + struct camera_data *cam = video_drvdata(filp); + __poll_t res; + + mutex_lock(&cam->v4l2_lock); + res = cpia2_poll(cam, filp, wait); + mutex_unlock(&cam->v4l2_lock); + return res; +} + +static int sync(struct camera_data *cam, int frame_nr) +{ + struct framebuf *frame = &cam->buffers[frame_nr]; + + while (1) { + if (frame->status == FRAME_READY) + return 0; + + if (!cam->streaming) { + frame->status = FRAME_READY; + frame->length = 0; + return 0; + } + + mutex_unlock(&cam->v4l2_lock); + wait_event_interruptible(cam->wq_stream, + !cam->streaming || + frame->status == FRAME_READY); + mutex_lock(&cam->v4l2_lock); + if (signal_pending(current)) + return -ERESTARTSYS; + if (!video_is_registered(&cam->vdev)) + return -ENOTTY; + } +} + +/****************************************************************************** + * + * ioctl_querycap + * + * V4L2 device capabilities + * + *****************************************************************************/ + +static int cpia2_querycap(struct file *file, void *fh, struct v4l2_capability *vc) +{ + struct camera_data *cam = video_drvdata(file); + + strscpy(vc->driver, "cpia2", sizeof(vc->driver)); + + if (cam->params.pnp_id.product == 0x151) + strscpy(vc->card, "QX5 Microscope", sizeof(vc->card)); + else + strscpy(vc->card, "CPiA2 Camera", sizeof(vc->card)); + switch (cam->params.pnp_id.device_type) { + case DEVICE_STV_672: + strcat(vc->card, " (672/"); + break; + case DEVICE_STV_676: + strcat(vc->card, " (676/"); + break; + default: + strcat(vc->card, " (XXX/"); + break; + } + switch (cam->params.version.sensor_flags) { + case CPIA2_VP_SENSOR_FLAGS_404: + strcat(vc->card, "404)"); + break; + case CPIA2_VP_SENSOR_FLAGS_407: + strcat(vc->card, "407)"); + break; + case CPIA2_VP_SENSOR_FLAGS_409: + strcat(vc->card, "409)"); + break; + case CPIA2_VP_SENSOR_FLAGS_410: + strcat(vc->card, "410)"); + break; + case CPIA2_VP_SENSOR_FLAGS_500: + strcat(vc->card, "500)"); + break; + default: + strcat(vc->card, "XXX)"); + break; + } + + if (usb_make_path(cam->dev, vc->bus_info, sizeof(vc->bus_info)) < 0) + memset(vc->bus_info, 0, sizeof(vc->bus_info)); + return 0; +} + +/****************************************************************************** + * + * ioctl_input + * + * V4L2 input get/set/enumerate + * + *****************************************************************************/ + +static int cpia2_enum_input(struct file *file, void *fh, struct v4l2_input *i) +{ + if (i->index) + return -EINVAL; + strscpy(i->name, "Camera", sizeof(i->name)); + i->type = V4L2_INPUT_TYPE_CAMERA; + return 0; +} + +static int cpia2_g_input(struct file *file, void *fh, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int cpia2_s_input(struct file *file, void *fh, unsigned int i) +{ + return i ? -EINVAL : 0; +} + +/****************************************************************************** + * + * ioctl_enum_fmt + * + * V4L2 format enumerate + * + *****************************************************************************/ + +static int cpia2_enum_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + if (f->index > 1) + return -EINVAL; + + if (f->index == 0) + f->pixelformat = V4L2_PIX_FMT_MJPEG; + else + f->pixelformat = V4L2_PIX_FMT_JPEG; + return 0; +} + +/****************************************************************************** + * + * ioctl_try_fmt + * + * V4L2 format try + * + *****************************************************************************/ + +static int cpia2_try_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct camera_data *cam = video_drvdata(file); + + if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG && + f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) + return -EINVAL; + + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = cam->frame_size; + f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; + + switch (cpia2_match_video_size(f->fmt.pix.width, f->fmt.pix.height)) { + case VIDEOSIZE_VGA: + f->fmt.pix.width = 640; + f->fmt.pix.height = 480; + break; + case VIDEOSIZE_CIF: + f->fmt.pix.width = 352; + f->fmt.pix.height = 288; + break; + case VIDEOSIZE_QVGA: + f->fmt.pix.width = 320; + f->fmt.pix.height = 240; + break; + case VIDEOSIZE_288_216: + f->fmt.pix.width = 288; + f->fmt.pix.height = 216; + break; + case VIDEOSIZE_256_192: + f->fmt.pix.width = 256; + f->fmt.pix.height = 192; + break; + case VIDEOSIZE_224_168: + f->fmt.pix.width = 224; + f->fmt.pix.height = 168; + break; + case VIDEOSIZE_192_144: + f->fmt.pix.width = 192; + f->fmt.pix.height = 144; + break; + case VIDEOSIZE_QCIF: + default: + f->fmt.pix.width = 176; + f->fmt.pix.height = 144; + break; + } + + return 0; +} + +/****************************************************************************** + * + * ioctl_set_fmt + * + * V4L2 format set + * + *****************************************************************************/ + +static int cpia2_s_fmt_vid_cap(struct file *file, void *_fh, + struct v4l2_format *f) +{ + struct camera_data *cam = video_drvdata(file); + int err, frame; + + err = cpia2_try_fmt_vid_cap(file, _fh, f); + if (err != 0) + return err; + + cam->pixelformat = f->fmt.pix.pixelformat; + + /* NOTE: This should be set to 1 for MJPEG, but some apps don't handle + * the missing Huffman table properly. + */ + cam->params.compression.inhibit_htables = 0; + /*f->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG;*/ + + /* we set the video window to something smaller or equal to what + * is requested by the user??? + */ + DBG("Requested width = %d, height = %d\n", + f->fmt.pix.width, f->fmt.pix.height); + if (f->fmt.pix.width != cam->width || + f->fmt.pix.height != cam->height) { + cam->width = f->fmt.pix.width; + cam->height = f->fmt.pix.height; + cam->params.roi.width = f->fmt.pix.width; + cam->params.roi.height = f->fmt.pix.height; + cpia2_set_format(cam); + } + + for (frame = 0; frame < cam->num_frames; ++frame) { + if (cam->buffers[frame].status == FRAME_READING) + if ((err = sync(cam, frame)) < 0) + return err; + + cam->buffers[frame].status = FRAME_EMPTY; + } + + return 0; +} + +/****************************************************************************** + * + * ioctl_get_fmt + * + * V4L2 format get + * + *****************************************************************************/ + +static int cpia2_g_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct camera_data *cam = video_drvdata(file); + + f->fmt.pix.width = cam->width; + f->fmt.pix.height = cam->height; + f->fmt.pix.pixelformat = cam->pixelformat; + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = cam->frame_size; + f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; + + return 0; +} + +/****************************************************************************** + * + * ioctl_cropcap + * + * V4L2 query cropping capabilities + * NOTE: cropping is currently disabled + * + *****************************************************************************/ + +static int cpia2_g_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct camera_data *cam = video_drvdata(file); + + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + switch (s->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + s->r.left = 0; + s->r.top = 0; + s->r.width = cam->width; + s->r.height = cam->height; + break; + default: + return -EINVAL; + } + return 0; +} + +struct framerate_info { + int value; + struct v4l2_fract period; +}; + +static const struct framerate_info framerate_controls[] = { + { CPIA2_VP_FRAMERATE_6_25, { 4, 25 } }, + { CPIA2_VP_FRAMERATE_7_5, { 2, 15 } }, + { CPIA2_VP_FRAMERATE_12_5, { 2, 25 } }, + { CPIA2_VP_FRAMERATE_15, { 1, 15 } }, + { CPIA2_VP_FRAMERATE_25, { 1, 25 } }, + { CPIA2_VP_FRAMERATE_30, { 1, 30 } }, +}; + +static int cpia2_g_parm(struct file *file, void *fh, struct v4l2_streamparm *p) +{ + struct camera_data *cam = video_drvdata(file); + struct v4l2_captureparm *cap = &p->parm.capture; + int i; + + if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + cap->capability = V4L2_CAP_TIMEPERFRAME; + cap->readbuffers = cam->num_frames; + for (i = 0; i < ARRAY_SIZE(framerate_controls); i++) + if (cam->params.vp_params.frame_rate == framerate_controls[i].value) { + cap->timeperframe = framerate_controls[i].period; + break; + } + return 0; +} + +static int cpia2_s_parm(struct file *file, void *fh, struct v4l2_streamparm *p) +{ + struct camera_data *cam = video_drvdata(file); + struct v4l2_captureparm *cap = &p->parm.capture; + struct v4l2_fract tpf = cap->timeperframe; + int max = ARRAY_SIZE(framerate_controls) - 1; + int ret; + int i; + + ret = cpia2_g_parm(file, fh, p); + if (ret || !tpf.denominator || !tpf.numerator) + return ret; + + /* Maximum 15 fps for this model */ + if (cam->params.pnp_id.device_type == DEVICE_STV_672 && + cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500) + max -= 2; + for (i = 0; i <= max; i++) { + struct v4l2_fract f1 = tpf; + struct v4l2_fract f2 = framerate_controls[i].period; + + f1.numerator *= f2.denominator; + f2.numerator *= f1.denominator; + if (f1.numerator >= f2.numerator) + break; + } + if (i > max) + i = max; + cap->timeperframe = framerate_controls[i].period; + return cpia2_set_fps(cam, framerate_controls[i].value); +} + +static const struct { + u32 width; + u32 height; +} cpia2_framesizes[] = { + { 640, 480 }, + { 352, 288 }, + { 320, 240 }, + { 288, 216 }, + { 256, 192 }, + { 224, 168 }, + { 192, 144 }, + { 176, 144 }, +}; + +static int cpia2_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + if (fsize->pixel_format != V4L2_PIX_FMT_MJPEG && + fsize->pixel_format != V4L2_PIX_FMT_JPEG) + return -EINVAL; + if (fsize->index >= ARRAY_SIZE(cpia2_framesizes)) + return -EINVAL; + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = cpia2_framesizes[fsize->index].width; + fsize->discrete.height = cpia2_framesizes[fsize->index].height; + + return 0; +} + +static int cpia2_enum_frameintervals(struct file *file, void *fh, + struct v4l2_frmivalenum *fival) +{ + struct camera_data *cam = video_drvdata(file); + int max = ARRAY_SIZE(framerate_controls) - 1; + int i; + + if (fival->pixel_format != V4L2_PIX_FMT_MJPEG && + fival->pixel_format != V4L2_PIX_FMT_JPEG) + return -EINVAL; + + /* Maximum 15 fps for this model */ + if (cam->params.pnp_id.device_type == DEVICE_STV_672 && + cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500) + max -= 2; + if (fival->index > max) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(cpia2_framesizes); i++) + if (fival->width == cpia2_framesizes[i].width && + fival->height == cpia2_framesizes[i].height) + break; + if (i == ARRAY_SIZE(cpia2_framesizes)) + return -EINVAL; + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete = framerate_controls[fival->index].period; + return 0; +} + +/****************************************************************************** + * + * ioctl_s_ctrl + * + * V4L2 set the value of a control variable + * + *****************************************************************************/ + +static int cpia2_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct camera_data *cam = + container_of(ctrl->handler, struct camera_data, hdl); + static const int flicker_table[] = { + NEVER_FLICKER, + FLICKER_50, + FLICKER_60, + }; + + DBG("Set control id:%d, value:%d\n", ctrl->id, ctrl->val); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + cpia2_set_brightness(cam, ctrl->val); + break; + case V4L2_CID_CONTRAST: + cpia2_set_contrast(cam, ctrl->val); + break; + case V4L2_CID_SATURATION: + cpia2_set_saturation(cam, ctrl->val); + break; + case V4L2_CID_HFLIP: + cpia2_set_property_mirror(cam, ctrl->val); + break; + case V4L2_CID_VFLIP: + cpia2_set_property_flip(cam, ctrl->val); + break; + case V4L2_CID_POWER_LINE_FREQUENCY: + return cpia2_set_flicker_mode(cam, flicker_table[ctrl->val]); + case V4L2_CID_ILLUMINATORS_1: + return cpia2_set_gpio(cam, (cam->top_light->val << 6) | + (cam->bottom_light->val << 7)); + case V4L2_CID_JPEG_ACTIVE_MARKER: + cam->params.compression.inhibit_htables = + !(ctrl->val & V4L2_JPEG_ACTIVE_MARKER_DHT); + break; + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + cam->params.vc_params.quality = ctrl->val; + break; + case CPIA2_CID_USB_ALT: + cam->params.camera_state.stream_mode = ctrl->val; + break; + default: + return -EINVAL; + } + + return 0; +} + +/****************************************************************************** + * + * ioctl_g_jpegcomp + * + * V4L2 get the JPEG compression parameters + * + *****************************************************************************/ + +static int cpia2_g_jpegcomp(struct file *file, void *fh, struct v4l2_jpegcompression *parms) +{ + struct camera_data *cam = video_drvdata(file); + + memset(parms, 0, sizeof(*parms)); + + parms->quality = 80; // TODO: Can this be made meaningful? + + parms->jpeg_markers = V4L2_JPEG_MARKER_DQT | V4L2_JPEG_MARKER_DRI; + if (!cam->params.compression.inhibit_htables) + parms->jpeg_markers |= V4L2_JPEG_MARKER_DHT; + + parms->APPn = cam->APPn; + parms->APP_len = cam->APP_len; + if (cam->APP_len > 0) { + memcpy(parms->APP_data, cam->APP_data, cam->APP_len); + parms->jpeg_markers |= V4L2_JPEG_MARKER_APP; + } + + parms->COM_len = cam->COM_len; + if (cam->COM_len > 0) { + memcpy(parms->COM_data, cam->COM_data, cam->COM_len); + parms->jpeg_markers |= JPEG_MARKER_COM; + } + + DBG("G_JPEGCOMP APP_len:%d COM_len:%d\n", + parms->APP_len, parms->COM_len); + + return 0; +} + +/****************************************************************************** + * + * ioctl_s_jpegcomp + * + * V4L2 set the JPEG compression parameters + * NOTE: quality and some jpeg_markers are ignored. + * + *****************************************************************************/ + +static int cpia2_s_jpegcomp(struct file *file, void *fh, + const struct v4l2_jpegcompression *parms) +{ + struct camera_data *cam = video_drvdata(file); + + DBG("S_JPEGCOMP APP_len:%d COM_len:%d\n", + parms->APP_len, parms->COM_len); + + cam->params.compression.inhibit_htables = + !(parms->jpeg_markers & V4L2_JPEG_MARKER_DHT); + + if (parms->APP_len != 0) { + if (parms->APP_len > 0 && + parms->APP_len <= sizeof(cam->APP_data) && + parms->APPn >= 0 && parms->APPn <= 15) { + cam->APPn = parms->APPn; + cam->APP_len = parms->APP_len; + memcpy(cam->APP_data, parms->APP_data, parms->APP_len); + } else { + LOG("Bad APPn Params n=%d len=%d\n", + parms->APPn, parms->APP_len); + return -EINVAL; + } + } else { + cam->APP_len = 0; + } + + if (parms->COM_len != 0) { + if (parms->COM_len > 0 && + parms->COM_len <= sizeof(cam->COM_data)) { + cam->COM_len = parms->COM_len; + memcpy(cam->COM_data, parms->COM_data, parms->COM_len); + } else { + LOG("Bad COM_len=%d\n", parms->COM_len); + return -EINVAL; + } + } + + return 0; +} + +/****************************************************************************** + * + * ioctl_reqbufs + * + * V4L2 Initiate memory mapping. + * NOTE: The user's request is ignored. For now the buffers are fixed. + * + *****************************************************************************/ + +static int cpia2_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *req) +{ + struct camera_data *cam = video_drvdata(file); + + if (req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + req->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + DBG("REQBUFS requested:%d returning:%d\n", req->count, cam->num_frames); + req->count = cam->num_frames; + memset(&req->reserved, 0, sizeof(req->reserved)); + + return 0; +} + +/****************************************************************************** + * + * ioctl_querybuf + * + * V4L2 Query memory buffer status. + * + *****************************************************************************/ + +static int cpia2_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) +{ + struct camera_data *cam = video_drvdata(file); + + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + buf->index >= cam->num_frames) + return -EINVAL; + + buf->m.offset = cam->buffers[buf->index].data - cam->frame_buffer; + buf->length = cam->frame_size; + + buf->memory = V4L2_MEMORY_MMAP; + + if (cam->mmapped) + buf->flags = V4L2_BUF_FLAG_MAPPED; + else + buf->flags = 0; + + buf->flags |= V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + + switch (cam->buffers[buf->index].status) { + case FRAME_EMPTY: + case FRAME_ERROR: + case FRAME_READING: + buf->bytesused = 0; + buf->flags = V4L2_BUF_FLAG_QUEUED; + break; + case FRAME_READY: + buf->bytesused = cam->buffers[buf->index].length; + v4l2_buffer_set_timestamp(buf, cam->buffers[buf->index].ts); + buf->sequence = cam->buffers[buf->index].seq; + buf->flags = V4L2_BUF_FLAG_DONE; + break; + } + + DBG("QUERYBUF index:%d offset:%d flags:%d seq:%d bytesused:%d\n", + buf->index, buf->m.offset, buf->flags, buf->sequence, + buf->bytesused); + + return 0; +} + +/****************************************************************************** + * + * ioctl_qbuf + * + * V4L2 User is freeing buffer + * + *****************************************************************************/ + +static int cpia2_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) +{ + struct camera_data *cam = video_drvdata(file); + + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + buf->memory != V4L2_MEMORY_MMAP || + buf->index >= cam->num_frames) + return -EINVAL; + + DBG("QBUF #%d\n", buf->index); + + if (cam->buffers[buf->index].status == FRAME_READY) + cam->buffers[buf->index].status = FRAME_EMPTY; + + return 0; +} + +/****************************************************************************** + * + * find_earliest_filled_buffer + * + * Helper for ioctl_dqbuf. Find the next ready buffer. + * + *****************************************************************************/ + +static int find_earliest_filled_buffer(struct camera_data *cam) +{ + int i; + int found = -1; + + for (i = 0; i < cam->num_frames; i++) { + if (cam->buffers[i].status == FRAME_READY) { + if (found < 0) { + found = i; + } else { + /* find which buffer is earlier */ + if (cam->buffers[i].ts < cam->buffers[found].ts) + found = i; + } + } + } + return found; +} + +/****************************************************************************** + * + * ioctl_dqbuf + * + * V4L2 User is asking for a filled buffer. + * + *****************************************************************************/ + +static int cpia2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) +{ + struct camera_data *cam = video_drvdata(file); + int frame; + + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + buf->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + frame = find_earliest_filled_buffer(cam); + + if (frame < 0 && file->f_flags & O_NONBLOCK) + return -EAGAIN; + + if (frame < 0) { + /* Wait for a frame to become available */ + struct framebuf *cb = cam->curbuff; + + mutex_unlock(&cam->v4l2_lock); + wait_event_interruptible(cam->wq_stream, + !video_is_registered(&cam->vdev) || + (cb = cam->curbuff)->status == FRAME_READY); + mutex_lock(&cam->v4l2_lock); + if (signal_pending(current)) + return -ERESTARTSYS; + if (!video_is_registered(&cam->vdev)) + return -ENOTTY; + frame = cb->num; + } + + buf->index = frame; + buf->bytesused = cam->buffers[buf->index].length; + buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_DONE + | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + buf->field = V4L2_FIELD_NONE; + v4l2_buffer_set_timestamp(buf, cam->buffers[buf->index].ts); + buf->sequence = cam->buffers[buf->index].seq; + buf->m.offset = cam->buffers[buf->index].data - cam->frame_buffer; + buf->length = cam->frame_size; + buf->reserved2 = 0; + buf->request_fd = 0; + memset(&buf->timecode, 0, sizeof(buf->timecode)); + + DBG("DQBUF #%d status:%d seq:%d length:%d\n", buf->index, + cam->buffers[buf->index].status, buf->sequence, buf->bytesused); + + return 0; +} + +static int cpia2_streamon(struct file *file, void *fh, enum v4l2_buf_type type) +{ + struct camera_data *cam = video_drvdata(file); + int ret = -EINVAL; + + DBG("VIDIOC_STREAMON, streaming=%d\n", cam->streaming); + if (!cam->mmapped || type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (!cam->streaming) { + ret = cpia2_usb_stream_start(cam, + cam->params.camera_state.stream_mode); + if (!ret) + v4l2_ctrl_grab(cam->usb_alt, true); + } + return ret; +} + +static int cpia2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) +{ + struct camera_data *cam = video_drvdata(file); + int ret = -EINVAL; + + DBG("VIDIOC_STREAMOFF, streaming=%d\n", cam->streaming); + if (!cam->mmapped || type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (cam->streaming) { + ret = cpia2_usb_stream_stop(cam); + if (!ret) + v4l2_ctrl_grab(cam->usb_alt, false); + } + return ret; +} + +/****************************************************************************** + * + * cpia2_mmap + * + *****************************************************************************/ +static int cpia2_mmap(struct file *file, struct vm_area_struct *area) +{ + struct camera_data *cam = video_drvdata(file); + int retval; + + if (mutex_lock_interruptible(&cam->v4l2_lock)) + return -ERESTARTSYS; + retval = cpia2_remap_buffer(cam, area); + + if (!retval) + cam->stream_fh = file->private_data; + mutex_unlock(&cam->v4l2_lock); + return retval; +} + +/****************************************************************************** + * + * reset_camera_struct_v4l + * + * Sets all values to the defaults + *****************************************************************************/ +static void reset_camera_struct_v4l(struct camera_data *cam) +{ + cam->width = cam->params.roi.width; + cam->height = cam->params.roi.height; + + cam->frame_size = buffer_size; + cam->num_frames = num_buffers; + + /* Flicker modes */ + cam->params.flicker_control.flicker_mode_req = flicker_mode; + + /* stream modes */ + cam->params.camera_state.stream_mode = alternate; + + cam->pixelformat = V4L2_PIX_FMT_JPEG; +} + +static const struct v4l2_ioctl_ops cpia2_ioctl_ops = { + .vidioc_querycap = cpia2_querycap, + .vidioc_enum_input = cpia2_enum_input, + .vidioc_g_input = cpia2_g_input, + .vidioc_s_input = cpia2_s_input, + .vidioc_enum_fmt_vid_cap = cpia2_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cpia2_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = cpia2_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cpia2_try_fmt_vid_cap, + .vidioc_g_jpegcomp = cpia2_g_jpegcomp, + .vidioc_s_jpegcomp = cpia2_s_jpegcomp, + .vidioc_g_selection = cpia2_g_selection, + .vidioc_reqbufs = cpia2_reqbufs, + .vidioc_querybuf = cpia2_querybuf, + .vidioc_qbuf = cpia2_qbuf, + .vidioc_dqbuf = cpia2_dqbuf, + .vidioc_streamon = cpia2_streamon, + .vidioc_streamoff = cpia2_streamoff, + .vidioc_s_parm = cpia2_s_parm, + .vidioc_g_parm = cpia2_g_parm, + .vidioc_enum_framesizes = cpia2_enum_framesizes, + .vidioc_enum_frameintervals = cpia2_enum_frameintervals, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/*** + * The v4l video device structure initialized for this device + ***/ +static const struct v4l2_file_operations cpia2_fops = { + .owner = THIS_MODULE, + .open = cpia2_open, + .release = cpia2_close, + .read = cpia2_v4l_read, + .poll = cpia2_v4l_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = cpia2_mmap, +}; + +static const struct video_device cpia2_template = { + /* I could not find any place for the old .initialize initializer?? */ + .name = "CPiA2 Camera", + .fops = &cpia2_fops, + .ioctl_ops = &cpia2_ioctl_ops, + .release = video_device_release_empty, +}; + +void cpia2_camera_release(struct v4l2_device *v4l2_dev) +{ + struct camera_data *cam = + container_of(v4l2_dev, struct camera_data, v4l2_dev); + + v4l2_ctrl_handler_free(&cam->hdl); + v4l2_device_unregister(&cam->v4l2_dev); + kfree(cam); +} + +static const struct v4l2_ctrl_ops cpia2_ctrl_ops = { + .s_ctrl = cpia2_s_ctrl, +}; + +/****************************************************************************** + * + * cpia2_register_camera + * + *****************************************************************************/ +int cpia2_register_camera(struct camera_data *cam) +{ + struct v4l2_ctrl_handler *hdl = &cam->hdl; + struct v4l2_ctrl_config cpia2_usb_alt = { + .ops = &cpia2_ctrl_ops, + .id = CPIA2_CID_USB_ALT, + .name = "USB Alternate", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = USBIF_ISO_1, + .max = USBIF_ISO_6, + .step = 1, + }; + int ret; + + v4l2_ctrl_handler_init(hdl, 12); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_BRIGHTNESS, + cam->params.pnp_id.device_type == DEVICE_STV_672 ? 1 : 0, + 255, 1, DEFAULT_BRIGHTNESS); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, DEFAULT_CONTRAST); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, DEFAULT_SATURATION); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_JPEG_ACTIVE_MARKER, 0, + V4L2_JPEG_ACTIVE_MARKER_DHT, 0, + V4L2_JPEG_ACTIVE_MARKER_DHT); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, 1, + 100, 1, 100); + cpia2_usb_alt.def = alternate; + cam->usb_alt = v4l2_ctrl_new_custom(hdl, &cpia2_usb_alt, NULL); + /* VP5 Only */ + if (cam->params.pnp_id.device_type != DEVICE_STV_672) + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + /* Flicker control only valid for 672 */ + if (cam->params.pnp_id.device_type == DEVICE_STV_672) + v4l2_ctrl_new_std_menu(hdl, &cpia2_ctrl_ops, + V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_60HZ, + 0, 0); + /* Light control only valid for the QX5 Microscope */ + if (cam->params.pnp_id.product == 0x151) { + cam->top_light = v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_ILLUMINATORS_1, + 0, 1, 1, 0); + cam->bottom_light = v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_ILLUMINATORS_2, + 0, 1, 1, 0); + v4l2_ctrl_cluster(2, &cam->top_light); + } + + if (hdl->error) { + ret = hdl->error; + v4l2_ctrl_handler_free(hdl); + return ret; + } + + cam->vdev = cpia2_template; + video_set_drvdata(&cam->vdev, cam); + cam->vdev.lock = &cam->v4l2_lock; + cam->vdev.ctrl_handler = hdl; + cam->vdev.v4l2_dev = &cam->v4l2_dev; + cam->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + + reset_camera_struct_v4l(cam); + + /* register v4l device */ + if (video_register_device(&cam->vdev, VFL_TYPE_VIDEO, video_nr) < 0) { + ERR("video_register_device failed\n"); + return -ENODEV; + } + + return 0; +} + +/****************************************************************************** + * + * cpia2_unregister_camera + * + *****************************************************************************/ +void cpia2_unregister_camera(struct camera_data *cam) +{ + video_unregister_device(&cam->vdev); +} + +/****************************************************************************** + * + * check_parameters + * + * Make sure that all user-supplied parameters are sensible + *****************************************************************************/ +static void __init check_parameters(void) +{ + if (buffer_size < PAGE_SIZE) { + buffer_size = PAGE_SIZE; + LOG("buffer_size too small, setting to %d\n", buffer_size); + } else if (buffer_size > 1024 * 1024) { + /* arbitrary upper limiit */ + buffer_size = 1024 * 1024; + LOG("buffer_size ridiculously large, setting to %d\n", + buffer_size); + } else { + buffer_size += PAGE_SIZE - 1; + buffer_size &= ~(PAGE_SIZE - 1); + } + + if (num_buffers < 1) { + num_buffers = 1; + LOG("num_buffers too small, setting to %d\n", num_buffers); + } else if (num_buffers > VIDEO_MAX_FRAME) { + num_buffers = VIDEO_MAX_FRAME; + LOG("num_buffers too large, setting to %d\n", num_buffers); + } + + if (alternate < USBIF_ISO_1 || alternate > USBIF_ISO_6) { + alternate = DEFAULT_ALT; + LOG("alternate specified is invalid, using %d\n", alternate); + } + + if (flicker_mode != 0 && flicker_mode != FLICKER_50 && flicker_mode != FLICKER_60) { + flicker_mode = 0; + LOG("Flicker mode specified is invalid, using %d\n", + flicker_mode); + } + + DBG("Using %d buffers, each %d bytes, alternate=%d\n", + num_buffers, buffer_size, alternate); +} + +/************ Module Stuff ***************/ + +/****************************************************************************** + * + * cpia2_init/module_init + * + *****************************************************************************/ +static int __init cpia2_init(void) +{ + LOG("%s v%s\n", + ABOUT, CPIA_VERSION); + check_parameters(); + return cpia2_usb_init(); +} + +/****************************************************************************** + * + * cpia2_exit/module_exit + * + *****************************************************************************/ +static void __exit cpia2_exit(void) +{ + cpia2_usb_cleanup(); + schedule_timeout(2 * HZ); +} + +module_init(cpia2_init); +module_exit(cpia2_exit); diff --git a/drivers/staging/media/deprecated/fsl-viu/Kconfig b/drivers/staging/media/deprecated/fsl-viu/Kconfig new file mode 100644 index 000000000000..399892c69a18 --- /dev/null +++ b/drivers/staging/media/deprecated/fsl-viu/Kconfig @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_VIU + tristate "NXP VIU Video Driver (DEPRECATED)" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV && (PPC_MPC512x || COMPILE_TEST) && I2C + select VIDEOBUF_DMA_CONTIG + help + Support for Freescale VIU video driver. This device captures + video data, or overlays video on DIU frame buffer. + + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + + Say Y here if you want to enable VIU device on MPC5121e Rev2+. + In doubt, say N. diff --git a/drivers/staging/media/deprecated/fsl-viu/Makefile b/drivers/staging/media/deprecated/fsl-viu/Makefile new file mode 100644 index 000000000000..931ec56ad08c --- /dev/null +++ b/drivers/staging/media/deprecated/fsl-viu/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o diff --git a/drivers/staging/media/deprecated/fsl-viu/TODO b/drivers/staging/media/deprecated/fsl-viu/TODO new file mode 100644 index 000000000000..ecb30a429689 --- /dev/null +++ b/drivers/staging/media/deprecated/fsl-viu/TODO @@ -0,0 +1,7 @@ +This is one of the few drivers still not using the vb2 +framework, so this driver is now deprecated with the intent of +removing it altogether by the beginning of 2023. + +In order to keep this driver it has to be converted to vb2. +If someone is interested in doing this work, then contact the +linux-media mailinglist (https://linuxtv.org/lists.php). diff --git a/drivers/staging/media/deprecated/fsl-viu/fsl-viu.c b/drivers/staging/media/deprecated/fsl-viu/fsl-viu.c new file mode 100644 index 000000000000..afc96f6db2a1 --- /dev/null +++ b/drivers/staging/media/deprecated/fsl-viu/fsl-viu.c @@ -0,0 +1,1599 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved. + * + * Freescale VIU video driver + * + * Authors: Hongjun Chen <hong-jun.chen@freescale.com> + * Porting to 2.6.35 by DENX Software Engineering, + * Anatolij Gustschin <agust@denx.de> + */ + +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/kernel.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/slab.h> +#include <media/v4l2-common.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-event.h> +#include <media/videobuf-dma-contig.h> + +#define DRV_NAME "fsl_viu" +#define VIU_VERSION "0.5.1" + +#define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */ + +#define VIU_VID_MEM_LIMIT 4 /* Video memory limit, in Mb */ + +/* I2C address of video decoder chip is 0x4A */ +#define VIU_VIDEO_DECODER_ADDR 0x25 + +static int info_level; + +#define dprintk(level, fmt, arg...) \ + do { \ + if (level <= info_level) \ + printk(KERN_DEBUG "viu: " fmt , ## arg); \ + } while (0) + +/* + * Basic structures + */ +struct viu_fmt { + u32 fourcc; /* v4l2 format id */ + u32 pixelformat; + int depth; +}; + +static struct viu_fmt formats[] = { + { + .fourcc = V4L2_PIX_FMT_RGB565, + .pixelformat = V4L2_PIX_FMT_RGB565, + .depth = 16, + }, { + .fourcc = V4L2_PIX_FMT_RGB32, + .pixelformat = V4L2_PIX_FMT_RGB32, + .depth = 32, + } +}; + +struct viu_dev; +struct viu_buf; + +/* buffer for one video frame */ +struct viu_buf { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + struct viu_fmt *fmt; +}; + +struct viu_dmaqueue { + struct viu_dev *dev; + struct list_head active; + struct list_head queued; + struct timer_list timeout; +}; + +struct viu_status { + u32 field_irq; + u32 vsync_irq; + u32 hsync_irq; + u32 vstart_irq; + u32 dma_end_irq; + u32 error_irq; +}; + +struct viu_reg { + u32 status_cfg; + u32 luminance; + u32 chroma_r; + u32 chroma_g; + u32 chroma_b; + u32 field_base_addr; + u32 dma_inc; + u32 picture_count; + u32 req_alarm; + u32 alpha; +} __attribute__ ((packed)); + +struct viu_dev { + struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler hdl; + struct mutex lock; + spinlock_t slock; + int users; + + struct device *dev; + /* various device info */ + struct video_device *vdev; + struct viu_dmaqueue vidq; + enum v4l2_field capfield; + int field; + int first; + int dma_done; + + /* Hardware register area */ + struct viu_reg __iomem *vr; + + /* Interrupt vector */ + int irq; + struct viu_status irqs; + + /* video overlay */ + struct v4l2_framebuffer ovbuf; + struct viu_fmt *ovfmt; + unsigned int ovenable; + enum v4l2_field ovfield; + + /* crop */ + struct v4l2_rect crop_current; + + /* clock pointer */ + struct clk *clk; + + /* decoder */ + struct v4l2_subdev *decoder; + + v4l2_std_id std; +}; + +struct viu_fh { + /* must remain the first field of this struct */ + struct v4l2_fh fh; + struct viu_dev *dev; + + /* video capture */ + struct videobuf_queue vb_vidq; + spinlock_t vbq_lock; /* spinlock for the videobuf queue */ + + /* video overlay */ + struct v4l2_window win; + struct v4l2_clip clips[1]; + + /* video capture */ + struct viu_fmt *fmt; + int width, height, sizeimage; + enum v4l2_buf_type type; +}; + +static struct viu_reg reg_val; + +/* + * Macro definitions of VIU registers + */ + +/* STATUS_CONFIG register */ +enum status_config { + SOFT_RST = 1 << 0, + + ERR_MASK = 0x0f << 4, /* Error code mask */ + ERR_NO = 0x00, /* No error */ + ERR_DMA_V = 0x01 << 4, /* DMA in vertical active */ + ERR_DMA_VB = 0x02 << 4, /* DMA in vertical blanking */ + ERR_LINE_TOO_LONG = 0x04 << 4, /* Line too long */ + ERR_TOO_MANG_LINES = 0x05 << 4, /* Too many lines in field */ + ERR_LINE_TOO_SHORT = 0x06 << 4, /* Line too short */ + ERR_NOT_ENOUGH_LINE = 0x07 << 4, /* Not enough lines in field */ + ERR_FIFO_OVERFLOW = 0x08 << 4, /* FIFO overflow */ + ERR_FIFO_UNDERFLOW = 0x09 << 4, /* FIFO underflow */ + ERR_1bit_ECC = 0x0a << 4, /* One bit ECC error */ + ERR_MORE_ECC = 0x0b << 4, /* Two/more bits ECC error */ + + INT_FIELD_EN = 0x01 << 8, /* Enable field interrupt */ + INT_VSYNC_EN = 0x01 << 9, /* Enable vsync interrupt */ + INT_HSYNC_EN = 0x01 << 10, /* Enable hsync interrupt */ + INT_VSTART_EN = 0x01 << 11, /* Enable vstart interrupt */ + INT_DMA_END_EN = 0x01 << 12, /* Enable DMA end interrupt */ + INT_ERROR_EN = 0x01 << 13, /* Enable error interrupt */ + INT_ECC_EN = 0x01 << 14, /* Enable ECC interrupt */ + + INT_FIELD_STATUS = 0x01 << 16, /* field interrupt status */ + INT_VSYNC_STATUS = 0x01 << 17, /* vsync interrupt status */ + INT_HSYNC_STATUS = 0x01 << 18, /* hsync interrupt status */ + INT_VSTART_STATUS = 0x01 << 19, /* vstart interrupt status */ + INT_DMA_END_STATUS = 0x01 << 20, /* DMA end interrupt status */ + INT_ERROR_STATUS = 0x01 << 21, /* error interrupt status */ + + DMA_ACT = 0x01 << 27, /* Enable DMA transfer */ + FIELD_NO = 0x01 << 28, /* Field number */ + DITHER_ON = 0x01 << 29, /* Dithering is on */ + ROUND_ON = 0x01 << 30, /* Round is on */ + MODE_32BIT = 1UL << 31, /* Data in RGBa888, + * 0 in RGB565 + */ +}; + +#define norm_maxw() 720 +#define norm_maxh() 576 + +#define INT_ALL_STATUS (INT_FIELD_STATUS | INT_VSYNC_STATUS | \ + INT_HSYNC_STATUS | INT_VSTART_STATUS | \ + INT_DMA_END_STATUS | INT_ERROR_STATUS) + +#define NUM_FORMATS ARRAY_SIZE(formats) + +static irqreturn_t viu_intr(int irq, void *dev_id); + +static struct viu_fmt *format_by_fourcc(int fourcc) +{ + int i; + + for (i = 0; i < NUM_FORMATS; i++) { + if (formats[i].pixelformat == fourcc) + return formats + i; + } + + dprintk(0, "unknown pixelformat:'%4.4s'\n", (char *)&fourcc); + return NULL; +} + +static void viu_start_dma(struct viu_dev *dev) +{ + struct viu_reg __iomem *vr = dev->vr; + + dev->field = 0; + + /* Enable DMA operation */ + iowrite32be(SOFT_RST, &vr->status_cfg); + iowrite32be(INT_FIELD_EN, &vr->status_cfg); +} + +static void viu_stop_dma(struct viu_dev *dev) +{ + struct viu_reg __iomem *vr = dev->vr; + int cnt = 100; + u32 status_cfg; + + iowrite32be(0, &vr->status_cfg); + + /* Clear pending interrupts */ + status_cfg = ioread32be(&vr->status_cfg); + if (status_cfg & 0x3f0000) + iowrite32be(status_cfg & 0x3f0000, &vr->status_cfg); + + if (status_cfg & DMA_ACT) { + do { + status_cfg = ioread32be(&vr->status_cfg); + if (status_cfg & INT_DMA_END_STATUS) + break; + } while (cnt--); + + if (cnt < 0) { + /* timed out, issue soft reset */ + iowrite32be(SOFT_RST, &vr->status_cfg); + iowrite32be(0, &vr->status_cfg); + } else { + /* clear DMA_END and other pending irqs */ + iowrite32be(status_cfg & 0x3f0000, &vr->status_cfg); + } + } + + dev->field = 0; +} + +static int restart_video_queue(struct viu_dmaqueue *vidq) +{ + struct viu_buf *buf, *prev; + + dprintk(1, "%s vidq=%p\n", __func__, vidq); + if (!list_empty(&vidq->active)) { + buf = list_entry(vidq->active.next, struct viu_buf, vb.queue); + dprintk(2, "restart_queue [%p/%d]: restart dma\n", + buf, buf->vb.i); + + viu_stop_dma(vidq->dev); + + /* cancel all outstanding capture requests */ + list_for_each_entry_safe(buf, prev, &vidq->active, vb.queue) { + list_del(&buf->vb.queue); + buf->vb.state = VIDEOBUF_ERROR; + wake_up(&buf->vb.done); + } + mod_timer(&vidq->timeout, jiffies+BUFFER_TIMEOUT); + return 0; + } + + prev = NULL; + for (;;) { + if (list_empty(&vidq->queued)) + return 0; + buf = list_entry(vidq->queued.next, struct viu_buf, vb.queue); + if (prev == NULL) { + list_move_tail(&buf->vb.queue, &vidq->active); + + dprintk(1, "Restarting video dma\n"); + viu_stop_dma(vidq->dev); + viu_start_dma(vidq->dev); + + buf->vb.state = VIDEOBUF_ACTIVE; + mod_timer(&vidq->timeout, jiffies+BUFFER_TIMEOUT); + dprintk(2, "[%p/%d] restart_queue - first active\n", + buf, buf->vb.i); + + } else if (prev->vb.width == buf->vb.width && + prev->vb.height == buf->vb.height && + prev->fmt == buf->fmt) { + list_move_tail(&buf->vb.queue, &vidq->active); + buf->vb.state = VIDEOBUF_ACTIVE; + dprintk(2, "[%p/%d] restart_queue - move to active\n", + buf, buf->vb.i); + } else { + return 0; + } + prev = buf; + } +} + +static void viu_vid_timeout(struct timer_list *t) +{ + struct viu_dev *dev = from_timer(dev, t, vidq.timeout); + struct viu_buf *buf; + struct viu_dmaqueue *vidq = &dev->vidq; + + while (!list_empty(&vidq->active)) { + buf = list_entry(vidq->active.next, struct viu_buf, vb.queue); + list_del(&buf->vb.queue); + buf->vb.state = VIDEOBUF_ERROR; + wake_up(&buf->vb.done); + dprintk(1, "viu/0: [%p/%d] timeout\n", buf, buf->vb.i); + } + + restart_video_queue(vidq); +} + +/* + * Videobuf operations + */ +static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct viu_fh *fh = vq->priv_data; + + *size = fh->width * fh->height * fh->fmt->depth >> 3; + if (*count == 0) + *count = 32; + + while (*size * *count > VIU_VID_MEM_LIMIT * 1024 * 1024) + (*count)--; + + dprintk(1, "%s, count=%d, size=%d\n", __func__, *count, *size); + return 0; +} + +static void free_buffer(struct videobuf_queue *vq, struct viu_buf *buf) +{ + struct videobuf_buffer *vb = &buf->vb; + void *vaddr = NULL; + + videobuf_waiton(vq, &buf->vb, 0, 0); + + if (vq->int_ops && vq->int_ops->vaddr) + vaddr = vq->int_ops->vaddr(vb); + + if (vaddr) + videobuf_dma_contig_free(vq, &buf->vb); + + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +inline int buffer_activate(struct viu_dev *dev, struct viu_buf *buf) +{ + struct viu_reg __iomem *vr = dev->vr; + int bpp; + + /* setup the DMA base address */ + reg_val.field_base_addr = videobuf_to_dma_contig(&buf->vb); + + dprintk(1, "buffer_activate [%p/%d]: dma addr 0x%lx\n", + buf, buf->vb.i, (unsigned long)reg_val.field_base_addr); + + /* interlace is on by default, set horizontal DMA increment */ + reg_val.status_cfg = 0; + bpp = buf->fmt->depth >> 3; + switch (bpp) { + case 2: + reg_val.status_cfg &= ~MODE_32BIT; + reg_val.dma_inc = buf->vb.width * 2; + break; + case 4: + reg_val.status_cfg |= MODE_32BIT; + reg_val.dma_inc = buf->vb.width * 4; + break; + default: + dprintk(0, "doesn't support color depth(%d)\n", + bpp * 8); + return -EINVAL; + } + + /* setup picture_count register */ + reg_val.picture_count = (buf->vb.height / 2) << 16 | + buf->vb.width; + + reg_val.status_cfg |= DMA_ACT | INT_DMA_END_EN | INT_FIELD_EN; + + buf->vb.state = VIDEOBUF_ACTIVE; + dev->capfield = buf->vb.field; + + /* reset dma increment if needed */ + if (!V4L2_FIELD_HAS_BOTH(buf->vb.field)) + reg_val.dma_inc = 0; + + iowrite32be(reg_val.dma_inc, &vr->dma_inc); + iowrite32be(reg_val.picture_count, &vr->picture_count); + iowrite32be(reg_val.field_base_addr, &vr->field_base_addr); + mod_timer(&dev->vidq.timeout, jiffies + BUFFER_TIMEOUT); + return 0; +} + +static int buffer_prepare(struct videobuf_queue *vq, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct viu_fh *fh = vq->priv_data; + struct viu_buf *buf = container_of(vb, struct viu_buf, vb); + int rc; + + BUG_ON(fh->fmt == NULL); + + if (fh->width < 48 || fh->width > norm_maxw() || + fh->height < 32 || fh->height > norm_maxh()) + return -EINVAL; + buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3; + if (buf->vb.baddr != 0 && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + if (buf->fmt != fh->fmt || + buf->vb.width != fh->width || + buf->vb.height != fh->height || + buf->vb.field != field) { + buf->fmt = fh->fmt; + buf->vb.width = fh->width; + buf->vb.height = fh->height; + buf->vb.field = field; + } + + if (buf->vb.state == VIDEOBUF_NEEDS_INIT) { + rc = videobuf_iolock(vq, &buf->vb, NULL); + if (rc != 0) + goto fail; + + buf->vb.width = fh->width; + buf->vb.height = fh->height; + buf->vb.field = field; + buf->fmt = fh->fmt; + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; + +fail: + free_buffer(vq, buf); + return rc; +} + +static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct viu_buf *buf = container_of(vb, struct viu_buf, vb); + struct viu_fh *fh = vq->priv_data; + struct viu_dev *dev = fh->dev; + struct viu_dmaqueue *vidq = &dev->vidq; + struct viu_buf *prev; + + if (!list_empty(&vidq->queued)) { + dprintk(1, "adding vb queue=%p\n", &buf->vb.queue); + dprintk(1, "vidq pointer 0x%p, queued 0x%p\n", + vidq, &vidq->queued); + dprintk(1, "dev %p, queued: self %p, next %p, head %p\n", + dev, &vidq->queued, vidq->queued.next, + vidq->queued.prev); + list_add_tail(&buf->vb.queue, &vidq->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - append to queued\n", + buf, buf->vb.i); + } else if (list_empty(&vidq->active)) { + dprintk(1, "adding vb active=%p\n", &buf->vb.queue); + list_add_tail(&buf->vb.queue, &vidq->active); + buf->vb.state = VIDEOBUF_ACTIVE; + mod_timer(&vidq->timeout, jiffies+BUFFER_TIMEOUT); + dprintk(2, "[%p/%d] buffer_queue - first active\n", + buf, buf->vb.i); + + buffer_activate(dev, buf); + } else { + dprintk(1, "adding vb queue2=%p\n", &buf->vb.queue); + prev = list_entry(vidq->active.prev, struct viu_buf, vb.queue); + if (prev->vb.width == buf->vb.width && + prev->vb.height == buf->vb.height && + prev->fmt == buf->fmt) { + list_add_tail(&buf->vb.queue, &vidq->active); + buf->vb.state = VIDEOBUF_ACTIVE; + dprintk(2, "[%p/%d] buffer_queue - append to active\n", + buf, buf->vb.i); + } else { + list_add_tail(&buf->vb.queue, &vidq->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - first queued\n", + buf, buf->vb.i); + } + } +} + +static void buffer_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct viu_buf *buf = container_of(vb, struct viu_buf, vb); + struct viu_fh *fh = vq->priv_data; + struct viu_dev *dev = (struct viu_dev *)fh->dev; + + viu_stop_dma(dev); + free_buffer(vq, buf); +} + +static const struct videobuf_queue_ops viu_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +/* + * IOCTL vidioc handling + */ +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, "viu", sizeof(cap->driver)); + strscpy(cap->card, "viu", sizeof(cap->card)); + strscpy(cap->bus_info, "platform:viu", sizeof(cap->bus_info)); + return 0; +} + +static int vidioc_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + int index = f->index; + + if (f->index >= NUM_FORMATS) + return -EINVAL; + + f->pixelformat = formats[index].fourcc; + return 0; +} + +static int vidioc_g_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct viu_fh *fh = priv; + + f->fmt.pix.width = fh->width; + f->fmt.pix.height = fh->height; + f->fmt.pix.field = fh->vb_vidq.field; + f->fmt.pix.pixelformat = fh->fmt->pixelformat; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fh->fmt->depth) >> 3; + f->fmt.pix.sizeimage = fh->sizeimage; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + return 0; +} + +static int vidioc_try_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct viu_fmt *fmt; + unsigned int maxw, maxh; + + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + if (!fmt) { + dprintk(1, "Fourcc format (0x%08x) invalid.", + f->fmt.pix.pixelformat); + return -EINVAL; + } + + maxw = norm_maxw(); + maxh = norm_maxh(); + + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + if (f->fmt.pix.height < 32) + f->fmt.pix.height = 32; + if (f->fmt.pix.height > maxh) + f->fmt.pix.height = maxh; + if (f->fmt.pix.width < 48) + f->fmt.pix.width = 48; + if (f->fmt.pix.width > maxw) + f->fmt.pix.width = maxw; + f->fmt.pix.width &= ~0x03; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fmt->depth) >> 3; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + + return 0; +} + +static int vidioc_s_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct viu_fh *fh = priv; + int ret; + + ret = vidioc_try_fmt_cap(file, fh, f); + if (ret < 0) + return ret; + + fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); + fh->width = f->fmt.pix.width; + fh->height = f->fmt.pix.height; + fh->sizeimage = f->fmt.pix.sizeimage; + fh->vb_vidq.field = f->fmt.pix.field; + fh->type = f->type; + return 0; +} + +static int vidioc_g_fmt_overlay(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct viu_fh *fh = priv; + + f->fmt.win = fh->win; + return 0; +} + +static int verify_preview(struct viu_dev *dev, struct v4l2_window *win) +{ + enum v4l2_field field; + int maxw, maxh; + + if (dev->ovbuf.base == NULL) + return -EINVAL; + if (dev->ovfmt == NULL) + return -EINVAL; + if (win->w.width < 48 || win->w.height < 32) + return -EINVAL; + + field = win->field; + maxw = dev->crop_current.width; + maxh = dev->crop_current.height; + + if (field == V4L2_FIELD_ANY) { + field = (win->w.height > maxh/2) + ? V4L2_FIELD_INTERLACED + : V4L2_FIELD_TOP; + } + switch (field) { + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + maxh = maxh / 2; + break; + case V4L2_FIELD_INTERLACED: + break; + default: + return -EINVAL; + } + + win->field = field; + if (win->w.width > maxw) + win->w.width = maxw; + if (win->w.height > maxh) + win->w.height = maxh; + return 0; +} + +inline void viu_activate_overlay(struct viu_reg __iomem *vr) +{ + iowrite32be(reg_val.field_base_addr, &vr->field_base_addr); + iowrite32be(reg_val.dma_inc, &vr->dma_inc); + iowrite32be(reg_val.picture_count, &vr->picture_count); +} + +static int viu_setup_preview(struct viu_dev *dev, struct viu_fh *fh) +{ + int bpp; + + dprintk(1, "%s %dx%d\n", __func__, + fh->win.w.width, fh->win.w.height); + + reg_val.status_cfg = 0; + + /* setup window */ + reg_val.picture_count = (fh->win.w.height / 2) << 16 | + fh->win.w.width; + + /* setup color depth and dma increment */ + bpp = dev->ovfmt->depth / 8; + switch (bpp) { + case 2: + reg_val.status_cfg &= ~MODE_32BIT; + reg_val.dma_inc = fh->win.w.width * 2; + break; + case 4: + reg_val.status_cfg |= MODE_32BIT; + reg_val.dma_inc = fh->win.w.width * 4; + break; + default: + dprintk(0, "device doesn't support color depth(%d)\n", + bpp * 8); + return -EINVAL; + } + + dev->ovfield = fh->win.field; + if (!V4L2_FIELD_HAS_BOTH(dev->ovfield)) + reg_val.dma_inc = 0; + + reg_val.status_cfg |= DMA_ACT | INT_DMA_END_EN | INT_FIELD_EN; + + /* setup the base address of the overlay buffer */ + reg_val.field_base_addr = (u32)(long)dev->ovbuf.base; + + return 0; +} + +static int vidioc_s_fmt_overlay(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct viu_fh *fh = priv; + struct viu_dev *dev = (struct viu_dev *)fh->dev; + unsigned long flags; + int err; + + err = verify_preview(dev, &f->fmt.win); + if (err) + return err; + + fh->win = f->fmt.win; + + spin_lock_irqsave(&dev->slock, flags); + viu_setup_preview(dev, fh); + spin_unlock_irqrestore(&dev->slock, flags); + return 0; +} + +static int vidioc_try_fmt_overlay(struct file *file, void *priv, + struct v4l2_format *f) +{ + return 0; +} + +static int vidioc_overlay(struct file *file, void *priv, unsigned int on) +{ + struct viu_fh *fh = priv; + struct viu_dev *dev = (struct viu_dev *)fh->dev; + unsigned long flags; + + if (on) { + spin_lock_irqsave(&dev->slock, flags); + viu_activate_overlay(dev->vr); + dev->ovenable = 1; + + /* start dma */ + viu_start_dma(dev); + spin_unlock_irqrestore(&dev->slock, flags); + } else { + viu_stop_dma(dev); + dev->ovenable = 0; + } + + return 0; +} + +static int vidioc_g_fbuf(struct file *file, void *priv, struct v4l2_framebuffer *arg) +{ + struct viu_fh *fh = priv; + struct viu_dev *dev = fh->dev; + struct v4l2_framebuffer *fb = arg; + + *fb = dev->ovbuf; + fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; + return 0; +} + +static int vidioc_s_fbuf(struct file *file, void *priv, const struct v4l2_framebuffer *arg) +{ + struct viu_fh *fh = priv; + struct viu_dev *dev = fh->dev; + const struct v4l2_framebuffer *fb = arg; + struct viu_fmt *fmt; + + if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO)) + return -EPERM; + + /* check args */ + fmt = format_by_fourcc(fb->fmt.pixelformat); + if (fmt == NULL) + return -EINVAL; + + /* ok, accept it */ + dev->ovbuf = *fb; + dev->ovfmt = fmt; + if (dev->ovbuf.fmt.bytesperline == 0) { + dev->ovbuf.fmt.bytesperline = + dev->ovbuf.fmt.width * fmt->depth / 8; + } + return 0; +} + +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + struct viu_fh *fh = priv; + + return videobuf_reqbufs(&fh->vb_vidq, p); +} + +static int vidioc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct viu_fh *fh = priv; + + return videobuf_querybuf(&fh->vb_vidq, p); +} + +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct viu_fh *fh = priv; + + return videobuf_qbuf(&fh->vb_vidq, p); +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct viu_fh *fh = priv; + + return videobuf_dqbuf(&fh->vb_vidq, p, + file->f_flags & O_NONBLOCK); +} + +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct viu_fh *fh = priv; + struct viu_dev *dev = fh->dev; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (fh->type != i) + return -EINVAL; + + if (dev->ovenable) + dev->ovenable = 0; + + viu_start_dma(fh->dev); + + return videobuf_streamon(&fh->vb_vidq); +} + +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct viu_fh *fh = priv; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (fh->type != i) + return -EINVAL; + + viu_stop_dma(fh->dev); + + return videobuf_streamoff(&fh->vb_vidq); +} + +#define decoder_call(viu, o, f, args...) \ + v4l2_subdev_call(viu->decoder, o, f, ##args) + +static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct viu_fh *fh = priv; + + decoder_call(fh->dev, video, querystd, std_id); + return 0; +} + +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id id) +{ + struct viu_fh *fh = priv; + + fh->dev->std = id; + decoder_call(fh->dev, video, s_std, id); + return 0; +} + +static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct viu_fh *fh = priv; + + *std_id = fh->dev->std; + return 0; +} + +/* only one input in this driver */ +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + struct viu_fh *fh = priv; + + if (inp->index != 0) + return -EINVAL; + + inp->type = V4L2_INPUT_TYPE_CAMERA; + inp->std = fh->dev->vdev->tvnorms; + strscpy(inp->name, "Camera", sizeof(inp->name)); + return 0; +} + +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + struct viu_fh *fh = priv; + + if (i) + return -EINVAL; + + decoder_call(fh->dev, video, s_routing, i, 0, 0); + return 0; +} + +inline void viu_activate_next_buf(struct viu_dev *dev, + struct viu_dmaqueue *viuq) +{ + struct viu_dmaqueue *vidq = viuq; + struct viu_buf *buf; + + /* launch another DMA operation for an active/queued buffer */ + if (!list_empty(&vidq->active)) { + buf = list_entry(vidq->active.next, struct viu_buf, + vb.queue); + dprintk(1, "start another queued buffer: 0x%p\n", buf); + buffer_activate(dev, buf); + } else if (!list_empty(&vidq->queued)) { + buf = list_entry(vidq->queued.next, struct viu_buf, + vb.queue); + list_del(&buf->vb.queue); + + dprintk(1, "start another queued buffer: 0x%p\n", buf); + list_add_tail(&buf->vb.queue, &vidq->active); + buf->vb.state = VIDEOBUF_ACTIVE; + buffer_activate(dev, buf); + } +} + +inline void viu_default_settings(struct viu_reg __iomem *vr) +{ + iowrite32be(0x9512A254, &vr->luminance); + iowrite32be(0x03310000, &vr->chroma_r); + iowrite32be(0x06600F38, &vr->chroma_g); + iowrite32be(0x00000409, &vr->chroma_b); + iowrite32be(0x000000ff, &vr->alpha); + iowrite32be(0x00000090, &vr->req_alarm); + dprintk(1, "status reg: 0x%08x, field base: 0x%08x\n", + ioread32be(&vr->status_cfg), ioread32be(&vr->field_base_addr)); +} + +static void viu_overlay_intr(struct viu_dev *dev, u32 status) +{ + struct viu_reg __iomem *vr = dev->vr; + + if (status & INT_DMA_END_STATUS) + dev->dma_done = 1; + + if (status & INT_FIELD_STATUS) { + if (dev->dma_done) { + u32 addr = reg_val.field_base_addr; + + dev->dma_done = 0; + if (status & FIELD_NO) + addr += reg_val.dma_inc; + + iowrite32be(addr, &vr->field_base_addr); + iowrite32be(reg_val.dma_inc, &vr->dma_inc); + iowrite32be((status & 0xffc0ffff) | + (status & INT_ALL_STATUS) | + reg_val.status_cfg, &vr->status_cfg); + } else if (status & INT_VSYNC_STATUS) { + iowrite32be((status & 0xffc0ffff) | + (status & INT_ALL_STATUS) | + reg_val.status_cfg, &vr->status_cfg); + } + } +} + +static void viu_capture_intr(struct viu_dev *dev, u32 status) +{ + struct viu_dmaqueue *vidq = &dev->vidq; + struct viu_reg __iomem *vr = dev->vr; + struct viu_buf *buf; + int field_num; + int need_two; + int dma_done = 0; + + field_num = status & FIELD_NO; + need_two = V4L2_FIELD_HAS_BOTH(dev->capfield); + + if (status & INT_DMA_END_STATUS) { + dma_done = 1; + if (((field_num == 0) && (dev->field == 0)) || + (field_num && (dev->field == 1))) + dev->field++; + } + + if (status & INT_FIELD_STATUS) { + dprintk(1, "irq: field %d, done %d\n", + !!field_num, dma_done); + if (unlikely(dev->first)) { + if (field_num == 0) { + dev->first = 0; + dprintk(1, "activate first buf\n"); + viu_activate_next_buf(dev, vidq); + } else + dprintk(1, "wait field 0\n"); + return; + } + + /* setup buffer address for next dma operation */ + if (!list_empty(&vidq->active)) { + u32 addr = reg_val.field_base_addr; + + if (field_num && need_two) { + addr += reg_val.dma_inc; + dprintk(1, "field 1, 0x%lx, dev field %d\n", + (unsigned long)addr, dev->field); + } + iowrite32be(addr, &vr->field_base_addr); + iowrite32be(reg_val.dma_inc, &vr->dma_inc); + iowrite32be((status & 0xffc0ffff) | + (status & INT_ALL_STATUS) | + reg_val.status_cfg, &vr->status_cfg); + return; + } + } + + if (dma_done && field_num && (dev->field == 2)) { + dev->field = 0; + buf = list_entry(vidq->active.next, + struct viu_buf, vb.queue); + dprintk(1, "viu/0: [%p/%d] 0x%lx/0x%lx: dma complete\n", + buf, buf->vb.i, + (unsigned long)videobuf_to_dma_contig(&buf->vb), + (unsigned long)ioread32be(&vr->field_base_addr)); + + if (waitqueue_active(&buf->vb.done)) { + list_del(&buf->vb.queue); + buf->vb.ts = ktime_get_ns(); + buf->vb.state = VIDEOBUF_DONE; + buf->vb.field_count++; + wake_up(&buf->vb.done); + } + /* activate next dma buffer */ + viu_activate_next_buf(dev, vidq); + } +} + +static irqreturn_t viu_intr(int irq, void *dev_id) +{ + struct viu_dev *dev = (struct viu_dev *)dev_id; + struct viu_reg __iomem *vr = dev->vr; + u32 status; + u32 error; + + status = ioread32be(&vr->status_cfg); + + if (status & INT_ERROR_STATUS) { + dev->irqs.error_irq++; + error = status & ERR_MASK; + if (error) + dprintk(1, "Err: error(%d), times:%d!\n", + error >> 4, dev->irqs.error_irq); + /* Clear interrupt error bit and error flags */ + iowrite32be((status & 0xffc0ffff) | INT_ERROR_STATUS, + &vr->status_cfg); + } + + if (status & INT_DMA_END_STATUS) { + dev->irqs.dma_end_irq++; + dev->dma_done = 1; + dprintk(2, "VIU DMA end interrupt times: %d\n", + dev->irqs.dma_end_irq); + } + + if (status & INT_HSYNC_STATUS) + dev->irqs.hsync_irq++; + + if (status & INT_FIELD_STATUS) { + dev->irqs.field_irq++; + dprintk(2, "VIU field interrupt times: %d\n", + dev->irqs.field_irq); + } + + if (status & INT_VSTART_STATUS) + dev->irqs.vstart_irq++; + + if (status & INT_VSYNC_STATUS) { + dev->irqs.vsync_irq++; + dprintk(2, "VIU vsync interrupt times: %d\n", + dev->irqs.vsync_irq); + } + + /* clear all pending irqs */ + status = ioread32be(&vr->status_cfg); + iowrite32be((status & 0xffc0ffff) | (status & INT_ALL_STATUS), + &vr->status_cfg); + + if (dev->ovenable) { + viu_overlay_intr(dev, status); + return IRQ_HANDLED; + } + + /* Capture mode */ + viu_capture_intr(dev, status); + return IRQ_HANDLED; +} + +/* + * File operations for the device + */ +static int viu_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct viu_dev *dev = video_get_drvdata(vdev); + struct viu_fh *fh; + struct viu_reg __iomem *vr; + int minor = vdev->minor; + u32 status_cfg; + + dprintk(1, "viu: open (minor=%d)\n", minor); + + dev->users++; + if (dev->users > 1) { + dev->users--; + return -EBUSY; + } + + vr = dev->vr; + + dprintk(1, "open minor=%d type=%s users=%d\n", minor, + v4l2_type_names[V4L2_BUF_TYPE_VIDEO_CAPTURE], dev->users); + + if (mutex_lock_interruptible(&dev->lock)) { + dev->users--; + return -ERESTARTSYS; + } + + /* allocate and initialize per filehandle data */ + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (!fh) { + dev->users--; + mutex_unlock(&dev->lock); + return -ENOMEM; + } + + v4l2_fh_init(&fh->fh, vdev); + file->private_data = fh; + fh->dev = dev; + + fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fh->fmt = format_by_fourcc(V4L2_PIX_FMT_RGB32); + fh->width = norm_maxw(); + fh->height = norm_maxh(); + dev->crop_current.width = fh->width; + dev->crop_current.height = fh->height; + + dprintk(1, "Open: fh=%p, dev=%p, dev->vidq=%p\n", fh, dev, &dev->vidq); + dprintk(1, "Open: list_empty queued=%d\n", + list_empty(&dev->vidq.queued)); + dprintk(1, "Open: list_empty active=%d\n", + list_empty(&dev->vidq.active)); + + viu_default_settings(vr); + + status_cfg = ioread32be(&vr->status_cfg); + iowrite32be(status_cfg & ~(INT_VSYNC_EN | INT_HSYNC_EN | + INT_FIELD_EN | INT_VSTART_EN | + INT_DMA_END_EN | INT_ERROR_EN | INT_ECC_EN), + &vr->status_cfg); + + status_cfg = ioread32be(&vr->status_cfg); + iowrite32be(status_cfg | INT_ALL_STATUS, &vr->status_cfg); + + spin_lock_init(&fh->vbq_lock); + videobuf_queue_dma_contig_init(&fh->vb_vidq, &viu_video_qops, + dev->dev, &fh->vbq_lock, + fh->type, V4L2_FIELD_INTERLACED, + sizeof(struct viu_buf), fh, + &fh->dev->lock); + v4l2_fh_add(&fh->fh); + mutex_unlock(&dev->lock); + return 0; +} + +static ssize_t viu_read(struct file *file, char __user *data, size_t count, + loff_t *ppos) +{ + struct viu_fh *fh = file->private_data; + struct viu_dev *dev = fh->dev; + int ret = 0; + + dprintk(2, "%s\n", __func__); + if (dev->ovenable) + dev->ovenable = 0; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (mutex_lock_interruptible(&dev->lock)) + return -ERESTARTSYS; + viu_start_dma(dev); + ret = videobuf_read_stream(&fh->vb_vidq, data, count, + ppos, 0, file->f_flags & O_NONBLOCK); + mutex_unlock(&dev->lock); + return ret; + } + return 0; +} + +static __poll_t viu_poll(struct file *file, struct poll_table_struct *wait) +{ + struct viu_fh *fh = file->private_data; + struct videobuf_queue *q = &fh->vb_vidq; + struct viu_dev *dev = fh->dev; + __poll_t req_events = poll_requested_events(wait); + __poll_t res = v4l2_ctrl_poll(file, wait); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) + return EPOLLERR; + + if (!(req_events & (EPOLLIN | EPOLLRDNORM))) + return res; + + mutex_lock(&dev->lock); + res |= videobuf_poll_stream(file, q, wait); + mutex_unlock(&dev->lock); + return res; +} + +static int viu_release(struct file *file) +{ + struct viu_fh *fh = file->private_data; + struct viu_dev *dev = fh->dev; + int minor = video_devdata(file)->minor; + + mutex_lock(&dev->lock); + viu_stop_dma(dev); + videobuf_stop(&fh->vb_vidq); + videobuf_mmap_free(&fh->vb_vidq); + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); + mutex_unlock(&dev->lock); + + kfree(fh); + + dev->users--; + dprintk(1, "close (minor=%d, users=%d)\n", + minor, dev->users); + return 0; +} + +static void viu_reset(struct viu_reg __iomem *reg) +{ + iowrite32be(0, ®->status_cfg); + iowrite32be(0x9512a254, ®->luminance); + iowrite32be(0x03310000, ®->chroma_r); + iowrite32be(0x06600f38, ®->chroma_g); + iowrite32be(0x00000409, ®->chroma_b); + iowrite32be(0, ®->field_base_addr); + iowrite32be(0, ®->dma_inc); + iowrite32be(0x01e002d0, ®->picture_count); + iowrite32be(0x00000090, ®->req_alarm); + iowrite32be(0x000000ff, ®->alpha); +} + +static int viu_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct viu_fh *fh = file->private_data; + struct viu_dev *dev = fh->dev; + int ret; + + dprintk(1, "mmap called, vma=%p\n", vma); + + if (mutex_lock_interruptible(&dev->lock)) + return -ERESTARTSYS; + ret = videobuf_mmap_mapper(&fh->vb_vidq, vma); + mutex_unlock(&dev->lock); + + dprintk(1, "vma start=0x%08lx, size=%ld, ret=%d\n", + (unsigned long)vma->vm_start, + (unsigned long)vma->vm_end-(unsigned long)vma->vm_start, + ret); + + return ret; +} + +static const struct v4l2_file_operations viu_fops = { + .owner = THIS_MODULE, + .open = viu_open, + .release = viu_release, + .read = viu_read, + .poll = viu_poll, + .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ + .mmap = viu_mmap, +}; + +static const struct v4l2_ioctl_ops viu_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_cap, + .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt, + .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_overlay, + .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_overlay, + .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_overlay, + .vidioc_overlay = vidioc_overlay, + .vidioc_g_fbuf = vidioc_g_fbuf, + .vidioc_s_fbuf = vidioc_s_fbuf, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_g_std = vidioc_g_std, + .vidioc_s_std = vidioc_s_std, + .vidioc_querystd = vidioc_querystd, + .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_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static const struct video_device viu_template = { + .name = "FSL viu", + .fops = &viu_fops, + .minor = -1, + .ioctl_ops = &viu_ioctl_ops, + .release = video_device_release, + + .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL, + .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_READWRITE, +}; + +static int viu_of_probe(struct platform_device *op) +{ + struct viu_dev *viu_dev; + struct video_device *vdev; + struct resource r; + struct viu_reg __iomem *viu_regs; + struct i2c_adapter *ad; + int ret, viu_irq; + struct clk *clk; + + ret = of_address_to_resource(op->dev.of_node, 0, &r); + if (ret) { + dev_err(&op->dev, "Can't parse device node resource\n"); + return -ENODEV; + } + + viu_irq = irq_of_parse_and_map(op->dev.of_node, 0); + if (!viu_irq) { + dev_err(&op->dev, "Error while mapping the irq\n"); + return -EINVAL; + } + + /* request mem region */ + if (!devm_request_mem_region(&op->dev, r.start, + sizeof(struct viu_reg), DRV_NAME)) { + dev_err(&op->dev, "Error while requesting mem region\n"); + ret = -EBUSY; + goto err_irq; + } + + /* remap registers */ + viu_regs = devm_ioremap(&op->dev, r.start, sizeof(struct viu_reg)); + if (!viu_regs) { + dev_err(&op->dev, "Can't map register set\n"); + ret = -ENOMEM; + goto err_irq; + } + + /* Prepare our private structure */ + viu_dev = devm_kzalloc(&op->dev, sizeof(struct viu_dev), GFP_KERNEL); + if (!viu_dev) { + dev_err(&op->dev, "Can't allocate private structure\n"); + ret = -ENOMEM; + goto err_irq; + } + + viu_dev->vr = viu_regs; + viu_dev->irq = viu_irq; + viu_dev->dev = &op->dev; + + /* init video dma queues */ + INIT_LIST_HEAD(&viu_dev->vidq.active); + INIT_LIST_HEAD(&viu_dev->vidq.queued); + + snprintf(viu_dev->v4l2_dev.name, + sizeof(viu_dev->v4l2_dev.name), "%s", "VIU"); + ret = v4l2_device_register(viu_dev->dev, &viu_dev->v4l2_dev); + if (ret < 0) { + dev_err(&op->dev, "v4l2_device_register() failed: %d\n", ret); + goto err_irq; + } + + ad = i2c_get_adapter(0); + if (!ad) { + ret = -EFAULT; + dev_err(&op->dev, "couldn't get i2c adapter\n"); + goto err_v4l2; + } + + v4l2_ctrl_handler_init(&viu_dev->hdl, 5); + if (viu_dev->hdl.error) { + ret = viu_dev->hdl.error; + dev_err(&op->dev, "couldn't register control\n"); + goto err_i2c; + } + /* This control handler will inherit the control(s) from the + sub-device(s). */ + viu_dev->v4l2_dev.ctrl_handler = &viu_dev->hdl; + viu_dev->decoder = v4l2_i2c_new_subdev(&viu_dev->v4l2_dev, ad, + "saa7113", VIU_VIDEO_DECODER_ADDR, NULL); + + timer_setup(&viu_dev->vidq.timeout, viu_vid_timeout, 0); + viu_dev->std = V4L2_STD_NTSC_M; + viu_dev->first = 1; + + /* Allocate memory for video device */ + vdev = video_device_alloc(); + if (vdev == NULL) { + ret = -ENOMEM; + goto err_hdl; + } + + *vdev = viu_template; + + vdev->v4l2_dev = &viu_dev->v4l2_dev; + + viu_dev->vdev = vdev; + + /* initialize locks */ + mutex_init(&viu_dev->lock); + viu_dev->vdev->lock = &viu_dev->lock; + spin_lock_init(&viu_dev->slock); + + video_set_drvdata(viu_dev->vdev, viu_dev); + + mutex_lock(&viu_dev->lock); + + ret = video_register_device(viu_dev->vdev, VFL_TYPE_VIDEO, -1); + if (ret < 0) { + video_device_release(viu_dev->vdev); + goto err_unlock; + } + + /* enable VIU clock */ + clk = devm_clk_get(&op->dev, "ipg"); + if (IS_ERR(clk)) { + dev_err(&op->dev, "failed to lookup the clock!\n"); + ret = PTR_ERR(clk); + goto err_vdev; + } + ret = clk_prepare_enable(clk); + if (ret) { + dev_err(&op->dev, "failed to enable the clock!\n"); + goto err_vdev; + } + viu_dev->clk = clk; + + /* reset VIU module */ + viu_reset(viu_dev->vr); + + /* install interrupt handler */ + if (request_irq(viu_dev->irq, viu_intr, 0, "viu", (void *)viu_dev)) { + dev_err(&op->dev, "Request VIU IRQ failed.\n"); + ret = -ENODEV; + goto err_clk; + } + + mutex_unlock(&viu_dev->lock); + + dev_info(&op->dev, "Freescale VIU Video Capture Board\n"); + return ret; + +err_clk: + clk_disable_unprepare(viu_dev->clk); +err_vdev: + video_unregister_device(viu_dev->vdev); +err_unlock: + mutex_unlock(&viu_dev->lock); +err_hdl: + v4l2_ctrl_handler_free(&viu_dev->hdl); +err_i2c: + i2c_put_adapter(ad); +err_v4l2: + v4l2_device_unregister(&viu_dev->v4l2_dev); +err_irq: + irq_dispose_mapping(viu_irq); + return ret; +} + +static int viu_of_remove(struct platform_device *op) +{ + struct v4l2_device *v4l2_dev = platform_get_drvdata(op); + struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev); + struct v4l2_subdev *sdev = list_entry(v4l2_dev->subdevs.next, + struct v4l2_subdev, list); + struct i2c_client *client = v4l2_get_subdevdata(sdev); + + free_irq(dev->irq, (void *)dev); + irq_dispose_mapping(dev->irq); + + clk_disable_unprepare(dev->clk); + + v4l2_ctrl_handler_free(&dev->hdl); + video_unregister_device(dev->vdev); + i2c_put_adapter(client->adapter); + v4l2_device_unregister(&dev->v4l2_dev); + return 0; +} + +#ifdef CONFIG_PM +static int viu_suspend(struct platform_device *op, pm_message_t state) +{ + struct v4l2_device *v4l2_dev = platform_get_drvdata(op); + struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev); + + clk_disable(dev->clk); + return 0; +} + +static int viu_resume(struct platform_device *op) +{ + struct v4l2_device *v4l2_dev = platform_get_drvdata(op); + struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev); + + clk_enable(dev->clk); + return 0; +} +#endif + +/* + * Initialization and module stuff + */ +static const struct of_device_id mpc512x_viu_of_match[] = { + { + .compatible = "fsl,mpc5121-viu", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, mpc512x_viu_of_match); + +static struct platform_driver viu_of_platform_driver = { + .probe = viu_of_probe, + .remove = viu_of_remove, +#ifdef CONFIG_PM + .suspend = viu_suspend, + .resume = viu_resume, +#endif + .driver = { + .name = DRV_NAME, + .of_match_table = mpc512x_viu_of_match, + }, +}; + +module_platform_driver(viu_of_platform_driver); + +MODULE_DESCRIPTION("Freescale Video-In(VIU)"); +MODULE_AUTHOR("Hongjun Chen"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(VIU_VERSION); diff --git a/drivers/staging/media/deprecated/meye/Kconfig b/drivers/staging/media/deprecated/meye/Kconfig new file mode 100644 index 000000000000..f135f8568c85 --- /dev/null +++ b/drivers/staging/media/deprecated/meye/Kconfig @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_MEYE + tristate "Sony Vaio Picturebook Motion Eye Video For Linux (DEPRECATED)" + depends on PCI && VIDEO_DEV + depends on SONY_LAPTOP + depends on X86 || COMPILE_TEST + help + This is the video4linux driver for the Motion Eye camera found + in the Vaio Picturebook laptops. Please read the material in + <file:Documentation/admin-guide/media/meye.rst> for more information. + + If you say Y or M here, you need to say Y or M to "Sony Laptop + Extras" in the misc device section. + + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + + To compile this driver as a module, choose M here: the + module will be called meye. diff --git a/drivers/staging/media/deprecated/meye/Makefile b/drivers/staging/media/deprecated/meye/Makefile new file mode 100644 index 000000000000..36f1f86f0d58 --- /dev/null +++ b/drivers/staging/media/deprecated/meye/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_VIDEO_MEYE) += meye.o diff --git a/drivers/staging/media/deprecated/meye/TODO b/drivers/staging/media/deprecated/meye/TODO new file mode 100644 index 000000000000..6d1d1433d5a0 --- /dev/null +++ b/drivers/staging/media/deprecated/meye/TODO @@ -0,0 +1,6 @@ +The meye driver does not use the vb2 framework for streaming +video, instead it implements this in the driver. + +To prevent removal of this driver early 2023 it has to be +converted to use vb2. Contact the linux-media@vger.kernel.org +mailing list if you want to do this. diff --git a/drivers/staging/media/deprecated/meye/meye.c b/drivers/staging/media/deprecated/meye/meye.c new file mode 100644 index 000000000000..5d87efd9b95c --- /dev/null +++ b/drivers/staging/media/deprecated/meye/meye.c @@ -0,0 +1,1814 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Motion Eye video4linux driver for Sony Vaio PictureBook + * + * Copyright (C) 2001-2004 Stelian Pop <stelian@popies.net> + * + * Copyright (C) 2001-2002 Alcôve <www.alcove.com> + * + * Copyright (C) 2000 Andrew Tridgell <tridge@valinux.com> + * + * Earlier work by Werner Almesberger, Paul `Rusty' Russell and Paul Mackerras. + * + * Some parts borrowed from various video4linux drivers, especially + * bttv-driver.c and zoran.c, see original files for credits. + */ +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/gfp.h> +#include <linux/videodev2.h> +#include <media/v4l2-common.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-event.h> +#include <linux/uaccess.h> +#include <asm/io.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/vmalloc.h> +#include <linux/dma-mapping.h> + +#include "meye.h" +#include <linux/meye.h> + +MODULE_AUTHOR("Stelian Pop <stelian@popies.net>"); +MODULE_DESCRIPTION("v4l2 driver for the MotionEye camera"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(MEYE_DRIVER_VERSION); + +/* number of grab buffers */ +static unsigned int gbuffers = 2; +module_param(gbuffers, int, 0444); +MODULE_PARM_DESC(gbuffers, "number of capture buffers, default is 2 (32 max)"); + +/* size of a grab buffer */ +static unsigned int gbufsize = MEYE_MAX_BUFSIZE; +module_param(gbufsize, int, 0444); +MODULE_PARM_DESC(gbufsize, "size of the capture buffers, default is 614400 (will be rounded up to a page multiple)"); + +/* /dev/videoX registration number */ +static int video_nr = -1; +module_param(video_nr, int, 0444); +MODULE_PARM_DESC(video_nr, "video device to register (0=/dev/video0, etc)"); + +/* driver structure - only one possible */ +static struct meye meye; + +/****************************************************************************/ +/* Memory allocation routines (stolen from bttv-driver.c) */ +/****************************************************************************/ +static void *rvmalloc(unsigned long size) +{ + void *mem; + unsigned long adr; + + size = PAGE_ALIGN(size); + mem = vmalloc_32(size); + if (mem) { + memset(mem, 0, size); + adr = (unsigned long) mem; + while (size > 0) { + SetPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + } + return mem; +} + +static void rvfree(void * mem, unsigned long size) +{ + unsigned long adr; + + if (mem) { + adr = (unsigned long) mem; + while ((long) size > 0) { + ClearPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + vfree(mem); + } +} + +/* + * return a page table pointing to N pages of locked memory + * + * NOTE: The meye device expects DMA addresses on 32 bits, we build + * a table of 1024 entries = 4 bytes * 1024 = 4096 bytes. + */ +static int ptable_alloc(void) +{ + u32 *pt; + int i; + + memset(meye.mchip_ptable, 0, sizeof(meye.mchip_ptable)); + + /* give only 32 bit DMA addresses */ + if (dma_set_mask(&meye.mchip_dev->dev, DMA_BIT_MASK(32))) + return -1; + + meye.mchip_ptable_toc = dma_alloc_coherent(&meye.mchip_dev->dev, + PAGE_SIZE, + &meye.mchip_dmahandle, + GFP_KERNEL); + if (!meye.mchip_ptable_toc) { + meye.mchip_dmahandle = 0; + return -1; + } + + pt = meye.mchip_ptable_toc; + for (i = 0; i < MCHIP_NB_PAGES; i++) { + dma_addr_t dma; + meye.mchip_ptable[i] = dma_alloc_coherent(&meye.mchip_dev->dev, + PAGE_SIZE, + &dma, + GFP_KERNEL); + if (!meye.mchip_ptable[i]) { + int j; + pt = meye.mchip_ptable_toc; + for (j = 0; j < i; ++j) { + dma = (dma_addr_t) *pt; + dma_free_coherent(&meye.mchip_dev->dev, + PAGE_SIZE, + meye.mchip_ptable[j], dma); + pt++; + } + dma_free_coherent(&meye.mchip_dev->dev, + PAGE_SIZE, + meye.mchip_ptable_toc, + meye.mchip_dmahandle); + meye.mchip_ptable_toc = NULL; + meye.mchip_dmahandle = 0; + return -1; + } + *pt = (u32) dma; + pt++; + } + return 0; +} + +static void ptable_free(void) +{ + u32 *pt; + int i; + + pt = meye.mchip_ptable_toc; + for (i = 0; i < MCHIP_NB_PAGES; i++) { + dma_addr_t dma = (dma_addr_t) *pt; + if (meye.mchip_ptable[i]) + dma_free_coherent(&meye.mchip_dev->dev, + PAGE_SIZE, + meye.mchip_ptable[i], dma); + pt++; + } + + if (meye.mchip_ptable_toc) + dma_free_coherent(&meye.mchip_dev->dev, + PAGE_SIZE, + meye.mchip_ptable_toc, + meye.mchip_dmahandle); + + memset(meye.mchip_ptable, 0, sizeof(meye.mchip_ptable)); + meye.mchip_ptable_toc = NULL; + meye.mchip_dmahandle = 0; +} + +/* copy data from ptable into buf */ +static void ptable_copy(u8 *buf, int start, int size, int pt_pages) +{ + int i; + + for (i = 0; i < (size / PAGE_SIZE) * PAGE_SIZE; i += PAGE_SIZE) { + memcpy(buf + i, meye.mchip_ptable[start++], PAGE_SIZE); + if (start >= pt_pages) + start = 0; + } + memcpy(buf + i, meye.mchip_ptable[start], size % PAGE_SIZE); +} + +/****************************************************************************/ +/* JPEG tables at different qualities to load into the VRJ chip */ +/****************************************************************************/ + +/* return a set of quantisation tables based on a quality from 1 to 10 */ +static u16 *jpeg_quantisation_tables(int *length, int quality) +{ + static u16 jpeg_tables[][70] = { { + 0xdbff, 0x4300, 0xff00, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, + 0xdbff, 0x4300, 0xff01, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, + }, + { + 0xdbff, 0x4300, 0x5000, 0x3c37, 0x3c46, 0x5032, 0x4146, 0x5a46, + 0x5055, 0x785f, 0x82c8, 0x6e78, 0x786e, 0xaff5, 0x91b9, 0xffc8, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, + 0xdbff, 0x4300, 0x5501, 0x5a5a, 0x6978, 0xeb78, 0x8282, 0xffeb, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, + }, + { + 0xdbff, 0x4300, 0x2800, 0x1e1c, 0x1e23, 0x2819, 0x2123, 0x2d23, + 0x282b, 0x3c30, 0x4164, 0x373c, 0x3c37, 0x587b, 0x495d, 0x9164, + 0x9980, 0x8f96, 0x8c80, 0xa08a, 0xe6b4, 0xa0c3, 0xdaaa, 0x8aad, + 0xc88c, 0xcbff, 0xeeda, 0xfff5, 0xffff, 0xc19b, 0xffff, 0xfaff, + 0xe6ff, 0xfffd, 0xfff8, + 0xdbff, 0x4300, 0x2b01, 0x2d2d, 0x353c, 0x763c, 0x4141, 0xf876, + 0x8ca5, 0xf8a5, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, + 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, + 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, + 0xf8f8, 0xf8f8, 0xfff8, + }, + { + 0xdbff, 0x4300, 0x1b00, 0x1412, 0x1417, 0x1b11, 0x1617, 0x1e17, + 0x1b1c, 0x2820, 0x2b42, 0x2528, 0x2825, 0x3a51, 0x303d, 0x6042, + 0x6555, 0x5f64, 0x5d55, 0x6a5b, 0x9978, 0x6a81, 0x9071, 0x5b73, + 0x855d, 0x86b5, 0x9e90, 0xaba3, 0xabad, 0x8067, 0xc9bc, 0xa6ba, + 0x99c7, 0xaba8, 0xffa4, + 0xdbff, 0x4300, 0x1c01, 0x1e1e, 0x2328, 0x4e28, 0x2b2b, 0xa44e, + 0x5d6e, 0xa46e, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, + 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, + 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, + 0xa4a4, 0xa4a4, 0xffa4, + }, + { + 0xdbff, 0x4300, 0x1400, 0x0f0e, 0x0f12, 0x140d, 0x1012, 0x1712, + 0x1415, 0x1e18, 0x2132, 0x1c1e, 0x1e1c, 0x2c3d, 0x242e, 0x4932, + 0x4c40, 0x474b, 0x4640, 0x5045, 0x735a, 0x5062, 0x6d55, 0x4556, + 0x6446, 0x6588, 0x776d, 0x817b, 0x8182, 0x604e, 0x978d, 0x7d8c, + 0x7396, 0x817e, 0xff7c, + 0xdbff, 0x4300, 0x1501, 0x1717, 0x1a1e, 0x3b1e, 0x2121, 0x7c3b, + 0x4653, 0x7c53, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, + 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, + 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, + 0x7c7c, 0x7c7c, 0xff7c, + }, + { + 0xdbff, 0x4300, 0x1000, 0x0c0b, 0x0c0e, 0x100a, 0x0d0e, 0x120e, + 0x1011, 0x1813, 0x1a28, 0x1618, 0x1816, 0x2331, 0x1d25, 0x3a28, + 0x3d33, 0x393c, 0x3833, 0x4037, 0x5c48, 0x404e, 0x5744, 0x3745, + 0x5038, 0x516d, 0x5f57, 0x6762, 0x6768, 0x4d3e, 0x7971, 0x6470, + 0x5c78, 0x6765, 0xff63, + 0xdbff, 0x4300, 0x1101, 0x1212, 0x1518, 0x2f18, 0x1a1a, 0x632f, + 0x3842, 0x6342, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, + 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, + 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, + 0x6363, 0x6363, 0xff63, + }, + { + 0xdbff, 0x4300, 0x0d00, 0x0a09, 0x0a0b, 0x0d08, 0x0a0b, 0x0e0b, + 0x0d0e, 0x130f, 0x1520, 0x1213, 0x1312, 0x1c27, 0x171e, 0x2e20, + 0x3129, 0x2e30, 0x2d29, 0x332c, 0x4a3a, 0x333e, 0x4636, 0x2c37, + 0x402d, 0x4157, 0x4c46, 0x524e, 0x5253, 0x3e32, 0x615a, 0x505a, + 0x4a60, 0x5251, 0xff4f, + 0xdbff, 0x4300, 0x0e01, 0x0e0e, 0x1113, 0x2613, 0x1515, 0x4f26, + 0x2d35, 0x4f35, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, + 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, + 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, + 0x4f4f, 0x4f4f, 0xff4f, + }, + { + 0xdbff, 0x4300, 0x0a00, 0x0707, 0x0708, 0x0a06, 0x0808, 0x0b08, + 0x0a0a, 0x0e0b, 0x1018, 0x0d0e, 0x0e0d, 0x151d, 0x1116, 0x2318, + 0x251f, 0x2224, 0x221f, 0x2621, 0x372b, 0x262f, 0x3429, 0x2129, + 0x3022, 0x3141, 0x3934, 0x3e3b, 0x3e3e, 0x2e25, 0x4944, 0x3c43, + 0x3748, 0x3e3d, 0xff3b, + 0xdbff, 0x4300, 0x0a01, 0x0b0b, 0x0d0e, 0x1c0e, 0x1010, 0x3b1c, + 0x2228, 0x3b28, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, + 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, + 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, + 0x3b3b, 0x3b3b, 0xff3b, + }, + { + 0xdbff, 0x4300, 0x0600, 0x0504, 0x0506, 0x0604, 0x0506, 0x0706, + 0x0607, 0x0a08, 0x0a10, 0x090a, 0x0a09, 0x0e14, 0x0c0f, 0x1710, + 0x1814, 0x1718, 0x1614, 0x1a16, 0x251d, 0x1a1f, 0x231b, 0x161c, + 0x2016, 0x202c, 0x2623, 0x2927, 0x292a, 0x1f19, 0x302d, 0x282d, + 0x2530, 0x2928, 0xff28, + 0xdbff, 0x4300, 0x0701, 0x0707, 0x080a, 0x130a, 0x0a0a, 0x2813, + 0x161a, 0x281a, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, + 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, + 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, + 0x2828, 0x2828, 0xff28, + }, + { + 0xdbff, 0x4300, 0x0300, 0x0202, 0x0203, 0x0302, 0x0303, 0x0403, + 0x0303, 0x0504, 0x0508, 0x0405, 0x0504, 0x070a, 0x0607, 0x0c08, + 0x0c0a, 0x0b0c, 0x0b0a, 0x0d0b, 0x120e, 0x0d10, 0x110e, 0x0b0e, + 0x100b, 0x1016, 0x1311, 0x1514, 0x1515, 0x0f0c, 0x1817, 0x1416, + 0x1218, 0x1514, 0xff14, + 0xdbff, 0x4300, 0x0301, 0x0404, 0x0405, 0x0905, 0x0505, 0x1409, + 0x0b0d, 0x140d, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, + 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, + 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, + 0x1414, 0x1414, 0xff14, + }, + { + 0xdbff, 0x4300, 0x0100, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0101, 0x0101, 0xff01, + 0xdbff, 0x4300, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0101, 0x0101, 0xff01, + } }; + + if (quality < 0 || quality > 10) { + printk(KERN_WARNING + "meye: invalid quality level %d - using 8\n", quality); + quality = 8; + } + + *length = ARRAY_SIZE(jpeg_tables[quality]); + return jpeg_tables[quality]; +} + +/* return a generic set of huffman tables */ +static u16 *jpeg_huffman_tables(int *length) +{ + static u16 tables[] = { + 0xC4FF, 0xB500, 0x0010, 0x0102, 0x0303, 0x0402, 0x0503, 0x0405, + 0x0004, 0x0100, 0x017D, 0x0302, 0x0400, 0x0511, 0x2112, 0x4131, + 0x1306, 0x6151, 0x2207, 0x1471, 0x8132, 0xA191, 0x2308, 0xB142, + 0x15C1, 0xD152, 0x24F0, 0x6233, 0x8272, 0x0A09, 0x1716, 0x1918, + 0x251A, 0x2726, 0x2928, 0x342A, 0x3635, 0x3837, 0x3A39, 0x4443, + 0x4645, 0x4847, 0x4A49, 0x5453, 0x5655, 0x5857, 0x5A59, 0x6463, + 0x6665, 0x6867, 0x6A69, 0x7473, 0x7675, 0x7877, 0x7A79, 0x8483, + 0x8685, 0x8887, 0x8A89, 0x9392, 0x9594, 0x9796, 0x9998, 0xA29A, + 0xA4A3, 0xA6A5, 0xA8A7, 0xAAA9, 0xB3B2, 0xB5B4, 0xB7B6, 0xB9B8, + 0xC2BA, 0xC4C3, 0xC6C5, 0xC8C7, 0xCAC9, 0xD3D2, 0xD5D4, 0xD7D6, + 0xD9D8, 0xE1DA, 0xE3E2, 0xE5E4, 0xE7E6, 0xE9E8, 0xF1EA, 0xF3F2, + 0xF5F4, 0xF7F6, 0xF9F8, 0xFFFA, + 0xC4FF, 0xB500, 0x0011, 0x0102, 0x0402, 0x0304, 0x0704, 0x0405, + 0x0004, 0x0201, 0x0077, 0x0201, 0x1103, 0x0504, 0x3121, 0x1206, + 0x5141, 0x6107, 0x1371, 0x3222, 0x0881, 0x4214, 0xA191, 0xC1B1, + 0x2309, 0x5233, 0x15F0, 0x7262, 0x0AD1, 0x2416, 0xE134, 0xF125, + 0x1817, 0x1A19, 0x2726, 0x2928, 0x352A, 0x3736, 0x3938, 0x433A, + 0x4544, 0x4746, 0x4948, 0x534A, 0x5554, 0x5756, 0x5958, 0x635A, + 0x6564, 0x6766, 0x6968, 0x736A, 0x7574, 0x7776, 0x7978, 0x827A, + 0x8483, 0x8685, 0x8887, 0x8A89, 0x9392, 0x9594, 0x9796, 0x9998, + 0xA29A, 0xA4A3, 0xA6A5, 0xA8A7, 0xAAA9, 0xB3B2, 0xB5B4, 0xB7B6, + 0xB9B8, 0xC2BA, 0xC4C3, 0xC6C5, 0xC8C7, 0xCAC9, 0xD3D2, 0xD5D4, + 0xD7D6, 0xD9D8, 0xE2DA, 0xE4E3, 0xE6E5, 0xE8E7, 0xEAE9, 0xF3F2, + 0xF5F4, 0xF7F6, 0xF9F8, 0xFFFA, + 0xC4FF, 0x1F00, 0x0000, 0x0501, 0x0101, 0x0101, 0x0101, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0201, 0x0403, 0x0605, 0x0807, 0x0A09, + 0xFF0B, + 0xC4FF, 0x1F00, 0x0001, 0x0103, 0x0101, 0x0101, 0x0101, 0x0101, + 0x0000, 0x0000, 0x0000, 0x0201, 0x0403, 0x0605, 0x0807, 0x0A09, + 0xFF0B + }; + + *length = ARRAY_SIZE(tables); + return tables; +} + +/****************************************************************************/ +/* MCHIP low-level functions */ +/****************************************************************************/ + +/* returns the horizontal capture size */ +static inline int mchip_hsize(void) +{ + return meye.params.subsample ? 320 : 640; +} + +/* returns the vertical capture size */ +static inline int mchip_vsize(void) +{ + return meye.params.subsample ? 240 : 480; +} + +/* waits for a register to be available */ +static void mchip_sync(int reg) +{ + u32 status; + int i; + + if (reg == MCHIP_MM_FIFO_DATA) { + for (i = 0; i < MCHIP_REG_TIMEOUT; i++) { + status = readl(meye.mchip_mmregs + + MCHIP_MM_FIFO_STATUS); + if (!(status & MCHIP_MM_FIFO_WAIT)) { + printk(KERN_WARNING "meye: fifo not ready\n"); + return; + } + if (status & MCHIP_MM_FIFO_READY) + return; + udelay(1); + } + } else if (reg > 0x80) { + u32 mask = (reg < 0x100) ? MCHIP_HIC_STATUS_MCC_RDY + : MCHIP_HIC_STATUS_VRJ_RDY; + for (i = 0; i < MCHIP_REG_TIMEOUT; i++) { + status = readl(meye.mchip_mmregs + MCHIP_HIC_STATUS); + if (status & mask) + return; + udelay(1); + } + } else + return; + printk(KERN_WARNING + "meye: mchip_sync() timeout on reg 0x%x status=0x%x\n", + reg, status); +} + +/* sets a value into the register */ +static inline void mchip_set(int reg, u32 v) +{ + mchip_sync(reg); + writel(v, meye.mchip_mmregs + reg); +} + +/* get the register value */ +static inline u32 mchip_read(int reg) +{ + mchip_sync(reg); + return readl(meye.mchip_mmregs + reg); +} + +/* wait for a register to become a particular value */ +static inline int mchip_delay(u32 reg, u32 v) +{ + int n = 10; + while (--n && mchip_read(reg) != v) + udelay(1); + return n; +} + +/* setup subsampling */ +static void mchip_subsample(void) +{ + mchip_set(MCHIP_MCC_R_SAMPLING, meye.params.subsample); + mchip_set(MCHIP_MCC_R_XRANGE, mchip_hsize()); + mchip_set(MCHIP_MCC_R_YRANGE, mchip_vsize()); + mchip_set(MCHIP_MCC_B_XRANGE, mchip_hsize()); + mchip_set(MCHIP_MCC_B_YRANGE, mchip_vsize()); + mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE); +} + +/* set the framerate into the mchip */ +static void mchip_set_framerate(void) +{ + mchip_set(MCHIP_HIC_S_RATE, meye.params.framerate); +} + +/* load some huffman and quantisation tables into the VRJ chip ready + for JPEG compression */ +static void mchip_load_tables(void) +{ + int i; + int length; + u16 *tables; + + tables = jpeg_huffman_tables(&length); + for (i = 0; i < length; i++) + writel(tables[i], meye.mchip_mmregs + MCHIP_VRJ_TABLE_DATA); + + tables = jpeg_quantisation_tables(&length, meye.params.quality); + for (i = 0; i < length; i++) + writel(tables[i], meye.mchip_mmregs + MCHIP_VRJ_TABLE_DATA); +} + +/* setup the VRJ parameters in the chip */ +static void mchip_vrj_setup(u8 mode) +{ + mchip_set(MCHIP_VRJ_BUS_MODE, 5); + mchip_set(MCHIP_VRJ_SIGNAL_ACTIVE_LEVEL, 0x1f); + mchip_set(MCHIP_VRJ_PDAT_USE, 1); + mchip_set(MCHIP_VRJ_IRQ_FLAG, 0xa0); + mchip_set(MCHIP_VRJ_MODE_SPECIFY, mode); + mchip_set(MCHIP_VRJ_NUM_LINES, mchip_vsize()); + mchip_set(MCHIP_VRJ_NUM_PIXELS, mchip_hsize()); + mchip_set(MCHIP_VRJ_NUM_COMPONENTS, 0x1b); + mchip_set(MCHIP_VRJ_LIMIT_COMPRESSED_LO, 0xFFFF); + mchip_set(MCHIP_VRJ_LIMIT_COMPRESSED_HI, 0xFFFF); + mchip_set(MCHIP_VRJ_COMP_DATA_FORMAT, 0xC); + mchip_set(MCHIP_VRJ_RESTART_INTERVAL, 0); + mchip_set(MCHIP_VRJ_SOF1, 0x601); + mchip_set(MCHIP_VRJ_SOF2, 0x1502); + mchip_set(MCHIP_VRJ_SOF3, 0x1503); + mchip_set(MCHIP_VRJ_SOF4, 0x1596); + mchip_set(MCHIP_VRJ_SOS, 0x0ed0); + + mchip_load_tables(); +} + +/* sets the DMA parameters into the chip */ +static void mchip_dma_setup(dma_addr_t dma_addr) +{ + int i; + + mchip_set(MCHIP_MM_PT_ADDR, (u32)dma_addr); + for (i = 0; i < 4; i++) + mchip_set(MCHIP_MM_FIR(i), 0); + meye.mchip_fnum = 0; +} + +/* setup for DMA transfers - also zeros the framebuffer */ +static int mchip_dma_alloc(void) +{ + if (!meye.mchip_dmahandle) + if (ptable_alloc()) + return -1; + return 0; +} + +/* frees the DMA buffer */ +static void mchip_dma_free(void) +{ + if (meye.mchip_dmahandle) { + mchip_dma_setup(0); + ptable_free(); + } +} + +/* stop any existing HIC action and wait for any dma to complete then + reset the dma engine */ +static void mchip_hic_stop(void) +{ + int i, j; + + meye.mchip_mode = MCHIP_HIC_MODE_NOOP; + if (!(mchip_read(MCHIP_HIC_STATUS) & MCHIP_HIC_STATUS_BUSY)) + return; + for (i = 0; i < 20; ++i) { + mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_STOP); + mchip_delay(MCHIP_HIC_CMD, 0); + for (j = 0; j < 100; ++j) { + if (mchip_delay(MCHIP_HIC_STATUS, + MCHIP_HIC_STATUS_IDLE)) + return; + msleep(1); + } + printk(KERN_ERR "meye: need to reset HIC!\n"); + + mchip_set(MCHIP_HIC_CTL, MCHIP_HIC_CTL_SOFT_RESET); + msleep(250); + } + printk(KERN_ERR "meye: resetting HIC hanged!\n"); +} + +/****************************************************************************/ +/* MCHIP frame processing functions */ +/****************************************************************************/ + +/* get the next ready frame from the dma engine */ +static u32 mchip_get_frame(void) +{ + return mchip_read(MCHIP_MM_FIR(meye.mchip_fnum)); +} + +/* frees the current frame from the dma engine */ +static void mchip_free_frame(void) +{ + mchip_set(MCHIP_MM_FIR(meye.mchip_fnum), 0); + meye.mchip_fnum++; + meye.mchip_fnum %= 4; +} + +/* read one frame from the framebuffer assuming it was captured using + a uncompressed transfer */ +static void mchip_cont_read_frame(u32 v, u8 *buf, int size) +{ + int pt_id; + + pt_id = (v >> 17) & 0x3FF; + + ptable_copy(buf, pt_id, size, MCHIP_NB_PAGES); +} + +/* read a compressed frame from the framebuffer */ +static int mchip_comp_read_frame(u32 v, u8 *buf, int size) +{ + int pt_start, pt_end, trailer; + int fsize; + int i; + + pt_start = (v >> 19) & 0xFF; + pt_end = (v >> 11) & 0xFF; + trailer = (v >> 1) & 0x3FF; + + if (pt_end < pt_start) + fsize = (MCHIP_NB_PAGES_MJPEG - pt_start) * PAGE_SIZE + + pt_end * PAGE_SIZE + trailer * 4; + else + fsize = (pt_end - pt_start) * PAGE_SIZE + trailer * 4; + + if (fsize > size) { + printk(KERN_WARNING "meye: oversized compressed frame %d\n", + fsize); + return -1; + } + + ptable_copy(buf, pt_start, fsize, MCHIP_NB_PAGES_MJPEG); + +#ifdef MEYE_JPEG_CORRECTION + + /* Some mchip generated jpeg frames are incorrect. In most + * (all ?) of those cases, the final EOI (0xff 0xd9) marker + * is not present at the end of the frame. + * + * Since adding the final marker is not enough to restore + * the jpeg integrity, we drop the frame. + */ + + for (i = fsize - 1; i > 0 && buf[i] == 0xff; i--) ; + + if (i < 2 || buf[i - 1] != 0xff || buf[i] != 0xd9) + return -1; + +#endif + + return fsize; +} + +/* take a picture into SDRAM */ +static void mchip_take_picture(void) +{ + int i; + + mchip_hic_stop(); + mchip_subsample(); + mchip_dma_setup(meye.mchip_dmahandle); + + mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_CAP); + mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START); + + mchip_delay(MCHIP_HIC_CMD, 0); + + for (i = 0; i < 100; ++i) { + if (mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE)) + break; + msleep(1); + } +} + +/* dma a previously taken picture into a buffer */ +static void mchip_get_picture(u8 *buf, int bufsize) +{ + u32 v; + int i; + + mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_OUT); + mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START); + + mchip_delay(MCHIP_HIC_CMD, 0); + for (i = 0; i < 100; ++i) { + if (mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE)) + break; + msleep(1); + } + for (i = 0; i < 4; ++i) { + v = mchip_get_frame(); + if (v & MCHIP_MM_FIR_RDY) { + mchip_cont_read_frame(v, buf, bufsize); + break; + } + mchip_free_frame(); + } +} + +/* start continuous dma capture */ +static void mchip_continuous_start(void) +{ + mchip_hic_stop(); + mchip_subsample(); + mchip_set_framerate(); + mchip_dma_setup(meye.mchip_dmahandle); + + meye.mchip_mode = MCHIP_HIC_MODE_CONT_OUT; + + mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_CONT_OUT); + mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START); + + mchip_delay(MCHIP_HIC_CMD, 0); +} + +/* compress one frame into a buffer */ +static int mchip_compress_frame(u8 *buf, int bufsize) +{ + u32 v; + int len = -1, i; + + mchip_vrj_setup(0x3f); + udelay(50); + + mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_COMP); + mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START); + + mchip_delay(MCHIP_HIC_CMD, 0); + for (i = 0; i < 100; ++i) { + if (mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE)) + break; + msleep(1); + } + + for (i = 0; i < 4; ++i) { + v = mchip_get_frame(); + if (v & MCHIP_MM_FIR_RDY) { + len = mchip_comp_read_frame(v, buf, bufsize); + break; + } + mchip_free_frame(); + } + return len; +} + +#if 0 +/* uncompress one image into a buffer */ +static int mchip_uncompress_frame(u8 *img, int imgsize, u8 *buf, int bufsize) +{ + mchip_vrj_setup(0x3f); + udelay(50); + + mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_DECOMP); + mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START); + + mchip_delay(MCHIP_HIC_CMD, 0); + + return mchip_comp_read_frame(buf, bufsize); +} +#endif + +/* start continuous compressed capture */ +static void mchip_cont_compression_start(void) +{ + mchip_hic_stop(); + mchip_vrj_setup(0x3f); + mchip_subsample(); + mchip_set_framerate(); + mchip_dma_setup(meye.mchip_dmahandle); + + meye.mchip_mode = MCHIP_HIC_MODE_CONT_COMP; + + mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_CONT_COMP); + mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START); + + mchip_delay(MCHIP_HIC_CMD, 0); +} + +/****************************************************************************/ +/* Interrupt handling */ +/****************************************************************************/ + +static irqreturn_t meye_irq(int irq, void *dev_id) +{ + u32 v; + int reqnr; + static int sequence; + + v = mchip_read(MCHIP_MM_INTA); + + if (meye.mchip_mode != MCHIP_HIC_MODE_CONT_OUT && + meye.mchip_mode != MCHIP_HIC_MODE_CONT_COMP) + return IRQ_NONE; + +again: + v = mchip_get_frame(); + if (!(v & MCHIP_MM_FIR_RDY)) + return IRQ_HANDLED; + + if (meye.mchip_mode == MCHIP_HIC_MODE_CONT_OUT) { + if (kfifo_out_locked(&meye.grabq, (unsigned char *)&reqnr, + sizeof(int), &meye.grabq_lock) != sizeof(int)) { + mchip_free_frame(); + return IRQ_HANDLED; + } + mchip_cont_read_frame(v, meye.grab_fbuffer + gbufsize * reqnr, + mchip_hsize() * mchip_vsize() * 2); + meye.grab_buffer[reqnr].size = mchip_hsize() * mchip_vsize() * 2; + meye.grab_buffer[reqnr].state = MEYE_BUF_DONE; + meye.grab_buffer[reqnr].ts = ktime_get_ns(); + meye.grab_buffer[reqnr].sequence = sequence++; + kfifo_in_locked(&meye.doneq, (unsigned char *)&reqnr, + sizeof(int), &meye.doneq_lock); + wake_up_interruptible(&meye.proc_list); + } else { + int size; + size = mchip_comp_read_frame(v, meye.grab_temp, gbufsize); + if (size == -1) { + mchip_free_frame(); + goto again; + } + if (kfifo_out_locked(&meye.grabq, (unsigned char *)&reqnr, + sizeof(int), &meye.grabq_lock) != sizeof(int)) { + mchip_free_frame(); + goto again; + } + memcpy(meye.grab_fbuffer + gbufsize * reqnr, meye.grab_temp, + size); + meye.grab_buffer[reqnr].size = size; + meye.grab_buffer[reqnr].state = MEYE_BUF_DONE; + meye.grab_buffer[reqnr].ts = ktime_get_ns(); + meye.grab_buffer[reqnr].sequence = sequence++; + kfifo_in_locked(&meye.doneq, (unsigned char *)&reqnr, + sizeof(int), &meye.doneq_lock); + wake_up_interruptible(&meye.proc_list); + } + mchip_free_frame(); + goto again; +} + +/****************************************************************************/ +/* video4linux integration */ +/****************************************************************************/ + +static int meye_open(struct file *file) +{ + int i; + + if (test_and_set_bit(0, &meye.in_use)) + return -EBUSY; + + mchip_hic_stop(); + + if (mchip_dma_alloc()) { + printk(KERN_ERR "meye: mchip framebuffer allocation failed\n"); + clear_bit(0, &meye.in_use); + return -ENOBUFS; + } + + for (i = 0; i < MEYE_MAX_BUFNBRS; i++) + meye.grab_buffer[i].state = MEYE_BUF_UNUSED; + kfifo_reset(&meye.grabq); + kfifo_reset(&meye.doneq); + return v4l2_fh_open(file); +} + +static int meye_release(struct file *file) +{ + mchip_hic_stop(); + mchip_dma_free(); + clear_bit(0, &meye.in_use); + return v4l2_fh_release(file); +} + +static int meyeioc_g_params(struct meye_params *p) +{ + *p = meye.params; + return 0; +} + +static int meyeioc_s_params(struct meye_params *jp) +{ + if (jp->subsample > 1) + return -EINVAL; + + if (jp->quality > 10) + return -EINVAL; + + if (jp->sharpness > 63 || jp->agc > 63 || jp->picture > 63) + return -EINVAL; + + if (jp->framerate > 31) + return -EINVAL; + + mutex_lock(&meye.lock); + + if (meye.params.subsample != jp->subsample || + meye.params.quality != jp->quality) + mchip_hic_stop(); /* need restart */ + + meye.params = *jp; + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERASHARPNESS, + meye.params.sharpness); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAAGC, + meye.params.agc); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAPICTURE, + meye.params.picture); + mutex_unlock(&meye.lock); + + return 0; +} + +static int meyeioc_qbuf_capt(int *nb) +{ + if (!meye.grab_fbuffer) + return -EINVAL; + + if (*nb >= gbuffers) + return -EINVAL; + + if (*nb < 0) { + /* stop capture */ + mchip_hic_stop(); + return 0; + } + + if (meye.grab_buffer[*nb].state != MEYE_BUF_UNUSED) + return -EBUSY; + + mutex_lock(&meye.lock); + + if (meye.mchip_mode != MCHIP_HIC_MODE_CONT_COMP) + mchip_cont_compression_start(); + + meye.grab_buffer[*nb].state = MEYE_BUF_USING; + kfifo_in_locked(&meye.grabq, (unsigned char *)nb, sizeof(int), + &meye.grabq_lock); + mutex_unlock(&meye.lock); + + return 0; +} + +static int meyeioc_sync(struct file *file, void *fh, int *i) +{ + int unused; + + if (*i < 0 || *i >= gbuffers) + return -EINVAL; + + mutex_lock(&meye.lock); + switch (meye.grab_buffer[*i].state) { + + case MEYE_BUF_UNUSED: + mutex_unlock(&meye.lock); + return -EINVAL; + case MEYE_BUF_USING: + if (file->f_flags & O_NONBLOCK) { + mutex_unlock(&meye.lock); + return -EAGAIN; + } + if (wait_event_interruptible(meye.proc_list, + (meye.grab_buffer[*i].state != MEYE_BUF_USING))) { + mutex_unlock(&meye.lock); + return -EINTR; + } + fallthrough; + case MEYE_BUF_DONE: + meye.grab_buffer[*i].state = MEYE_BUF_UNUSED; + if (kfifo_out_locked(&meye.doneq, (unsigned char *)&unused, + sizeof(int), &meye.doneq_lock) != sizeof(int)) + break; + } + *i = meye.grab_buffer[*i].size; + mutex_unlock(&meye.lock); + return 0; +} + +static int meyeioc_stillcapt(void) +{ + if (!meye.grab_fbuffer) + return -EINVAL; + + if (meye.grab_buffer[0].state != MEYE_BUF_UNUSED) + return -EBUSY; + + mutex_lock(&meye.lock); + meye.grab_buffer[0].state = MEYE_BUF_USING; + mchip_take_picture(); + + mchip_get_picture(meye.grab_fbuffer, + mchip_hsize() * mchip_vsize() * 2); + + meye.grab_buffer[0].state = MEYE_BUF_DONE; + mutex_unlock(&meye.lock); + + return 0; +} + +static int meyeioc_stilljcapt(int *len) +{ + if (!meye.grab_fbuffer) + return -EINVAL; + + if (meye.grab_buffer[0].state != MEYE_BUF_UNUSED) + return -EBUSY; + + mutex_lock(&meye.lock); + meye.grab_buffer[0].state = MEYE_BUF_USING; + *len = -1; + + while (*len == -1) { + mchip_take_picture(); + *len = mchip_compress_frame(meye.grab_fbuffer, gbufsize); + } + + meye.grab_buffer[0].state = MEYE_BUF_DONE; + mutex_unlock(&meye.lock); + return 0; +} + +static int vidioc_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, "meye", sizeof(cap->driver)); + strscpy(cap->card, "meye", sizeof(cap->card)); + return 0; +} + +static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) +{ + if (i->index != 0) + return -EINVAL; + + strscpy(i->name, "Camera", sizeof(i->name)); + i->type = V4L2_INPUT_TYPE_CAMERA; + + return 0; +} + +static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int vidioc_s_input(struct file *file, void *fh, unsigned int i) +{ + if (i != 0) + return -EINVAL; + + return 0; +} + +static int meye_s_ctrl(struct v4l2_ctrl *ctrl) +{ + mutex_lock(&meye.lock); + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, ctrl->val); + meye.brightness = ctrl->val << 10; + break; + case V4L2_CID_HUE: + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERAHUE, ctrl->val); + meye.hue = ctrl->val << 10; + break; + case V4L2_CID_CONTRAST: + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERACONTRAST, ctrl->val); + meye.contrast = ctrl->val << 10; + break; + case V4L2_CID_SATURATION: + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERACOLOR, ctrl->val); + meye.colour = ctrl->val << 10; + break; + case V4L2_CID_MEYE_AGC: + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERAAGC, ctrl->val); + meye.params.agc = ctrl->val; + break; + case V4L2_CID_SHARPNESS: + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERASHARPNESS, ctrl->val); + meye.params.sharpness = ctrl->val; + break; + case V4L2_CID_MEYE_PICTURE: + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERAPICTURE, ctrl->val); + meye.params.picture = ctrl->val; + break; + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + meye.params.quality = ctrl->val; + break; + case V4L2_CID_MEYE_FRAMERATE: + meye.params.framerate = ctrl->val; + break; + default: + mutex_unlock(&meye.lock); + return -EINVAL; + } + mutex_unlock(&meye.lock); + + return 0; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + if (f->index > 1) + return -EINVAL; + + if (f->index == 0) { + /* standard YUV 422 capture */ + f->flags = 0; + f->pixelformat = V4L2_PIX_FMT_YUYV; + } else { + /* compressed MJPEG capture */ + f->pixelformat = V4L2_PIX_FMT_MJPEG; + } + + return 0; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV && + f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) + return -EINVAL; + + if (f->fmt.pix.field != V4L2_FIELD_ANY && + f->fmt.pix.field != V4L2_FIELD_NONE) + return -EINVAL; + + f->fmt.pix.field = V4L2_FIELD_NONE; + + if (f->fmt.pix.width <= 320) { + f->fmt.pix.width = 320; + f->fmt.pix.height = 240; + } else { + f->fmt.pix.width = 640; + f->fmt.pix.height = 480; + } + + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * + f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = 0; + + return 0; +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + switch (meye.mchip_mode) { + case MCHIP_HIC_MODE_CONT_OUT: + default: + f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; + break; + case MCHIP_HIC_MODE_CONT_COMP: + f->fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; + break; + } + + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.width = mchip_hsize(); + f->fmt.pix.height = mchip_vsize(); + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * + f->fmt.pix.bytesperline; + + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV && + f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) + return -EINVAL; + + if (f->fmt.pix.field != V4L2_FIELD_ANY && + f->fmt.pix.field != V4L2_FIELD_NONE) + return -EINVAL; + + f->fmt.pix.field = V4L2_FIELD_NONE; + mutex_lock(&meye.lock); + + if (f->fmt.pix.width <= 320) { + f->fmt.pix.width = 320; + f->fmt.pix.height = 240; + meye.params.subsample = 1; + } else { + f->fmt.pix.width = 640; + f->fmt.pix.height = 480; + meye.params.subsample = 0; + } + + switch (f->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_YUYV: + meye.mchip_mode = MCHIP_HIC_MODE_CONT_OUT; + break; + case V4L2_PIX_FMT_MJPEG: + meye.mchip_mode = MCHIP_HIC_MODE_CONT_COMP; + break; + } + + mutex_unlock(&meye.lock); + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * + f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = 0; + + return 0; +} + +static int vidioc_reqbufs(struct file *file, void *fh, + struct v4l2_requestbuffers *req) +{ + int i; + + if (req->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + if (meye.grab_fbuffer && req->count == gbuffers) { + /* already allocated, no modifications */ + return 0; + } + + mutex_lock(&meye.lock); + if (meye.grab_fbuffer) { + for (i = 0; i < gbuffers; i++) + if (meye.vma_use_count[i]) { + mutex_unlock(&meye.lock); + return -EINVAL; + } + rvfree(meye.grab_fbuffer, gbuffers * gbufsize); + meye.grab_fbuffer = NULL; + } + + gbuffers = max(2, min((int)req->count, MEYE_MAX_BUFNBRS)); + req->count = gbuffers; + meye.grab_fbuffer = rvmalloc(gbuffers * gbufsize); + + if (!meye.grab_fbuffer) { + printk(KERN_ERR "meye: v4l framebuffer allocation failed\n"); + mutex_unlock(&meye.lock); + return -ENOMEM; + } + + for (i = 0; i < gbuffers; i++) + meye.vma_use_count[i] = 0; + + mutex_unlock(&meye.lock); + + return 0; +} + +static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) +{ + unsigned int index = buf->index; + + if (index >= gbuffers) + return -EINVAL; + + buf->bytesused = meye.grab_buffer[index].size; + buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + + if (meye.grab_buffer[index].state == MEYE_BUF_USING) + buf->flags |= V4L2_BUF_FLAG_QUEUED; + + if (meye.grab_buffer[index].state == MEYE_BUF_DONE) + buf->flags |= V4L2_BUF_FLAG_DONE; + + buf->field = V4L2_FIELD_NONE; + v4l2_buffer_set_timestamp(buf, meye.grab_buffer[index].ts); + buf->sequence = meye.grab_buffer[index].sequence; + buf->memory = V4L2_MEMORY_MMAP; + buf->m.offset = index * gbufsize; + buf->length = gbufsize; + + return 0; +} + +static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) +{ + if (buf->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + if (buf->index >= gbuffers) + return -EINVAL; + + if (meye.grab_buffer[buf->index].state != MEYE_BUF_UNUSED) + return -EINVAL; + + mutex_lock(&meye.lock); + buf->flags |= V4L2_BUF_FLAG_QUEUED; + buf->flags &= ~V4L2_BUF_FLAG_DONE; + meye.grab_buffer[buf->index].state = MEYE_BUF_USING; + kfifo_in_locked(&meye.grabq, (unsigned char *)&buf->index, + sizeof(int), &meye.grabq_lock); + mutex_unlock(&meye.lock); + + return 0; +} + +static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) +{ + int reqnr; + + if (buf->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + mutex_lock(&meye.lock); + + if (kfifo_len(&meye.doneq) == 0 && file->f_flags & O_NONBLOCK) { + mutex_unlock(&meye.lock); + return -EAGAIN; + } + + if (wait_event_interruptible(meye.proc_list, + kfifo_len(&meye.doneq) != 0) < 0) { + mutex_unlock(&meye.lock); + return -EINTR; + } + + if (!kfifo_out_locked(&meye.doneq, (unsigned char *)&reqnr, + sizeof(int), &meye.doneq_lock)) { + mutex_unlock(&meye.lock); + return -EBUSY; + } + + if (meye.grab_buffer[reqnr].state != MEYE_BUF_DONE) { + mutex_unlock(&meye.lock); + return -EINVAL; + } + + buf->index = reqnr; + buf->bytesused = meye.grab_buffer[reqnr].size; + buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + buf->field = V4L2_FIELD_NONE; + v4l2_buffer_set_timestamp(buf, meye.grab_buffer[reqnr].ts); + buf->sequence = meye.grab_buffer[reqnr].sequence; + buf->memory = V4L2_MEMORY_MMAP; + buf->m.offset = reqnr * gbufsize; + buf->length = gbufsize; + meye.grab_buffer[reqnr].state = MEYE_BUF_UNUSED; + mutex_unlock(&meye.lock); + + return 0; +} + +static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i) +{ + mutex_lock(&meye.lock); + + switch (meye.mchip_mode) { + case MCHIP_HIC_MODE_CONT_OUT: + mchip_continuous_start(); + break; + case MCHIP_HIC_MODE_CONT_COMP: + mchip_cont_compression_start(); + break; + default: + mutex_unlock(&meye.lock); + return -EINVAL; + } + + mutex_unlock(&meye.lock); + + return 0; +} + +static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i) +{ + mutex_lock(&meye.lock); + mchip_hic_stop(); + kfifo_reset(&meye.grabq); + kfifo_reset(&meye.doneq); + + for (i = 0; i < MEYE_MAX_BUFNBRS; i++) + meye.grab_buffer[i].state = MEYE_BUF_UNUSED; + + mutex_unlock(&meye.lock); + return 0; +} + +static long vidioc_default(struct file *file, void *fh, bool valid_prio, + unsigned int cmd, void *arg) +{ + switch (cmd) { + case MEYEIOC_G_PARAMS: + return meyeioc_g_params((struct meye_params *) arg); + + case MEYEIOC_S_PARAMS: + return meyeioc_s_params((struct meye_params *) arg); + + case MEYEIOC_QBUF_CAPT: + return meyeioc_qbuf_capt((int *) arg); + + case MEYEIOC_SYNC: + return meyeioc_sync(file, fh, (int *) arg); + + case MEYEIOC_STILLCAPT: + return meyeioc_stillcapt(); + + case MEYEIOC_STILLJCAPT: + return meyeioc_stilljcapt((int *) arg); + + default: + return -ENOTTY; + } + +} + +static __poll_t meye_poll(struct file *file, poll_table *wait) +{ + __poll_t res = v4l2_ctrl_poll(file, wait); + + mutex_lock(&meye.lock); + poll_wait(file, &meye.proc_list, wait); + if (kfifo_len(&meye.doneq)) + res |= EPOLLIN | EPOLLRDNORM; + mutex_unlock(&meye.lock); + return res; +} + +static void meye_vm_open(struct vm_area_struct *vma) +{ + long idx = (long)vma->vm_private_data; + meye.vma_use_count[idx]++; +} + +static void meye_vm_close(struct vm_area_struct *vma) +{ + long idx = (long)vma->vm_private_data; + meye.vma_use_count[idx]--; +} + +static const struct vm_operations_struct meye_vm_ops = { + .open = meye_vm_open, + .close = meye_vm_close, +}; + +static int meye_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long start = vma->vm_start; + unsigned long size = vma->vm_end - vma->vm_start; + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + unsigned long page, pos; + + mutex_lock(&meye.lock); + if (size > gbuffers * gbufsize || offset > gbuffers * gbufsize - size) { + mutex_unlock(&meye.lock); + return -EINVAL; + } + if (!meye.grab_fbuffer) { + int i; + + /* lazy allocation */ + meye.grab_fbuffer = rvmalloc(gbuffers*gbufsize); + if (!meye.grab_fbuffer) { + printk(KERN_ERR "meye: v4l framebuffer allocation failed\n"); + mutex_unlock(&meye.lock); + return -ENOMEM; + } + for (i = 0; i < gbuffers; i++) + meye.vma_use_count[i] = 0; + } + pos = (unsigned long)meye.grab_fbuffer + offset; + + while (size > 0) { + page = vmalloc_to_pfn((void *)pos); + if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { + mutex_unlock(&meye.lock); + return -EAGAIN; + } + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + vma->vm_ops = &meye_vm_ops; + vma->vm_flags &= ~VM_IO; /* not I/O memory */ + vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; + vma->vm_private_data = (void *) (offset / gbufsize); + meye_vm_open(vma); + + mutex_unlock(&meye.lock); + return 0; +} + +static const struct v4l2_file_operations meye_fops = { + .owner = THIS_MODULE, + .open = meye_open, + .release = meye_release, + .mmap = meye_mmap, + .unlocked_ioctl = video_ioctl2, + .poll = meye_poll, +}; + +static const struct v4l2_ioctl_ops meye_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_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_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_default = vidioc_default, +}; + +static const struct video_device meye_template = { + .name = "meye", + .fops = &meye_fops, + .ioctl_ops = &meye_ioctl_ops, + .release = video_device_release_empty, + .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING, +}; + +static const struct v4l2_ctrl_ops meye_ctrl_ops = { + .s_ctrl = meye_s_ctrl, +}; + +static int __maybe_unused meye_suspend(struct device *dev) +{ + meye.pm_mchip_mode = meye.mchip_mode; + mchip_hic_stop(); + mchip_set(MCHIP_MM_INTA, 0x0); + return 0; +} + +static int __maybe_unused meye_resume(struct device *dev) +{ + pci_write_config_word(meye.mchip_dev, MCHIP_PCI_SOFTRESET_SET, 1); + + mchip_delay(MCHIP_HIC_CMD, 0); + mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE); + msleep(1); + mchip_set(MCHIP_VRJ_SOFT_RESET, 1); + msleep(1); + mchip_set(MCHIP_MM_PCI_MODE, 5); + msleep(1); + mchip_set(MCHIP_MM_INTA, MCHIP_MM_INTA_HIC_1_MASK); + + switch (meye.pm_mchip_mode) { + case MCHIP_HIC_MODE_CONT_OUT: + mchip_continuous_start(); + break; + case MCHIP_HIC_MODE_CONT_COMP: + mchip_cont_compression_start(); + break; + } + return 0; +} + +static int meye_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) +{ + static const struct v4l2_ctrl_config ctrl_agc = { + .id = V4L2_CID_MEYE_AGC, + .type = V4L2_CTRL_TYPE_INTEGER, + .ops = &meye_ctrl_ops, + .name = "AGC", + .max = 63, + .step = 1, + .def = 48, + .flags = V4L2_CTRL_FLAG_SLIDER, + }; + static const struct v4l2_ctrl_config ctrl_picture = { + .id = V4L2_CID_MEYE_PICTURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .ops = &meye_ctrl_ops, + .name = "Picture", + .max = 63, + .step = 1, + }; + static const struct v4l2_ctrl_config ctrl_framerate = { + .id = V4L2_CID_MEYE_FRAMERATE, + .type = V4L2_CTRL_TYPE_INTEGER, + .ops = &meye_ctrl_ops, + .name = "Framerate", + .max = 31, + .step = 1, + }; + struct v4l2_device *v4l2_dev = &meye.v4l2_dev; + int ret = -EBUSY; + unsigned long mchip_adr; + + if (meye.mchip_dev != NULL) { + printk(KERN_ERR "meye: only one device allowed!\n"); + return ret; + } + + ret = v4l2_device_register(&pcidev->dev, v4l2_dev); + if (ret < 0) { + v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); + return ret; + } + ret = -ENOMEM; + meye.mchip_dev = pcidev; + + meye.grab_temp = vmalloc(array_size(PAGE_SIZE, MCHIP_NB_PAGES_MJPEG)); + if (!meye.grab_temp) + goto outvmalloc; + + spin_lock_init(&meye.grabq_lock); + if (kfifo_alloc(&meye.grabq, sizeof(int) * MEYE_MAX_BUFNBRS, + GFP_KERNEL)) + goto outkfifoalloc1; + + spin_lock_init(&meye.doneq_lock); + if (kfifo_alloc(&meye.doneq, sizeof(int) * MEYE_MAX_BUFNBRS, + GFP_KERNEL)) + goto outkfifoalloc2; + + meye.vdev = meye_template; + meye.vdev.v4l2_dev = &meye.v4l2_dev; + + ret = sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 1); + if (ret) { + v4l2_err(v4l2_dev, "meye: unable to power on the camera\n"); + v4l2_err(v4l2_dev, "meye: did you enable the camera in sonypi using the module options ?\n"); + goto outsonypienable; + } + + ret = pci_enable_device(meye.mchip_dev); + if (ret) { + v4l2_err(v4l2_dev, "meye: pci_enable_device failed\n"); + goto outenabledev; + } + + ret = -EIO; + mchip_adr = pci_resource_start(meye.mchip_dev,0); + if (!mchip_adr) { + v4l2_err(v4l2_dev, "meye: mchip has no device base address\n"); + goto outregions; + } + if (!request_mem_region(pci_resource_start(meye.mchip_dev, 0), + pci_resource_len(meye.mchip_dev, 0), + "meye")) { + v4l2_err(v4l2_dev, "meye: request_mem_region failed\n"); + goto outregions; + } + meye.mchip_mmregs = ioremap(mchip_adr, MCHIP_MM_REGS); + if (!meye.mchip_mmregs) { + v4l2_err(v4l2_dev, "meye: ioremap failed\n"); + goto outremap; + } + + meye.mchip_irq = pcidev->irq; + if (request_irq(meye.mchip_irq, meye_irq, + IRQF_SHARED, "meye", meye_irq)) { + v4l2_err(v4l2_dev, "request_irq failed\n"); + goto outreqirq; + } + + pci_write_config_byte(meye.mchip_dev, PCI_CACHE_LINE_SIZE, 8); + pci_write_config_byte(meye.mchip_dev, PCI_LATENCY_TIMER, 64); + + pci_set_master(meye.mchip_dev); + + /* Ask the camera to perform a soft reset. */ + pci_write_config_word(meye.mchip_dev, MCHIP_PCI_SOFTRESET_SET, 1); + + mchip_delay(MCHIP_HIC_CMD, 0); + mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE); + + msleep(1); + mchip_set(MCHIP_VRJ_SOFT_RESET, 1); + + msleep(1); + mchip_set(MCHIP_MM_PCI_MODE, 5); + + msleep(1); + mchip_set(MCHIP_MM_INTA, MCHIP_MM_INTA_HIC_1_MASK); + + mutex_init(&meye.lock); + init_waitqueue_head(&meye.proc_list); + + v4l2_ctrl_handler_init(&meye.hdl, 3); + v4l2_ctrl_new_std(&meye.hdl, &meye_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 63, 1, 32); + v4l2_ctrl_new_std(&meye.hdl, &meye_ctrl_ops, + V4L2_CID_HUE, 0, 63, 1, 32); + v4l2_ctrl_new_std(&meye.hdl, &meye_ctrl_ops, + V4L2_CID_CONTRAST, 0, 63, 1, 32); + v4l2_ctrl_new_std(&meye.hdl, &meye_ctrl_ops, + V4L2_CID_SATURATION, 0, 63, 1, 32); + v4l2_ctrl_new_custom(&meye.hdl, &ctrl_agc, NULL); + v4l2_ctrl_new_std(&meye.hdl, &meye_ctrl_ops, + V4L2_CID_SHARPNESS, 0, 63, 1, 32); + v4l2_ctrl_new_custom(&meye.hdl, &ctrl_picture, NULL); + v4l2_ctrl_new_std(&meye.hdl, &meye_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, 0, 10, 1, 8); + v4l2_ctrl_new_custom(&meye.hdl, &ctrl_framerate, NULL); + if (meye.hdl.error) { + v4l2_err(v4l2_dev, "couldn't register controls\n"); + goto outvideoreg; + } + + v4l2_ctrl_handler_setup(&meye.hdl); + meye.vdev.ctrl_handler = &meye.hdl; + + if (video_register_device(&meye.vdev, VFL_TYPE_VIDEO, + video_nr) < 0) { + v4l2_err(v4l2_dev, "video_register_device failed\n"); + goto outvideoreg; + } + + v4l2_info(v4l2_dev, "Motion Eye Camera Driver v%s.\n", + MEYE_DRIVER_VERSION); + v4l2_info(v4l2_dev, "mchip KL5A72002 rev. %d, base %lx, irq %d\n", + meye.mchip_dev->revision, mchip_adr, meye.mchip_irq); + + return 0; + +outvideoreg: + v4l2_ctrl_handler_free(&meye.hdl); + free_irq(meye.mchip_irq, meye_irq); +outreqirq: + iounmap(meye.mchip_mmregs); +outremap: + release_mem_region(pci_resource_start(meye.mchip_dev, 0), + pci_resource_len(meye.mchip_dev, 0)); +outregions: + pci_disable_device(meye.mchip_dev); +outenabledev: + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 0); +outsonypienable: + kfifo_free(&meye.doneq); +outkfifoalloc2: + kfifo_free(&meye.grabq); +outkfifoalloc1: + vfree(meye.grab_temp); +outvmalloc: + return ret; +} + +static void meye_remove(struct pci_dev *pcidev) +{ + video_unregister_device(&meye.vdev); + + mchip_hic_stop(); + + mchip_dma_free(); + + /* disable interrupts */ + mchip_set(MCHIP_MM_INTA, 0x0); + + free_irq(meye.mchip_irq, meye_irq); + + iounmap(meye.mchip_mmregs); + + release_mem_region(pci_resource_start(meye.mchip_dev, 0), + pci_resource_len(meye.mchip_dev, 0)); + + pci_disable_device(meye.mchip_dev); + + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 0); + + kfifo_free(&meye.doneq); + kfifo_free(&meye.grabq); + + vfree(meye.grab_temp); + + if (meye.grab_fbuffer) { + rvfree(meye.grab_fbuffer, gbuffers*gbufsize); + meye.grab_fbuffer = NULL; + } + + printk(KERN_INFO "meye: removed\n"); +} + +static const struct pci_device_id meye_pci_tbl[] = { + { PCI_VDEVICE(KAWASAKI, PCI_DEVICE_ID_MCHIP_KL5A72002), 0 }, + { } +}; + +MODULE_DEVICE_TABLE(pci, meye_pci_tbl); + +static SIMPLE_DEV_PM_OPS(meye_pm_ops, meye_suspend, meye_resume); + +static struct pci_driver meye_driver = { + .name = "meye", + .id_table = meye_pci_tbl, + .probe = meye_probe, + .remove = meye_remove, + .driver.pm = &meye_pm_ops, +}; + +static int __init meye_init(void) +{ + gbuffers = max(2, min((int)gbuffers, MEYE_MAX_BUFNBRS)); + if (gbufsize > MEYE_MAX_BUFSIZE) + gbufsize = MEYE_MAX_BUFSIZE; + gbufsize = PAGE_ALIGN(gbufsize); + printk(KERN_INFO "meye: using %d buffers with %dk (%dk total) for capture\n", + gbuffers, + gbufsize / 1024, gbuffers * gbufsize / 1024); + return pci_register_driver(&meye_driver); +} + +static void __exit meye_exit(void) +{ + pci_unregister_driver(&meye_driver); +} + +module_init(meye_init); +module_exit(meye_exit); diff --git a/drivers/staging/media/deprecated/meye/meye.h b/drivers/staging/media/deprecated/meye/meye.h new file mode 100644 index 000000000000..5fa6552cf93d --- /dev/null +++ b/drivers/staging/media/deprecated/meye/meye.h @@ -0,0 +1,311 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Motion Eye video4linux driver for Sony Vaio PictureBook + * + * Copyright (C) 2001-2004 Stelian Pop <stelian@popies.net> + * + * Copyright (C) 2001-2002 Alcôve <www.alcove.com> + * + * Copyright (C) 2000 Andrew Tridgell <tridge@valinux.com> + * + * Earlier work by Werner Almesberger, Paul `Rusty' Russell and Paul Mackerras. + * + * Some parts borrowed from various video4linux drivers, especially + * bttv-driver.c and zoran.c, see original files for credits. + */ + +#ifndef _MEYE_PRIV_H_ +#define _MEYE_PRIV_H_ + +#define MEYE_DRIVER_MAJORVERSION 1 +#define MEYE_DRIVER_MINORVERSION 14 + +#define MEYE_DRIVER_VERSION __stringify(MEYE_DRIVER_MAJORVERSION) "." \ + __stringify(MEYE_DRIVER_MINORVERSION) + +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/kfifo.h> +#include <media/v4l2-ctrls.h> + +/****************************************************************************/ +/* Motion JPEG chip registers */ +/****************************************************************************/ + +/* Motion JPEG chip PCI configuration registers */ +#define MCHIP_PCI_POWER_CSR 0x54 +#define MCHIP_PCI_MCORE_STATUS 0x60 /* see HIC_STATUS */ +#define MCHIP_PCI_HOSTUSEREQ_SET 0x64 +#define MCHIP_PCI_HOSTUSEREQ_CLR 0x68 +#define MCHIP_PCI_LOWPOWER_SET 0x6c +#define MCHIP_PCI_LOWPOWER_CLR 0x70 +#define MCHIP_PCI_SOFTRESET_SET 0x74 + +/* Motion JPEG chip memory mapped registers */ +#define MCHIP_MM_REGS 0x200 /* 512 bytes */ +#define MCHIP_REG_TIMEOUT 1000 /* reg access, ~us */ +#define MCHIP_MCC_VRJ_TIMEOUT 1000 /* MCC & VRJ access */ + +#define MCHIP_MM_PCI_MODE 0x00 /* PCI access mode */ +#define MCHIP_MM_PCI_MODE_RETRY 0x00000001 /* retry mode */ +#define MCHIP_MM_PCI_MODE_MASTER 0x00000002 /* master access */ +#define MCHIP_MM_PCI_MODE_READ_LINE 0x00000004 /* read line */ + +#define MCHIP_MM_INTA 0x04 /* Int status/mask */ +#define MCHIP_MM_INTA_MCC 0x00000001 /* MCC interrupt */ +#define MCHIP_MM_INTA_VRJ 0x00000002 /* VRJ interrupt */ +#define MCHIP_MM_INTA_HIC_1 0x00000004 /* one frame done */ +#define MCHIP_MM_INTA_HIC_1_MASK 0x00000400 /* 1: enable */ +#define MCHIP_MM_INTA_HIC_END 0x00000008 /* all frames done */ +#define MCHIP_MM_INTA_HIC_END_MASK 0x00000800 +#define MCHIP_MM_INTA_JPEG 0x00000010 /* decompress. error */ +#define MCHIP_MM_INTA_JPEG_MASK 0x00001000 +#define MCHIP_MM_INTA_CAPTURE 0x00000020 /* capture end */ +#define MCHIP_MM_INTA_PCI_ERR 0x00000040 /* PCI error */ +#define MCHIP_MM_INTA_PCI_ERR_MASK 0x00004000 + +#define MCHIP_MM_PT_ADDR 0x08 /* page table address*/ + /* n*4kB */ +#define MCHIP_NB_PAGES 1024 /* pages for display */ +#define MCHIP_NB_PAGES_MJPEG 256 /* pages for mjpeg */ + +#define MCHIP_MM_FIR(n) (0x0c+(n)*4) /* Frame info 0-3 */ +#define MCHIP_MM_FIR_RDY 0x00000001 /* frame ready */ +#define MCHIP_MM_FIR_FAILFR_MASK 0xf8000000 /* # of failed frames */ +#define MCHIP_MM_FIR_FAILFR_SHIFT 27 + + /* continuous comp/decomp mode */ +#define MCHIP_MM_FIR_C_ENDL_MASK 0x000007fe /* end DW [10] */ +#define MCHIP_MM_FIR_C_ENDL_SHIFT 1 +#define MCHIP_MM_FIR_C_ENDP_MASK 0x0007f800 /* end page [8] */ +#define MCHIP_MM_FIR_C_ENDP_SHIFT 11 +#define MCHIP_MM_FIR_C_STARTP_MASK 0x07f80000 /* start page [8] */ +#define MCHIP_MM_FIR_C_STARTP_SHIFT 19 + + /* continuous picture output mode */ +#define MCHIP_MM_FIR_O_STARTP_MASK 0x7ffe0000 /* start page [10] */ +#define MCHIP_MM_FIR_O_STARTP_SHIFT 17 + +#define MCHIP_MM_FIFO_DATA 0x1c /* PCI TGT FIFO data */ +#define MCHIP_MM_FIFO_STATUS 0x20 /* PCI TGT FIFO stat */ +#define MCHIP_MM_FIFO_MASK 0x00000003 +#define MCHIP_MM_FIFO_WAIT_OR_READY 0x00000002 /* Bits common to WAIT & READY*/ +#define MCHIP_MM_FIFO_IDLE 0x0 /* HIC idle */ +#define MCHIP_MM_FIFO_IDLE1 0x1 /* idem ??? */ +#define MCHIP_MM_FIFO_WAIT 0x2 /* wait request */ +#define MCHIP_MM_FIFO_READY 0x3 /* data ready */ + +#define MCHIP_HIC_HOST_USEREQ 0x40 /* host uses MCORE */ + +#define MCHIP_HIC_TP_BUSY 0x44 /* taking picture */ + +#define MCHIP_HIC_PIC_SAVED 0x48 /* pic in SDRAM */ + +#define MCHIP_HIC_LOWPOWER 0x4c /* clock stopped */ + +#define MCHIP_HIC_CTL 0x50 /* HIC control */ +#define MCHIP_HIC_CTL_SOFT_RESET 0x00000001 /* MCORE reset */ +#define MCHIP_HIC_CTL_MCORE_RDY 0x00000002 /* MCORE ready */ + +#define MCHIP_HIC_CMD 0x54 /* HIC command */ +#define MCHIP_HIC_CMD_BITS 0x00000003 /* cmd width=[1:0]*/ +#define MCHIP_HIC_CMD_NOOP 0x0 +#define MCHIP_HIC_CMD_START 0x1 +#define MCHIP_HIC_CMD_STOP 0x2 + +#define MCHIP_HIC_MODE 0x58 +#define MCHIP_HIC_MODE_NOOP 0x0 +#define MCHIP_HIC_MODE_STILL_CAP 0x1 /* still pic capt */ +#define MCHIP_HIC_MODE_DISPLAY 0x2 /* display */ +#define MCHIP_HIC_MODE_STILL_COMP 0x3 /* still pic comp. */ +#define MCHIP_HIC_MODE_STILL_DECOMP 0x4 /* still pic decomp. */ +#define MCHIP_HIC_MODE_CONT_COMP 0x5 /* cont capt+comp */ +#define MCHIP_HIC_MODE_CONT_DECOMP 0x6 /* cont decomp+disp */ +#define MCHIP_HIC_MODE_STILL_OUT 0x7 /* still pic output */ +#define MCHIP_HIC_MODE_CONT_OUT 0x8 /* cont output */ + +#define MCHIP_HIC_STATUS 0x5c +#define MCHIP_HIC_STATUS_MCC_RDY 0x00000001 /* MCC reg acc ok */ +#define MCHIP_HIC_STATUS_VRJ_RDY 0x00000002 /* VRJ reg acc ok */ +#define MCHIP_HIC_STATUS_IDLE 0x00000003 +#define MCHIP_HIC_STATUS_CAPDIS 0x00000004 /* cap/disp in prog */ +#define MCHIP_HIC_STATUS_COMPDEC 0x00000008 /* (de)comp in prog */ +#define MCHIP_HIC_STATUS_BUSY 0x00000010 /* HIC busy */ + +#define MCHIP_HIC_S_RATE 0x60 /* MJPEG # frames */ + +#define MCHIP_HIC_PCI_VFMT 0x64 /* video format */ +#define MCHIP_HIC_PCI_VFMT_YVYU 0x00000001 /* 0: V Y' U Y */ + /* 1: Y' V Y U */ + +#define MCHIP_MCC_CMD 0x80 /* MCC commands */ +#define MCHIP_MCC_CMD_INITIAL 0x0 /* idle ? */ +#define MCHIP_MCC_CMD_IIC_START_SET 0x1 +#define MCHIP_MCC_CMD_IIC_END_SET 0x2 +#define MCHIP_MCC_CMD_FM_WRITE 0x3 /* frame memory */ +#define MCHIP_MCC_CMD_FM_READ 0x4 +#define MCHIP_MCC_CMD_FM_STOP 0x5 +#define MCHIP_MCC_CMD_CAPTURE 0x6 +#define MCHIP_MCC_CMD_DISPLAY 0x7 +#define MCHIP_MCC_CMD_END_DISP 0x8 +#define MCHIP_MCC_CMD_STILL_COMP 0x9 +#define MCHIP_MCC_CMD_STILL_DECOMP 0xa +#define MCHIP_MCC_CMD_STILL_OUTPUT 0xb +#define MCHIP_MCC_CMD_CONT_OUTPUT 0xc +#define MCHIP_MCC_CMD_CONT_COMP 0xd +#define MCHIP_MCC_CMD_CONT_DECOMP 0xe +#define MCHIP_MCC_CMD_RESET 0xf /* MCC reset */ + +#define MCHIP_MCC_IIC_WR 0x84 + +#define MCHIP_MCC_MCC_WR 0x88 + +#define MCHIP_MCC_MCC_RD 0x8c + +#define MCHIP_MCC_STATUS 0x90 +#define MCHIP_MCC_STATUS_CAPT 0x00000001 /* capturing */ +#define MCHIP_MCC_STATUS_DISP 0x00000002 /* displaying */ +#define MCHIP_MCC_STATUS_COMP 0x00000004 /* compressing */ +#define MCHIP_MCC_STATUS_DECOMP 0x00000008 /* decompressing */ +#define MCHIP_MCC_STATUS_MCC_WR 0x00000010 /* register ready */ +#define MCHIP_MCC_STATUS_MCC_RD 0x00000020 /* register ready */ +#define MCHIP_MCC_STATUS_IIC_WR 0x00000040 /* register ready */ +#define MCHIP_MCC_STATUS_OUTPUT 0x00000080 /* output in prog */ + +#define MCHIP_MCC_SIG_POLARITY 0x94 +#define MCHIP_MCC_SIG_POL_VS_H 0x00000001 /* VS active-high */ +#define MCHIP_MCC_SIG_POL_HS_H 0x00000002 /* HS active-high */ +#define MCHIP_MCC_SIG_POL_DOE_H 0x00000004 /* DOE active-high */ + +#define MCHIP_MCC_IRQ 0x98 +#define MCHIP_MCC_IRQ_CAPDIS_STRT 0x00000001 /* cap/disp started */ +#define MCHIP_MCC_IRQ_CAPDIS_STRT_MASK 0x00000010 +#define MCHIP_MCC_IRQ_CAPDIS_END 0x00000002 /* cap/disp ended */ +#define MCHIP_MCC_IRQ_CAPDIS_END_MASK 0x00000020 +#define MCHIP_MCC_IRQ_COMPDEC_STRT 0x00000004 /* (de)comp started */ +#define MCHIP_MCC_IRQ_COMPDEC_STRT_MASK 0x00000040 +#define MCHIP_MCC_IRQ_COMPDEC_END 0x00000008 /* (de)comp ended */ +#define MCHIP_MCC_IRQ_COMPDEC_END_MASK 0x00000080 + +#define MCHIP_MCC_HSTART 0x9c /* video in */ +#define MCHIP_MCC_VSTART 0xa0 +#define MCHIP_MCC_HCOUNT 0xa4 +#define MCHIP_MCC_VCOUNT 0xa8 +#define MCHIP_MCC_R_XBASE 0xac /* capt/disp */ +#define MCHIP_MCC_R_YBASE 0xb0 +#define MCHIP_MCC_R_XRANGE 0xb4 +#define MCHIP_MCC_R_YRANGE 0xb8 +#define MCHIP_MCC_B_XBASE 0xbc /* comp/decomp */ +#define MCHIP_MCC_B_YBASE 0xc0 +#define MCHIP_MCC_B_XRANGE 0xc4 +#define MCHIP_MCC_B_YRANGE 0xc8 + +#define MCHIP_MCC_R_SAMPLING 0xcc /* 1: 1:4 */ + +#define MCHIP_VRJ_CMD 0x100 /* VRJ commands */ + +/* VRJ registers (see table 12.2.4) */ +#define MCHIP_VRJ_COMPRESSED_DATA 0x1b0 +#define MCHIP_VRJ_PIXEL_DATA 0x1b8 + +#define MCHIP_VRJ_BUS_MODE 0x100 +#define MCHIP_VRJ_SIGNAL_ACTIVE_LEVEL 0x108 +#define MCHIP_VRJ_PDAT_USE 0x110 +#define MCHIP_VRJ_MODE_SPECIFY 0x118 +#define MCHIP_VRJ_LIMIT_COMPRESSED_LO 0x120 +#define MCHIP_VRJ_LIMIT_COMPRESSED_HI 0x124 +#define MCHIP_VRJ_COMP_DATA_FORMAT 0x128 +#define MCHIP_VRJ_TABLE_DATA 0x140 +#define MCHIP_VRJ_RESTART_INTERVAL 0x148 +#define MCHIP_VRJ_NUM_LINES 0x150 +#define MCHIP_VRJ_NUM_PIXELS 0x158 +#define MCHIP_VRJ_NUM_COMPONENTS 0x160 +#define MCHIP_VRJ_SOF1 0x168 +#define MCHIP_VRJ_SOF2 0x170 +#define MCHIP_VRJ_SOF3 0x178 +#define MCHIP_VRJ_SOF4 0x180 +#define MCHIP_VRJ_SOS 0x188 +#define MCHIP_VRJ_SOFT_RESET 0x190 + +#define MCHIP_VRJ_STATUS 0x1c0 +#define MCHIP_VRJ_STATUS_BUSY 0x00001 +#define MCHIP_VRJ_STATUS_COMP_ACCESS 0x00002 +#define MCHIP_VRJ_STATUS_PIXEL_ACCESS 0x00004 +#define MCHIP_VRJ_STATUS_ERROR 0x00008 + +#define MCHIP_VRJ_IRQ_FLAG 0x1c8 +#define MCHIP_VRJ_ERROR_REPORT 0x1d8 + +#define MCHIP_VRJ_START_COMMAND 0x1a0 + +/****************************************************************************/ +/* Driver definitions. */ +/****************************************************************************/ + +/* Sony Programmable I/O Controller for accessing the camera commands */ +#include <linux/sony-laptop.h> + +/* private API definitions */ +#include <linux/meye.h> +#include <linux/mutex.h> + + +/* Enable jpg software correction */ +#define MEYE_JPEG_CORRECTION 1 + +/* Maximum size of a buffer */ +#define MEYE_MAX_BUFSIZE 614400 /* 640 * 480 * 2 */ + +/* Maximum number of buffers */ +#define MEYE_MAX_BUFNBRS 32 + +/* State of a buffer */ +#define MEYE_BUF_UNUSED 0 /* not used */ +#define MEYE_BUF_USING 1 /* currently grabbing / playing */ +#define MEYE_BUF_DONE 2 /* done */ + +/* grab buffer */ +struct meye_grab_buffer { + int state; /* state of buffer */ + unsigned long size; /* size of jpg frame */ + u64 ts; /* timestamp */ + unsigned long sequence; /* sequence number */ +}; + +/* size of kfifos containing buffer indices */ +#define MEYE_QUEUE_SIZE MEYE_MAX_BUFNBRS + +/* Motion Eye device structure */ +struct meye { + struct v4l2_device v4l2_dev; /* Main v4l2_device struct */ + struct v4l2_ctrl_handler hdl; + struct pci_dev *mchip_dev; /* pci device */ + u8 mchip_irq; /* irq */ + u8 mchip_mode; /* actual mchip mode: HIC_MODE... */ + u8 mchip_fnum; /* current mchip frame number */ + unsigned char __iomem *mchip_mmregs;/* mchip: memory mapped registers */ + u8 *mchip_ptable[MCHIP_NB_PAGES];/* mchip: ptable */ + void *mchip_ptable_toc; /* mchip: ptable toc */ + dma_addr_t mchip_dmahandle; /* mchip: dma handle to ptable toc */ + unsigned char *grab_fbuffer; /* capture framebuffer */ + unsigned char *grab_temp; /* temporary buffer */ + /* list of buffers */ + struct meye_grab_buffer grab_buffer[MEYE_MAX_BUFNBRS]; + int vma_use_count[MEYE_MAX_BUFNBRS]; /* mmap count */ + struct mutex lock; /* mutex for open/mmap... */ + struct kfifo grabq; /* queue for buffers to be grabbed */ + spinlock_t grabq_lock; /* lock protecting the queue */ + struct kfifo doneq; /* queue for grabbed buffers */ + spinlock_t doneq_lock; /* lock protecting the queue */ + wait_queue_head_t proc_list; /* wait queue */ + struct video_device vdev; /* video device parameters */ + u16 brightness; + u16 hue; + u16 contrast; + u16 colour; + struct meye_params params; /* additional parameters */ + unsigned long in_use; /* set to 1 if the device is in use */ + u8 pm_mchip_mode; /* old mchip mode */ +}; + +#endif diff --git a/drivers/staging/media/deprecated/saa7146/Kconfig b/drivers/staging/media/deprecated/saa7146/Kconfig new file mode 100644 index 000000000000..54154da79f59 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/Kconfig @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 +source "drivers/staging/media/deprecated/saa7146/common/Kconfig" +source "drivers/staging/media/deprecated/saa7146/av7110/Kconfig" +source "drivers/staging/media/deprecated/saa7146/saa7146/Kconfig" +source "drivers/staging/media/deprecated/saa7146/ttpci/Kconfig" diff --git a/drivers/staging/media/deprecated/saa7146/Makefile b/drivers/staging/media/deprecated/saa7146/Makefile new file mode 100644 index 000000000000..68e7aa10c639 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/Makefile @@ -0,0 +1,2 @@ + # SPDX-License-Identifier: GPL-2.0-only +obj-y += common/ av7110/ saa7146/ ttpci/ diff --git a/drivers/staging/media/av7110/Kconfig b/drivers/staging/media/deprecated/saa7146/av7110/Kconfig index 9faf9d2d4001..1571eab31926 100644 --- a/drivers/staging/media/av7110/Kconfig +++ b/drivers/staging/media/deprecated/saa7146/av7110/Kconfig @@ -5,7 +5,7 @@ config DVB_AV7110_IR default DVB_AV7110 config DVB_AV7110 - tristate "AV7110 cards" + tristate "AV7110 cards (DEPRECATED)" depends on DVB_CORE && PCI && I2C select TTPCI_EEPROM select VIDEO_SAA7146_VV @@ -35,10 +35,13 @@ config DVB_AV7110 kernel image by adding the filename to the EXTRA_FIRMWARE configuration option string. + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + Say Y if you own such a card and want to use it. config DVB_AV7110_OSD - bool "AV7110 OSD support" + bool "AV7110 OSD support (DEPRECATED)" depends on DVB_AV7110 default y if DVB_AV7110=y || DVB_AV7110=m help @@ -49,10 +52,13 @@ config DVB_AV7110_OSD Anyway, some popular DVB software like VDR uses this OSD to render its menus, so say Y if you want to use this software. + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + All other people say N. config DVB_BUDGET_PATCH - tristate "AV7110 cards with Budget Patch" + tristate "AV7110 cards with Budget Patch (DEPRECATED)" depends on DVB_BUDGET_CORE && I2C depends on DVB_AV7110 select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT @@ -68,6 +74,9 @@ config DVB_BUDGET_PATCH standard AV7110 driver prior to loading this driver. + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + Say Y if you own such a card and want to use it. To compile this driver as a module, choose M here: the @@ -80,7 +89,7 @@ if DVB_AV7110 # it if we drop support for AV7110, as no other driver will use it. config DVB_SP8870 - tristate "Spase sp8870 based" + tristate "Spase sp8870 based (DEPRECATED)" depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT help @@ -91,4 +100,7 @@ config DVB_SP8870 download/extract it, and then copy it to /usr/lib/hotplug/firmware or /lib/firmware (depending on configuration of firmware hotplug). + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + endif diff --git a/drivers/staging/media/av7110/Makefile b/drivers/staging/media/deprecated/saa7146/av7110/Makefile index 307b267598ea..c04cd0a59109 100644 --- a/drivers/staging/media/av7110/Makefile +++ b/drivers/staging/media/deprecated/saa7146/av7110/Makefile @@ -18,5 +18,6 @@ obj-$(CONFIG_DVB_SP8870) += sp8870.o ccflags-y += -I $(srctree)/drivers/media/dvb-frontends ccflags-y += -I $(srctree)/drivers/media/tuners -ccflags-y += -I $(srctree)/drivers/media/pci/ttpci ccflags-y += -I $(srctree)/drivers/media/common +ccflags-y += -I $(srctree)/drivers/staging/media/deprecated/saa7146/ttpci +ccflags-y += -I $(srctree)/drivers/staging/media/deprecated/saa7146/common diff --git a/drivers/staging/media/deprecated/saa7146/av7110/TODO b/drivers/staging/media/deprecated/saa7146/av7110/TODO new file mode 100644 index 000000000000..38817e04bb67 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/av7110/TODO @@ -0,0 +1,9 @@ +- This driver is too old and relies on a different API. + Drop it from Kernel on a couple of versions. +- Cleanup patches for the drivers here won't be accepted. + +These drivers are now deprecated with the intent of +removing them altogether by the beginning of 2023. + +If someone is interested in doing this work, then contact the +linux-media mailinglist (https://linuxtv.org/lists.php). diff --git a/drivers/staging/media/av7110/audio-bilingual-channel-select.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-bilingual-channel-select.rst index 33b5363317f1..33b5363317f1 100644 --- a/drivers/staging/media/av7110/audio-bilingual-channel-select.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-bilingual-channel-select.rst diff --git a/drivers/staging/media/av7110/audio-channel-select.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-channel-select.rst index 74093df92a68..74093df92a68 100644 --- a/drivers/staging/media/av7110/audio-channel-select.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-channel-select.rst diff --git a/drivers/staging/media/av7110/audio-clear-buffer.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-clear-buffer.rst index a0ebb0278260..a0ebb0278260 100644 --- a/drivers/staging/media/av7110/audio-clear-buffer.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-clear-buffer.rst diff --git a/drivers/staging/media/av7110/audio-continue.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-continue.rst index a2e9850f37f2..a2e9850f37f2 100644 --- a/drivers/staging/media/av7110/audio-continue.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-continue.rst diff --git a/drivers/staging/media/av7110/audio-fclose.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-fclose.rst index 77857d578e83..77857d578e83 100644 --- a/drivers/staging/media/av7110/audio-fclose.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-fclose.rst diff --git a/drivers/staging/media/av7110/audio-fopen.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-fopen.rst index 774daaab3bad..774daaab3bad 100644 --- a/drivers/staging/media/av7110/audio-fopen.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-fopen.rst diff --git a/drivers/staging/media/av7110/audio-fwrite.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-fwrite.rst index 7b096ac2b6c4..7b096ac2b6c4 100644 --- a/drivers/staging/media/av7110/audio-fwrite.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-fwrite.rst diff --git a/drivers/staging/media/av7110/audio-get-capabilities.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-get-capabilities.rst index 6d9eb71dad17..6d9eb71dad17 100644 --- a/drivers/staging/media/av7110/audio-get-capabilities.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-get-capabilities.rst diff --git a/drivers/staging/media/av7110/audio-get-status.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-get-status.rst index 7ae8db2e65e9..7ae8db2e65e9 100644 --- a/drivers/staging/media/av7110/audio-get-status.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-get-status.rst diff --git a/drivers/staging/media/av7110/audio-pause.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-pause.rst index d37d1ddce4df..d37d1ddce4df 100644 --- a/drivers/staging/media/av7110/audio-pause.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-pause.rst diff --git a/drivers/staging/media/av7110/audio-play.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-play.rst index e591930b6ca7..e591930b6ca7 100644 --- a/drivers/staging/media/av7110/audio-play.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-play.rst diff --git a/drivers/staging/media/av7110/audio-select-source.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-select-source.rst index 6a0c0f365eb1..6a0c0f365eb1 100644 --- a/drivers/staging/media/av7110/audio-select-source.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-select-source.rst diff --git a/drivers/staging/media/av7110/audio-set-av-sync.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-set-av-sync.rst index 85a8016bf025..85a8016bf025 100644 --- a/drivers/staging/media/av7110/audio-set-av-sync.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-set-av-sync.rst diff --git a/drivers/staging/media/av7110/audio-set-bypass-mode.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-set-bypass-mode.rst index 80d551a2053a..80d551a2053a 100644 --- a/drivers/staging/media/av7110/audio-set-bypass-mode.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-set-bypass-mode.rst diff --git a/drivers/staging/media/av7110/audio-set-id.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-set-id.rst index 39ad846d412d..39ad846d412d 100644 --- a/drivers/staging/media/av7110/audio-set-id.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-set-id.rst diff --git a/drivers/staging/media/av7110/audio-set-mixer.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-set-mixer.rst index 45dbdf4801e0..45dbdf4801e0 100644 --- a/drivers/staging/media/av7110/audio-set-mixer.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-set-mixer.rst diff --git a/drivers/staging/media/av7110/audio-set-mute.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-set-mute.rst index 987751f92967..987751f92967 100644 --- a/drivers/staging/media/av7110/audio-set-mute.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-set-mute.rst diff --git a/drivers/staging/media/av7110/audio-set-streamtype.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-set-streamtype.rst index 77d73c74882f..77d73c74882f 100644 --- a/drivers/staging/media/av7110/audio-set-streamtype.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-set-streamtype.rst diff --git a/drivers/staging/media/av7110/audio-stop.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio-stop.rst index d77f786fd797..d77f786fd797 100644 --- a/drivers/staging/media/av7110/audio-stop.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio-stop.rst diff --git a/drivers/staging/media/av7110/audio.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio.rst index aa753336b31f..aa753336b31f 100644 --- a/drivers/staging/media/av7110/audio.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio.rst diff --git a/drivers/staging/media/av7110/audio_data_types.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio_data_types.rst index 4744529136a8..4744529136a8 100644 --- a/drivers/staging/media/av7110/audio_data_types.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio_data_types.rst diff --git a/drivers/staging/media/av7110/audio_function_calls.rst b/drivers/staging/media/deprecated/saa7146/av7110/audio_function_calls.rst index fa5ba9539caf..fa5ba9539caf 100644 --- a/drivers/staging/media/av7110/audio_function_calls.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/audio_function_calls.rst diff --git a/drivers/staging/media/av7110/av7110.c b/drivers/staging/media/deprecated/saa7146/av7110/av7110.c index df81a9b744c2..df81a9b744c2 100644 --- a/drivers/staging/media/av7110/av7110.c +++ b/drivers/staging/media/deprecated/saa7146/av7110/av7110.c diff --git a/drivers/staging/media/av7110/av7110.h b/drivers/staging/media/deprecated/saa7146/av7110/av7110.h index 809d938ae166..9fde69b38f1c 100644 --- a/drivers/staging/media/av7110/av7110.h +++ b/drivers/staging/media/deprecated/saa7146/av7110/av7110.h @@ -33,7 +33,7 @@ #include "stv0297.h" #include "l64781.h" -#include <media/drv-intf/saa7146_vv.h> +#include "saa7146_vv.h" #define ANALOG_TUNER_VES1820 1 diff --git a/drivers/staging/media/av7110/av7110_av.c b/drivers/staging/media/deprecated/saa7146/av7110/av7110_av.c index ab7cf496b454..0bf513c26b6b 100644 --- a/drivers/staging/media/av7110/av7110_av.c +++ b/drivers/staging/media/deprecated/saa7146/av7110/av7110_av.c @@ -106,7 +106,7 @@ int av7110_av_start_record(struct av7110 *av7110, int av, int ret = 0; struct dvb_demux *dvbdmx = dvbdmxfeed->demux; - dprintk(2, "av7110:%p, , dvb_demux_feed:%p\n", av7110, dvbdmxfeed); + dprintk(2, "av7110:%p, dvb_demux_feed:%p\n", av7110, dvbdmxfeed); if (av7110->playing || (av7110->rec_mode & av)) return -EBUSY; diff --git a/drivers/staging/media/av7110/av7110_av.h b/drivers/staging/media/deprecated/saa7146/av7110/av7110_av.h index 71bbd4391f57..71bbd4391f57 100644 --- a/drivers/staging/media/av7110/av7110_av.h +++ b/drivers/staging/media/deprecated/saa7146/av7110/av7110_av.h diff --git a/drivers/staging/media/av7110/av7110_ca.c b/drivers/staging/media/deprecated/saa7146/av7110/av7110_ca.c index c1338e074a3d..c1338e074a3d 100644 --- a/drivers/staging/media/av7110/av7110_ca.c +++ b/drivers/staging/media/deprecated/saa7146/av7110/av7110_ca.c diff --git a/drivers/staging/media/av7110/av7110_ca.h b/drivers/staging/media/deprecated/saa7146/av7110/av7110_ca.h index a6e3f2955730..a6e3f2955730 100644 --- a/drivers/staging/media/av7110/av7110_ca.h +++ b/drivers/staging/media/deprecated/saa7146/av7110/av7110_ca.h diff --git a/drivers/staging/media/av7110/av7110_hw.c b/drivers/staging/media/deprecated/saa7146/av7110/av7110_hw.c index 93ca31e38ddd..93ca31e38ddd 100644 --- a/drivers/staging/media/av7110/av7110_hw.c +++ b/drivers/staging/media/deprecated/saa7146/av7110/av7110_hw.c diff --git a/drivers/staging/media/av7110/av7110_hw.h b/drivers/staging/media/deprecated/saa7146/av7110/av7110_hw.h index 6380d8950c69..6380d8950c69 100644 --- a/drivers/staging/media/av7110/av7110_hw.h +++ b/drivers/staging/media/deprecated/saa7146/av7110/av7110_hw.h diff --git a/drivers/staging/media/av7110/av7110_ipack.c b/drivers/staging/media/deprecated/saa7146/av7110/av7110_ipack.c index 30330ed01ce8..30330ed01ce8 100644 --- a/drivers/staging/media/av7110/av7110_ipack.c +++ b/drivers/staging/media/deprecated/saa7146/av7110/av7110_ipack.c diff --git a/drivers/staging/media/av7110/av7110_ipack.h b/drivers/staging/media/deprecated/saa7146/av7110/av7110_ipack.h index 943ec899bb93..943ec899bb93 100644 --- a/drivers/staging/media/av7110/av7110_ipack.h +++ b/drivers/staging/media/deprecated/saa7146/av7110/av7110_ipack.h diff --git a/drivers/staging/media/av7110/av7110_ir.c b/drivers/staging/media/deprecated/saa7146/av7110/av7110_ir.c index a851ba328e4a..a851ba328e4a 100644 --- a/drivers/staging/media/av7110/av7110_ir.c +++ b/drivers/staging/media/deprecated/saa7146/av7110/av7110_ir.c diff --git a/drivers/staging/media/av7110/av7110_v4l.c b/drivers/staging/media/deprecated/saa7146/av7110/av7110_v4l.c index c89f536f699c..c89f536f699c 100644 --- a/drivers/staging/media/av7110/av7110_v4l.c +++ b/drivers/staging/media/deprecated/saa7146/av7110/av7110_v4l.c diff --git a/drivers/staging/media/av7110/budget-patch.c b/drivers/staging/media/deprecated/saa7146/av7110/budget-patch.c index d173c8ade6a7..d173c8ade6a7 100644 --- a/drivers/staging/media/av7110/budget-patch.c +++ b/drivers/staging/media/deprecated/saa7146/av7110/budget-patch.c diff --git a/drivers/staging/media/av7110/dvb_filter.c b/drivers/staging/media/deprecated/saa7146/av7110/dvb_filter.c index 8c2eca5dcdc9..8c2eca5dcdc9 100644 --- a/drivers/staging/media/av7110/dvb_filter.c +++ b/drivers/staging/media/deprecated/saa7146/av7110/dvb_filter.c diff --git a/drivers/staging/media/av7110/dvb_filter.h b/drivers/staging/media/deprecated/saa7146/av7110/dvb_filter.h index 67a3c6333bca..67a3c6333bca 100644 --- a/drivers/staging/media/av7110/dvb_filter.h +++ b/drivers/staging/media/deprecated/saa7146/av7110/dvb_filter.h diff --git a/drivers/staging/media/av7110/sp8870.c b/drivers/staging/media/deprecated/saa7146/av7110/sp8870.c index 9767159aeb9b..9767159aeb9b 100644 --- a/drivers/staging/media/av7110/sp8870.c +++ b/drivers/staging/media/deprecated/saa7146/av7110/sp8870.c diff --git a/drivers/staging/media/av7110/sp8870.h b/drivers/staging/media/deprecated/saa7146/av7110/sp8870.h index 5eacf39f425e..5eacf39f425e 100644 --- a/drivers/staging/media/av7110/sp8870.h +++ b/drivers/staging/media/deprecated/saa7146/av7110/sp8870.h diff --git a/drivers/staging/media/av7110/video-clear-buffer.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-clear-buffer.rst index a7730559bbb2..a7730559bbb2 100644 --- a/drivers/staging/media/av7110/video-clear-buffer.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-clear-buffer.rst diff --git a/drivers/staging/media/av7110/video-command.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-command.rst index cae9445eb3af..cae9445eb3af 100644 --- a/drivers/staging/media/av7110/video-command.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-command.rst diff --git a/drivers/staging/media/av7110/video-continue.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-continue.rst index bc34bf3989e4..bc34bf3989e4 100644 --- a/drivers/staging/media/av7110/video-continue.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-continue.rst diff --git a/drivers/staging/media/av7110/video-fast-forward.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-fast-forward.rst index e71fa8d6965b..e71fa8d6965b 100644 --- a/drivers/staging/media/av7110/video-fast-forward.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-fast-forward.rst diff --git a/drivers/staging/media/av7110/video-fclose.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-fclose.rst index 01d24d548439..01d24d548439 100644 --- a/drivers/staging/media/av7110/video-fclose.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-fclose.rst diff --git a/drivers/staging/media/av7110/video-fopen.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-fopen.rst index 1371b083e4e8..1371b083e4e8 100644 --- a/drivers/staging/media/av7110/video-fopen.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-fopen.rst diff --git a/drivers/staging/media/av7110/video-freeze.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-freeze.rst index 4321f257cb70..4321f257cb70 100644 --- a/drivers/staging/media/av7110/video-freeze.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-freeze.rst diff --git a/drivers/staging/media/av7110/video-fwrite.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-fwrite.rst index a07fd7d7a40e..a07fd7d7a40e 100644 --- a/drivers/staging/media/av7110/video-fwrite.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-fwrite.rst diff --git a/drivers/staging/media/av7110/video-get-capabilities.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-get-capabilities.rst index 01e09f56656c..01e09f56656c 100644 --- a/drivers/staging/media/av7110/video-get-capabilities.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-get-capabilities.rst diff --git a/drivers/staging/media/av7110/video-get-event.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-get-event.rst index 90382bc36cfe..90382bc36cfe 100644 --- a/drivers/staging/media/av7110/video-get-event.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-get-event.rst diff --git a/drivers/staging/media/av7110/video-get-frame-count.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-get-frame-count.rst index b48ac8c58a41..b48ac8c58a41 100644 --- a/drivers/staging/media/av7110/video-get-frame-count.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-get-frame-count.rst diff --git a/drivers/staging/media/av7110/video-get-pts.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-get-pts.rst index fedaff41be0b..fedaff41be0b 100644 --- a/drivers/staging/media/av7110/video-get-pts.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-get-pts.rst diff --git a/drivers/staging/media/av7110/video-get-size.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-get-size.rst index de34331c5bd1..de34331c5bd1 100644 --- a/drivers/staging/media/av7110/video-get-size.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-get-size.rst diff --git a/drivers/staging/media/av7110/video-get-status.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-get-status.rst index 9b86fbf411d4..9b86fbf411d4 100644 --- a/drivers/staging/media/av7110/video-get-status.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-get-status.rst diff --git a/drivers/staging/media/av7110/video-play.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-play.rst index 35ac8b98fdbf..35ac8b98fdbf 100644 --- a/drivers/staging/media/av7110/video-play.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-play.rst diff --git a/drivers/staging/media/av7110/video-select-source.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-select-source.rst index 929a20985d53..929a20985d53 100644 --- a/drivers/staging/media/av7110/video-select-source.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-select-source.rst diff --git a/drivers/staging/media/av7110/video-set-blank.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-set-blank.rst index 70249a6ba125..70249a6ba125 100644 --- a/drivers/staging/media/av7110/video-set-blank.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-set-blank.rst diff --git a/drivers/staging/media/av7110/video-set-display-format.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-set-display-format.rst index 1de4f40ae732..1de4f40ae732 100644 --- a/drivers/staging/media/av7110/video-set-display-format.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-set-display-format.rst diff --git a/drivers/staging/media/av7110/video-set-format.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-set-format.rst index bb64e37ae081..bb64e37ae081 100644 --- a/drivers/staging/media/av7110/video-set-format.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-set-format.rst diff --git a/drivers/staging/media/av7110/video-set-streamtype.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-set-streamtype.rst index 1f31c048bdbc..1f31c048bdbc 100644 --- a/drivers/staging/media/av7110/video-set-streamtype.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-set-streamtype.rst diff --git a/drivers/staging/media/av7110/video-slowmotion.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-slowmotion.rst index 1478fcc30cb8..1478fcc30cb8 100644 --- a/drivers/staging/media/av7110/video-slowmotion.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-slowmotion.rst diff --git a/drivers/staging/media/av7110/video-stillpicture.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-stillpicture.rst index d25384222a20..d25384222a20 100644 --- a/drivers/staging/media/av7110/video-stillpicture.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-stillpicture.rst diff --git a/drivers/staging/media/av7110/video-stop.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-stop.rst index 96f61c5b48a2..96f61c5b48a2 100644 --- a/drivers/staging/media/av7110/video-stop.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-stop.rst diff --git a/drivers/staging/media/av7110/video-try-command.rst b/drivers/staging/media/deprecated/saa7146/av7110/video-try-command.rst index 79bf3dfb8a32..79bf3dfb8a32 100644 --- a/drivers/staging/media/av7110/video-try-command.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/video-try-command.rst diff --git a/drivers/staging/media/av7110/video.rst b/drivers/staging/media/deprecated/saa7146/av7110/video.rst index 808705b769a1..808705b769a1 100644 --- a/drivers/staging/media/av7110/video.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/video.rst diff --git a/drivers/staging/media/av7110/video_function_calls.rst b/drivers/staging/media/deprecated/saa7146/av7110/video_function_calls.rst index 20a897be5dca..20a897be5dca 100644 --- a/drivers/staging/media/av7110/video_function_calls.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/video_function_calls.rst diff --git a/drivers/staging/media/av7110/video_types.rst b/drivers/staging/media/deprecated/saa7146/av7110/video_types.rst index c4557d328b7a..c4557d328b7a 100644 --- a/drivers/staging/media/av7110/video_types.rst +++ b/drivers/staging/media/deprecated/saa7146/av7110/video_types.rst diff --git a/drivers/staging/media/deprecated/saa7146/common/Kconfig b/drivers/staging/media/deprecated/saa7146/common/Kconfig new file mode 100644 index 000000000000..a0aa155e5d85 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/common/Kconfig @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_SAA7146 + tristate + depends on I2C && PCI + +config VIDEO_SAA7146_VV + tristate + depends on VIDEO_DEV + select VIDEOBUF_DMA_SG + select VIDEO_SAA7146 diff --git a/drivers/staging/media/deprecated/saa7146/common/Makefile b/drivers/staging/media/deprecated/saa7146/common/Makefile new file mode 100644 index 000000000000..2a6337feaec8 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/common/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only +saa7146-objs := saa7146_i2c.o saa7146_core.o +saa7146_vv-objs := saa7146_fops.o saa7146_video.o saa7146_hlp.o saa7146_vbi.o + +obj-$(CONFIG_VIDEO_SAA7146) += saa7146.o +obj-$(CONFIG_VIDEO_SAA7146_VV) += saa7146_vv.o diff --git a/drivers/staging/media/deprecated/saa7146/common/saa7146.h b/drivers/staging/media/deprecated/saa7146/common/saa7146.h new file mode 100644 index 000000000000..71ce63c99cb4 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/common/saa7146.h @@ -0,0 +1,472 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __SAA7146__ +#define __SAA7146__ + +#include <linux/delay.h> /* for delay-stuff */ +#include <linux/slab.h> /* for kmalloc/kfree */ +#include <linux/pci.h> /* for pci-config-stuff, vendor ids etc. */ +#include <linux/init.h> /* for "__init" */ +#include <linux/interrupt.h> /* for IMMEDIATE_BH */ +#include <linux/kmod.h> /* for kernel module loader */ +#include <linux/i2c.h> /* for i2c subsystem */ +#include <asm/io.h> /* for accessing devices */ +#include <linux/stringify.h> +#include <linux/mutex.h> +#include <linux/scatterlist.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> + +#include <linux/vmalloc.h> /* for vmalloc() */ +#include <linux/mm.h> /* for vmalloc_to_page() */ + +#define saa7146_write(sxy,adr,dat) writel((dat),(sxy->mem+(adr))) +#define saa7146_read(sxy,adr) readl(sxy->mem+(adr)) + +extern unsigned int saa7146_debug; + +#ifndef DEBUG_VARIABLE + #define DEBUG_VARIABLE saa7146_debug +#endif + +#define ERR(fmt, ...) pr_err("%s: " fmt, __func__, ##__VA_ARGS__) + +#define _DBG(mask, fmt, ...) \ +do { \ + if (DEBUG_VARIABLE & mask) \ + pr_debug("%s(): " fmt, __func__, ##__VA_ARGS__); \ +} while (0) + +/* simple debug messages */ +#define DEB_S(fmt, ...) _DBG(0x01, fmt, ##__VA_ARGS__) +/* more detailed debug messages */ +#define DEB_D(fmt, ...) _DBG(0x02, fmt, ##__VA_ARGS__) +/* print enter and exit of functions */ +#define DEB_EE(fmt, ...) _DBG(0x04, fmt, ##__VA_ARGS__) +/* i2c debug messages */ +#define DEB_I2C(fmt, ...) _DBG(0x08, fmt, ##__VA_ARGS__) +/* vbi debug messages */ +#define DEB_VBI(fmt, ...) _DBG(0x10, fmt, ##__VA_ARGS__) +/* interrupt debug messages */ +#define DEB_INT(fmt, ...) _DBG(0x20, fmt, ##__VA_ARGS__) +/* capture debug messages */ +#define DEB_CAP(fmt, ...) _DBG(0x40, fmt, ##__VA_ARGS__) + +#define SAA7146_ISR_CLEAR(x,y) \ + saa7146_write(x, ISR, (y)); + +struct module; + +struct saa7146_dev; +struct saa7146_extension; +struct saa7146_vv; + +/* saa7146 page table */ +struct saa7146_pgtable { + unsigned int size; + __le32 *cpu; + dma_addr_t dma; + /* used for offsets for u,v planes for planar capture modes */ + unsigned long offset; + /* used for custom pagetables (used for example by budget dvb cards) */ + struct scatterlist *slist; + int nents; +}; + +struct saa7146_pci_extension_data { + struct saa7146_extension *ext; + void *ext_priv; /* most likely a name string */ +}; + +#define MAKE_EXTENSION_PCI(x_var, x_vendor, x_device) \ + { \ + .vendor = PCI_VENDOR_ID_PHILIPS, \ + .device = PCI_DEVICE_ID_PHILIPS_SAA7146, \ + .subvendor = x_vendor, \ + .subdevice = x_device, \ + .driver_data = (unsigned long)& x_var, \ + } + +struct saa7146_extension +{ + char name[32]; /* name of the device */ +#define SAA7146_USE_I2C_IRQ 0x1 +#define SAA7146_I2C_SHORT_DELAY 0x2 + int flags; + + /* pairs of subvendor and subdevice ids for + supported devices, last entry 0xffff, 0xfff */ + struct module *module; + struct pci_driver driver; + const struct pci_device_id *pci_tbl; + + /* extension functions */ + int (*probe)(struct saa7146_dev *); + int (*attach)(struct saa7146_dev *, struct saa7146_pci_extension_data *); + int (*detach)(struct saa7146_dev*); + + u32 irq_mask; /* mask to indicate, which irq-events are handled by the extension */ + void (*irq_func)(struct saa7146_dev*, u32* irq_mask); +}; + +struct saa7146_dma +{ + dma_addr_t dma_handle; + __le32 *cpu_addr; +}; + +struct saa7146_dev +{ + struct module *module; + + struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler ctrl_handler; + + /* different device locks */ + spinlock_t slock; + struct mutex v4l2_lock; + + unsigned char __iomem *mem; /* pointer to mapped IO memory */ + u32 revision; /* chip revision; needed for bug-workarounds*/ + + /* pci-device & irq stuff*/ + char name[32]; + struct pci_dev *pci; + u32 int_todo; + spinlock_t int_slock; + + /* extension handling */ + struct saa7146_extension *ext; /* indicates if handled by extension */ + void *ext_priv; /* pointer for extension private use (most likely some private data) */ + struct saa7146_ext_vv *ext_vv_data; + + /* per device video/vbi information (if available) */ + struct saa7146_vv *vv_data; + void (*vv_callback)(struct saa7146_dev *dev, unsigned long status); + + /* i2c-stuff */ + struct mutex i2c_lock; + + u32 i2c_bitrate; + struct saa7146_dma d_i2c; /* pointer to i2c memory */ + wait_queue_head_t i2c_wq; + int i2c_op; + + /* memories */ + struct saa7146_dma d_rps0; + struct saa7146_dma d_rps1; +}; + +static inline struct saa7146_dev *to_saa7146_dev(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct saa7146_dev, v4l2_dev); +} + +/* from saa7146_i2c.c */ +int saa7146_i2c_adapter_prepare(struct saa7146_dev *dev, struct i2c_adapter *i2c_adapter, u32 bitrate); + +/* from saa7146_core.c */ +int saa7146_register_extension(struct saa7146_extension*); +int saa7146_unregister_extension(struct saa7146_extension*); +struct saa7146_format* saa7146_format_by_fourcc(struct saa7146_dev *dev, int fourcc); +int saa7146_pgtable_alloc(struct pci_dev *pci, struct saa7146_pgtable *pt); +void saa7146_pgtable_free(struct pci_dev *pci, struct saa7146_pgtable *pt); +int saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt, struct scatterlist *list, int length ); +void *saa7146_vmalloc_build_pgtable(struct pci_dev *pci, long length, struct saa7146_pgtable *pt); +void saa7146_vfree_destroy_pgtable(struct pci_dev *pci, void *mem, struct saa7146_pgtable *pt); +void saa7146_setgpio(struct saa7146_dev *dev, int port, u32 data); +int saa7146_wait_for_debi_done(struct saa7146_dev *dev, int nobusyloop); + +/* some memory sizes */ +#define SAA7146_I2C_MEM ( 1*PAGE_SIZE) +#define SAA7146_RPS_MEM ( 1*PAGE_SIZE) + +/* some i2c constants */ +#define SAA7146_I2C_TIMEOUT 100 /* i2c-timeout-value in ms */ +#define SAA7146_I2C_RETRIES 3 /* how many times shall we retry an i2c-operation? */ +#define SAA7146_I2C_DELAY 5 /* time we wait after certain i2c-operations */ + +/* unsorted defines */ +#define ME1 0x0000000800 +#define PV1 0x0000000008 + +/* gpio defines */ +#define SAA7146_GPIO_INPUT 0x00 +#define SAA7146_GPIO_IRQHI 0x10 +#define SAA7146_GPIO_IRQLO 0x20 +#define SAA7146_GPIO_IRQHL 0x30 +#define SAA7146_GPIO_OUTLO 0x40 +#define SAA7146_GPIO_OUTHI 0x50 + +/* debi defines */ +#define DEBINOSWAP 0x000e0000 + +/* define for the register programming sequencer (rps) */ +#define CMD_NOP 0x00000000 /* No operation */ +#define CMD_CLR_EVENT 0x00000000 /* Clear event */ +#define CMD_SET_EVENT 0x10000000 /* Set signal event */ +#define CMD_PAUSE 0x20000000 /* Pause */ +#define CMD_CHECK_LATE 0x30000000 /* Check late */ +#define CMD_UPLOAD 0x40000000 /* Upload */ +#define CMD_STOP 0x50000000 /* Stop */ +#define CMD_INTERRUPT 0x60000000 /* Interrupt */ +#define CMD_JUMP 0x80000000 /* Jump */ +#define CMD_WR_REG 0x90000000 /* Write (load) register */ +#define CMD_RD_REG 0xa0000000 /* Read (store) register */ +#define CMD_WR_REG_MASK 0xc0000000 /* Write register with mask */ + +#define CMD_OAN MASK_27 +#define CMD_INV MASK_26 +#define CMD_SIG4 MASK_25 +#define CMD_SIG3 MASK_24 +#define CMD_SIG2 MASK_23 +#define CMD_SIG1 MASK_22 +#define CMD_SIG0 MASK_21 +#define CMD_O_FID_B MASK_14 +#define CMD_E_FID_B MASK_13 +#define CMD_O_FID_A MASK_12 +#define CMD_E_FID_A MASK_11 + +/* some events and command modifiers for rps1 squarewave generator */ +#define EVT_HS (1<<15) // Source Line Threshold reached +#define EVT_VBI_B (1<<9) // VSYNC Event +#define RPS_OAN (1<<27) // 1: OR events, 0: AND events +#define RPS_INV (1<<26) // Invert (compound) event +#define GPIO3_MSK 0xFF000000 // GPIO #3 control bits + +/* Bit mask constants */ +#define MASK_00 0x00000001 /* Mask value for bit 0 */ +#define MASK_01 0x00000002 /* Mask value for bit 1 */ +#define MASK_02 0x00000004 /* Mask value for bit 2 */ +#define MASK_03 0x00000008 /* Mask value for bit 3 */ +#define MASK_04 0x00000010 /* Mask value for bit 4 */ +#define MASK_05 0x00000020 /* Mask value for bit 5 */ +#define MASK_06 0x00000040 /* Mask value for bit 6 */ +#define MASK_07 0x00000080 /* Mask value for bit 7 */ +#define MASK_08 0x00000100 /* Mask value for bit 8 */ +#define MASK_09 0x00000200 /* Mask value for bit 9 */ +#define MASK_10 0x00000400 /* Mask value for bit 10 */ +#define MASK_11 0x00000800 /* Mask value for bit 11 */ +#define MASK_12 0x00001000 /* Mask value for bit 12 */ +#define MASK_13 0x00002000 /* Mask value for bit 13 */ +#define MASK_14 0x00004000 /* Mask value for bit 14 */ +#define MASK_15 0x00008000 /* Mask value for bit 15 */ +#define MASK_16 0x00010000 /* Mask value for bit 16 */ +#define MASK_17 0x00020000 /* Mask value for bit 17 */ +#define MASK_18 0x00040000 /* Mask value for bit 18 */ +#define MASK_19 0x00080000 /* Mask value for bit 19 */ +#define MASK_20 0x00100000 /* Mask value for bit 20 */ +#define MASK_21 0x00200000 /* Mask value for bit 21 */ +#define MASK_22 0x00400000 /* Mask value for bit 22 */ +#define MASK_23 0x00800000 /* Mask value for bit 23 */ +#define MASK_24 0x01000000 /* Mask value for bit 24 */ +#define MASK_25 0x02000000 /* Mask value for bit 25 */ +#define MASK_26 0x04000000 /* Mask value for bit 26 */ +#define MASK_27 0x08000000 /* Mask value for bit 27 */ +#define MASK_28 0x10000000 /* Mask value for bit 28 */ +#define MASK_29 0x20000000 /* Mask value for bit 29 */ +#define MASK_30 0x40000000 /* Mask value for bit 30 */ +#define MASK_31 0x80000000 /* Mask value for bit 31 */ + +#define MASK_B0 0x000000ff /* Mask value for byte 0 */ +#define MASK_B1 0x0000ff00 /* Mask value for byte 1 */ +#define MASK_B2 0x00ff0000 /* Mask value for byte 2 */ +#define MASK_B3 0xff000000 /* Mask value for byte 3 */ + +#define MASK_W0 0x0000ffff /* Mask value for word 0 */ +#define MASK_W1 0xffff0000 /* Mask value for word 1 */ + +#define MASK_PA 0xfffffffc /* Mask value for physical address */ +#define MASK_PR 0xfffffffe /* Mask value for protection register */ +#define MASK_ER 0xffffffff /* Mask value for the entire register */ + +#define MASK_NONE 0x00000000 /* No mask */ + +/* register aliases */ +#define BASE_ODD1 0x00 /* Video DMA 1 registers */ +#define BASE_EVEN1 0x04 +#define PROT_ADDR1 0x08 +#define PITCH1 0x0C +#define BASE_PAGE1 0x10 /* Video DMA 1 base page */ +#define NUM_LINE_BYTE1 0x14 + +#define BASE_ODD2 0x18 /* Video DMA 2 registers */ +#define BASE_EVEN2 0x1C +#define PROT_ADDR2 0x20 +#define PITCH2 0x24 +#define BASE_PAGE2 0x28 /* Video DMA 2 base page */ +#define NUM_LINE_BYTE2 0x2C + +#define BASE_ODD3 0x30 /* Video DMA 3 registers */ +#define BASE_EVEN3 0x34 +#define PROT_ADDR3 0x38 +#define PITCH3 0x3C +#define BASE_PAGE3 0x40 /* Video DMA 3 base page */ +#define NUM_LINE_BYTE3 0x44 + +#define PCI_BT_V1 0x48 /* Video/FIFO 1 */ +#define PCI_BT_V2 0x49 /* Video/FIFO 2 */ +#define PCI_BT_V3 0x4A /* Video/FIFO 3 */ +#define PCI_BT_DEBI 0x4B /* DEBI */ +#define PCI_BT_A 0x4C /* Audio */ + +#define DD1_INIT 0x50 /* Init setting of DD1 interface */ + +#define DD1_STREAM_B 0x54 /* DD1 B video data stream handling */ +#define DD1_STREAM_A 0x56 /* DD1 A video data stream handling */ + +#define BRS_CTRL 0x58 /* BRS control register */ +#define HPS_CTRL 0x5C /* HPS control register */ +#define HPS_V_SCALE 0x60 /* HPS vertical scale */ +#define HPS_V_GAIN 0x64 /* HPS vertical ACL and gain */ +#define HPS_H_PRESCALE 0x68 /* HPS horizontal prescale */ +#define HPS_H_SCALE 0x6C /* HPS horizontal scale */ +#define BCS_CTRL 0x70 /* BCS control */ +#define CHROMA_KEY_RANGE 0x74 +#define CLIP_FORMAT_CTRL 0x78 /* HPS outputs formats & clipping */ + +#define DEBI_CONFIG 0x7C +#define DEBI_COMMAND 0x80 +#define DEBI_PAGE 0x84 +#define DEBI_AD 0x88 + +#define I2C_TRANSFER 0x8C +#define I2C_STATUS 0x90 + +#define BASE_A1_IN 0x94 /* Audio 1 input DMA */ +#define PROT_A1_IN 0x98 +#define PAGE_A1_IN 0x9C + +#define BASE_A1_OUT 0xA0 /* Audio 1 output DMA */ +#define PROT_A1_OUT 0xA4 +#define PAGE_A1_OUT 0xA8 + +#define BASE_A2_IN 0xAC /* Audio 2 input DMA */ +#define PROT_A2_IN 0xB0 +#define PAGE_A2_IN 0xB4 + +#define BASE_A2_OUT 0xB8 /* Audio 2 output DMA */ +#define PROT_A2_OUT 0xBC +#define PAGE_A2_OUT 0xC0 + +#define RPS_PAGE0 0xC4 /* RPS task 0 page register */ +#define RPS_PAGE1 0xC8 /* RPS task 1 page register */ + +#define RPS_THRESH0 0xCC /* HBI threshold for task 0 */ +#define RPS_THRESH1 0xD0 /* HBI threshold for task 1 */ + +#define RPS_TOV0 0xD4 /* RPS timeout for task 0 */ +#define RPS_TOV1 0xD8 /* RPS timeout for task 1 */ + +#define IER 0xDC /* Interrupt enable register */ + +#define GPIO_CTRL 0xE0 /* GPIO 0-3 register */ + +#define EC1SSR 0xE4 /* Event cnt set 1 source select */ +#define EC2SSR 0xE8 /* Event cnt set 2 source select */ +#define ECT1R 0xEC /* Event cnt set 1 thresholds */ +#define ECT2R 0xF0 /* Event cnt set 2 thresholds */ + +#define ACON1 0xF4 +#define ACON2 0xF8 + +#define MC1 0xFC /* Main control register 1 */ +#define MC2 0x100 /* Main control register 2 */ + +#define RPS_ADDR0 0x104 /* RPS task 0 address register */ +#define RPS_ADDR1 0x108 /* RPS task 1 address register */ + +#define ISR 0x10C /* Interrupt status register */ +#define PSR 0x110 /* Primary status register */ +#define SSR 0x114 /* Secondary status register */ + +#define EC1R 0x118 /* Event counter set 1 register */ +#define EC2R 0x11C /* Event counter set 2 register */ + +#define PCI_VDP1 0x120 /* Video DMA pointer of FIFO 1 */ +#define PCI_VDP2 0x124 /* Video DMA pointer of FIFO 2 */ +#define PCI_VDP3 0x128 /* Video DMA pointer of FIFO 3 */ +#define PCI_ADP1 0x12C /* Audio DMA pointer of audio out 1 */ +#define PCI_ADP2 0x130 /* Audio DMA pointer of audio in 1 */ +#define PCI_ADP3 0x134 /* Audio DMA pointer of audio out 2 */ +#define PCI_ADP4 0x138 /* Audio DMA pointer of audio in 2 */ +#define PCI_DMA_DDP 0x13C /* DEBI DMA pointer */ + +#define LEVEL_REP 0x140, +#define A_TIME_SLOT1 0x180, /* from 180 - 1BC */ +#define A_TIME_SLOT2 0x1C0, /* from 1C0 - 1FC */ + +/* isr masks */ +#define SPCI_PPEF 0x80000000 /* PCI parity error */ +#define SPCI_PABO 0x40000000 /* PCI access error (target or master abort) */ +#define SPCI_PPED 0x20000000 /* PCI parity error on 'real time data' */ +#define SPCI_RPS_I1 0x10000000 /* Interrupt issued by RPS1 */ +#define SPCI_RPS_I0 0x08000000 /* Interrupt issued by RPS0 */ +#define SPCI_RPS_LATE1 0x04000000 /* RPS task 1 is late */ +#define SPCI_RPS_LATE0 0x02000000 /* RPS task 0 is late */ +#define SPCI_RPS_E1 0x01000000 /* RPS error from task 1 */ +#define SPCI_RPS_E0 0x00800000 /* RPS error from task 0 */ +#define SPCI_RPS_TO1 0x00400000 /* RPS timeout task 1 */ +#define SPCI_RPS_TO0 0x00200000 /* RPS timeout task 0 */ +#define SPCI_UPLD 0x00100000 /* RPS in upload */ +#define SPCI_DEBI_S 0x00080000 /* DEBI status */ +#define SPCI_DEBI_E 0x00040000 /* DEBI error */ +#define SPCI_IIC_S 0x00020000 /* I2C status */ +#define SPCI_IIC_E 0x00010000 /* I2C error */ +#define SPCI_A2_IN 0x00008000 /* Audio 2 input DMA protection / limit */ +#define SPCI_A2_OUT 0x00004000 /* Audio 2 output DMA protection / limit */ +#define SPCI_A1_IN 0x00002000 /* Audio 1 input DMA protection / limit */ +#define SPCI_A1_OUT 0x00001000 /* Audio 1 output DMA protection / limit */ +#define SPCI_AFOU 0x00000800 /* Audio FIFO over- / underflow */ +#define SPCI_V_PE 0x00000400 /* Video protection address */ +#define SPCI_VFOU 0x00000200 /* Video FIFO over- / underflow */ +#define SPCI_FIDA 0x00000100 /* Field ID video port A */ +#define SPCI_FIDB 0x00000080 /* Field ID video port B */ +#define SPCI_PIN3 0x00000040 /* GPIO pin 3 */ +#define SPCI_PIN2 0x00000020 /* GPIO pin 2 */ +#define SPCI_PIN1 0x00000010 /* GPIO pin 1 */ +#define SPCI_PIN0 0x00000008 /* GPIO pin 0 */ +#define SPCI_ECS 0x00000004 /* Event counter 1, 2, 4, 5 */ +#define SPCI_EC3S 0x00000002 /* Event counter 3 */ +#define SPCI_EC0S 0x00000001 /* Event counter 0 */ + +/* i2c */ +#define SAA7146_I2C_ABORT (1<<7) +#define SAA7146_I2C_SPERR (1<<6) +#define SAA7146_I2C_APERR (1<<5) +#define SAA7146_I2C_DTERR (1<<4) +#define SAA7146_I2C_DRERR (1<<3) +#define SAA7146_I2C_AL (1<<2) +#define SAA7146_I2C_ERR (1<<1) +#define SAA7146_I2C_BUSY (1<<0) + +#define SAA7146_I2C_START (0x3) +#define SAA7146_I2C_CONT (0x2) +#define SAA7146_I2C_STOP (0x1) +#define SAA7146_I2C_NOP (0x0) + +#define SAA7146_I2C_BUS_BIT_RATE_6400 (0x500) +#define SAA7146_I2C_BUS_BIT_RATE_3200 (0x100) +#define SAA7146_I2C_BUS_BIT_RATE_480 (0x400) +#define SAA7146_I2C_BUS_BIT_RATE_320 (0x600) +#define SAA7146_I2C_BUS_BIT_RATE_240 (0x700) +#define SAA7146_I2C_BUS_BIT_RATE_120 (0x000) +#define SAA7146_I2C_BUS_BIT_RATE_80 (0x200) +#define SAA7146_I2C_BUS_BIT_RATE_60 (0x300) + +static inline void SAA7146_IER_DISABLE(struct saa7146_dev *x, unsigned y) +{ + unsigned long flags; + spin_lock_irqsave(&x->int_slock, flags); + saa7146_write(x, IER, saa7146_read(x, IER) & ~y); + spin_unlock_irqrestore(&x->int_slock, flags); +} + +static inline void SAA7146_IER_ENABLE(struct saa7146_dev *x, unsigned y) +{ + unsigned long flags; + spin_lock_irqsave(&x->int_slock, flags); + saa7146_write(x, IER, saa7146_read(x, IER) | y); + spin_unlock_irqrestore(&x->int_slock, flags); +} + +#endif diff --git a/drivers/staging/media/deprecated/saa7146/common/saa7146_core.c b/drivers/staging/media/deprecated/saa7146/common/saa7146_core.c new file mode 100644 index 000000000000..da21d346b870 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/common/saa7146_core.c @@ -0,0 +1,578 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + saa7146.o - driver for generic saa7146-based hardware + + Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de> + +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include "saa7146.h" + +static int saa7146_num; + +unsigned int saa7146_debug; + +module_param(saa7146_debug, uint, 0644); +MODULE_PARM_DESC(saa7146_debug, "debug level (default: 0)"); + +#if 0 +static void dump_registers(struct saa7146_dev* dev) +{ + int i = 0; + + pr_info(" @ %li jiffies:\n", jiffies); + for (i = 0; i <= 0x148; i += 4) + pr_info("0x%03x: 0x%08x\n", i, saa7146_read(dev, i)); +} +#endif + +/**************************************************************************** + * gpio and debi helper functions + ****************************************************************************/ + +void saa7146_setgpio(struct saa7146_dev *dev, int port, u32 data) +{ + u32 value = 0; + + BUG_ON(port > 3); + + value = saa7146_read(dev, GPIO_CTRL); + value &= ~(0xff << (8*port)); + value |= (data << (8*port)); + saa7146_write(dev, GPIO_CTRL, value); +} + +/* This DEBI code is based on the saa7146 Stradis driver by Nathan Laredo */ +static inline int saa7146_wait_for_debi_done_sleep(struct saa7146_dev *dev, + unsigned long us1, unsigned long us2) +{ + unsigned long timeout; + int err; + + /* wait for registers to be programmed */ + timeout = jiffies + usecs_to_jiffies(us1); + while (1) { + err = time_after(jiffies, timeout); + if (saa7146_read(dev, MC2) & 2) + break; + if (err) { + pr_debug("%s: %s timed out while waiting for registers getting programmed\n", + dev->name, __func__); + return -ETIMEDOUT; + } + msleep(1); + } + + /* wait for transfer to complete */ + timeout = jiffies + usecs_to_jiffies(us2); + while (1) { + err = time_after(jiffies, timeout); + if (!(saa7146_read(dev, PSR) & SPCI_DEBI_S)) + break; + saa7146_read(dev, MC2); + if (err) { + DEB_S("%s: %s timed out while waiting for transfer completion\n", + dev->name, __func__); + return -ETIMEDOUT; + } + msleep(1); + } + + return 0; +} + +static inline int saa7146_wait_for_debi_done_busyloop(struct saa7146_dev *dev, + unsigned long us1, unsigned long us2) +{ + unsigned long loops; + + /* wait for registers to be programmed */ + loops = us1; + while (1) { + if (saa7146_read(dev, MC2) & 2) + break; + if (!loops--) { + pr_err("%s: %s timed out while waiting for registers getting programmed\n", + dev->name, __func__); + return -ETIMEDOUT; + } + udelay(1); + } + + /* wait for transfer to complete */ + loops = us2 / 5; + while (1) { + if (!(saa7146_read(dev, PSR) & SPCI_DEBI_S)) + break; + saa7146_read(dev, MC2); + if (!loops--) { + DEB_S("%s: %s timed out while waiting for transfer completion\n", + dev->name, __func__); + return -ETIMEDOUT; + } + udelay(5); + } + + return 0; +} + +int saa7146_wait_for_debi_done(struct saa7146_dev *dev, int nobusyloop) +{ + if (nobusyloop) + return saa7146_wait_for_debi_done_sleep(dev, 50000, 250000); + else + return saa7146_wait_for_debi_done_busyloop(dev, 50000, 250000); +} + +/**************************************************************************** + * general helper functions + ****************************************************************************/ + +/* this is videobuf_vmalloc_to_sg() from videobuf-dma-sg.c + make sure virt has been allocated with vmalloc_32(), otherwise the BUG() + may be triggered on highmem machines */ +static struct scatterlist* vmalloc_to_sg(unsigned char *virt, int nr_pages) +{ + struct scatterlist *sglist; + struct page *pg; + int i; + + sglist = kmalloc_array(nr_pages, sizeof(struct scatterlist), GFP_KERNEL); + if (NULL == sglist) + return NULL; + sg_init_table(sglist, nr_pages); + for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) { + pg = vmalloc_to_page(virt); + if (NULL == pg) + goto err; + BUG_ON(PageHighMem(pg)); + sg_set_page(&sglist[i], pg, PAGE_SIZE, 0); + } + return sglist; + + err: + kfree(sglist); + return NULL; +} + +/********************************************************************************/ +/* common page table functions */ + +void *saa7146_vmalloc_build_pgtable(struct pci_dev *pci, long length, struct saa7146_pgtable *pt) +{ + int pages = (length+PAGE_SIZE-1)/PAGE_SIZE; + void *mem = vmalloc_32(length); + int slen = 0; + + if (NULL == mem) + goto err_null; + + if (!(pt->slist = vmalloc_to_sg(mem, pages))) + goto err_free_mem; + + if (saa7146_pgtable_alloc(pci, pt)) + goto err_free_slist; + + pt->nents = pages; + slen = dma_map_sg(&pci->dev, pt->slist, pt->nents, DMA_FROM_DEVICE); + if (0 == slen) + goto err_free_pgtable; + + if (0 != saa7146_pgtable_build_single(pci, pt, pt->slist, slen)) + goto err_unmap_sg; + + return mem; + +err_unmap_sg: + dma_unmap_sg(&pci->dev, pt->slist, pt->nents, DMA_FROM_DEVICE); +err_free_pgtable: + saa7146_pgtable_free(pci, pt); +err_free_slist: + kfree(pt->slist); + pt->slist = NULL; +err_free_mem: + vfree(mem); +err_null: + return NULL; +} + +void saa7146_vfree_destroy_pgtable(struct pci_dev *pci, void *mem, struct saa7146_pgtable *pt) +{ + dma_unmap_sg(&pci->dev, pt->slist, pt->nents, DMA_FROM_DEVICE); + saa7146_pgtable_free(pci, pt); + kfree(pt->slist); + pt->slist = NULL; + vfree(mem); +} + +void saa7146_pgtable_free(struct pci_dev *pci, struct saa7146_pgtable *pt) +{ + if (NULL == pt->cpu) + return; + dma_free_coherent(&pci->dev, pt->size, pt->cpu, pt->dma); + pt->cpu = NULL; +} + +int saa7146_pgtable_alloc(struct pci_dev *pci, struct saa7146_pgtable *pt) +{ + __le32 *cpu; + dma_addr_t dma_addr = 0; + + cpu = dma_alloc_coherent(&pci->dev, PAGE_SIZE, &dma_addr, GFP_KERNEL); + if (NULL == cpu) { + return -ENOMEM; + } + pt->size = PAGE_SIZE; + pt->cpu = cpu; + pt->dma = dma_addr; + + return 0; +} + +int saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt, + struct scatterlist *list, int sglen ) +{ + __le32 *ptr, fill; + int nr_pages = 0; + int i,p; + + BUG_ON(0 == sglen); + BUG_ON(list->offset > PAGE_SIZE); + + /* if we have a user buffer, the first page may not be + aligned to a page boundary. */ + pt->offset = list->offset; + + ptr = pt->cpu; + for (i = 0; i < sglen; i++, list++) { +/* + pr_debug("i:%d, adr:0x%08x, len:%d, offset:%d\n", + i, sg_dma_address(list), sg_dma_len(list), + list->offset); +*/ + for (p = 0; p * 4096 < sg_dma_len(list); p++, ptr++) { + *ptr = cpu_to_le32(sg_dma_address(list) + p * 4096); + nr_pages++; + } + } + + + /* safety; fill the page table up with the last valid page */ + fill = *(ptr-1); + for(i=nr_pages;i<1024;i++) { + *ptr++ = fill; + } + +/* + ptr = pt->cpu; + pr_debug("offset: %d\n", pt->offset); + for(i=0;i<5;i++) { + pr_debug("ptr1 %d: 0x%08x\n", i, ptr[i]); + } +*/ + return 0; +} + +/********************************************************************************/ +/* interrupt handler */ +static irqreturn_t interrupt_hw(int irq, void *dev_id) +{ + struct saa7146_dev *dev = dev_id; + u32 isr; + u32 ack_isr; + + /* read out the interrupt status register */ + ack_isr = isr = saa7146_read(dev, ISR); + + /* is this our interrupt? */ + if ( 0 == isr ) { + /* nope, some other device */ + return IRQ_NONE; + } + + if (dev->ext) { + if (dev->ext->irq_mask & isr) { + if (dev->ext->irq_func) + dev->ext->irq_func(dev, &isr); + isr &= ~dev->ext->irq_mask; + } + } + if (0 != (isr & (MASK_27))) { + DEB_INT("irq: RPS0 (0x%08x)\n", isr); + if (dev->vv_data && dev->vv_callback) + dev->vv_callback(dev,isr); + isr &= ~MASK_27; + } + if (0 != (isr & (MASK_28))) { + if (dev->vv_data && dev->vv_callback) + dev->vv_callback(dev,isr); + isr &= ~MASK_28; + } + if (0 != (isr & (MASK_16|MASK_17))) { + SAA7146_IER_DISABLE(dev, MASK_16|MASK_17); + /* only wake up if we expect something */ + if (0 != dev->i2c_op) { + dev->i2c_op = 0; + wake_up(&dev->i2c_wq); + } else { + u32 psr = saa7146_read(dev, PSR); + u32 ssr = saa7146_read(dev, SSR); + pr_warn("%s: unexpected i2c irq: isr %08x psr %08x ssr %08x\n", + dev->name, isr, psr, ssr); + } + isr &= ~(MASK_16|MASK_17); + } + if( 0 != isr ) { + ERR("warning: interrupt enabled, but not handled properly.(0x%08x)\n", + isr); + ERR("disabling interrupt source(s)!\n"); + SAA7146_IER_DISABLE(dev,isr); + } + saa7146_write(dev, ISR, ack_isr); + return IRQ_HANDLED; +} + +/*********************************************************************************/ +/* configuration-functions */ + +static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent) +{ + struct saa7146_pci_extension_data *pci_ext = (struct saa7146_pci_extension_data *)ent->driver_data; + struct saa7146_extension *ext = pci_ext->ext; + struct saa7146_dev *dev; + int err = -ENOMEM; + + /* clear out mem for sure */ + dev = kzalloc(sizeof(struct saa7146_dev), GFP_KERNEL); + if (!dev) { + ERR("out of memory\n"); + goto out; + } + + /* create a nice device name */ + sprintf(dev->name, "saa7146 (%d)", saa7146_num); + + DEB_EE("pci:%p\n", pci); + + err = pci_enable_device(pci); + if (err < 0) { + ERR("pci_enable_device() failed\n"); + goto err_free; + } + + /* enable bus-mastering */ + pci_set_master(pci); + + dev->pci = pci; + + /* get chip-revision; this is needed to enable bug-fixes */ + dev->revision = pci->revision; + + /* remap the memory from virtual to physical address */ + + err = pci_request_region(pci, 0, "saa7146"); + if (err < 0) + goto err_disable; + + dev->mem = ioremap(pci_resource_start(pci, 0), + pci_resource_len(pci, 0)); + if (!dev->mem) { + ERR("ioremap() failed\n"); + err = -ENODEV; + goto err_release; + } + + /* we don't do a master reset here anymore, it screws up + some boards that don't have an i2c-eeprom for configuration + values */ +/* + saa7146_write(dev, MC1, MASK_31); +*/ + + /* disable all irqs */ + saa7146_write(dev, IER, 0); + + /* shut down all dma transfers and rps tasks */ + saa7146_write(dev, MC1, 0x30ff0000); + + /* clear out any rps-signals pending */ + saa7146_write(dev, MC2, 0xf8000000); + + /* request an interrupt for the saa7146 */ + err = request_irq(pci->irq, interrupt_hw, IRQF_SHARED, + dev->name, dev); + if (err < 0) { + ERR("request_irq() failed\n"); + goto err_unmap; + } + + err = -ENOMEM; + + /* get memory for various stuff */ + dev->d_rps0.cpu_addr = dma_alloc_coherent(&pci->dev, SAA7146_RPS_MEM, + &dev->d_rps0.dma_handle, + GFP_KERNEL); + if (!dev->d_rps0.cpu_addr) + goto err_free_irq; + + dev->d_rps1.cpu_addr = dma_alloc_coherent(&pci->dev, SAA7146_RPS_MEM, + &dev->d_rps1.dma_handle, + GFP_KERNEL); + if (!dev->d_rps1.cpu_addr) + goto err_free_rps0; + + dev->d_i2c.cpu_addr = dma_alloc_coherent(&pci->dev, SAA7146_RPS_MEM, + &dev->d_i2c.dma_handle, GFP_KERNEL); + if (!dev->d_i2c.cpu_addr) + goto err_free_rps1; + + /* the rest + print status message */ + + pr_info("found saa7146 @ mem %p (revision %d, irq %d) (0x%04x,0x%04x)\n", + dev->mem, dev->revision, pci->irq, + pci->subsystem_vendor, pci->subsystem_device); + dev->ext = ext; + + mutex_init(&dev->v4l2_lock); + spin_lock_init(&dev->int_slock); + spin_lock_init(&dev->slock); + + mutex_init(&dev->i2c_lock); + + dev->module = THIS_MODULE; + init_waitqueue_head(&dev->i2c_wq); + + /* set some sane pci arbitrition values */ + saa7146_write(dev, PCI_BT_V1, 0x1c00101f); + + /* TODO: use the status code of the callback */ + + err = -ENODEV; + + if (ext->probe && ext->probe(dev)) { + DEB_D("ext->probe() failed for %p. skipping device.\n", dev); + goto err_free_i2c; + } + + if (ext->attach(dev, pci_ext)) { + DEB_D("ext->attach() failed for %p. skipping device.\n", dev); + goto err_free_i2c; + } + /* V4L extensions will set the pci drvdata to the v4l2_device in the + attach() above. So for those cards that do not use V4L we have to + set it explicitly. */ + pci_set_drvdata(pci, &dev->v4l2_dev); + + saa7146_num++; + + err = 0; +out: + return err; + +err_free_i2c: + dma_free_coherent(&pci->dev, SAA7146_RPS_MEM, dev->d_i2c.cpu_addr, + dev->d_i2c.dma_handle); +err_free_rps1: + dma_free_coherent(&pci->dev, SAA7146_RPS_MEM, dev->d_rps1.cpu_addr, + dev->d_rps1.dma_handle); +err_free_rps0: + dma_free_coherent(&pci->dev, SAA7146_RPS_MEM, dev->d_rps0.cpu_addr, + dev->d_rps0.dma_handle); +err_free_irq: + free_irq(pci->irq, (void *)dev); +err_unmap: + iounmap(dev->mem); +err_release: + pci_release_region(pci, 0); +err_disable: + pci_disable_device(pci); +err_free: + kfree(dev); + goto out; +} + +static void saa7146_remove_one(struct pci_dev *pdev) +{ + struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); + struct saa7146_dev *dev = to_saa7146_dev(v4l2_dev); + struct { + void *addr; + dma_addr_t dma; + } dev_map[] = { + { dev->d_i2c.cpu_addr, dev->d_i2c.dma_handle }, + { dev->d_rps1.cpu_addr, dev->d_rps1.dma_handle }, + { dev->d_rps0.cpu_addr, dev->d_rps0.dma_handle }, + { NULL, 0 } + }, *p; + + DEB_EE("dev:%p\n", dev); + + dev->ext->detach(dev); + + /* shut down all video dma transfers */ + saa7146_write(dev, MC1, 0x00ff0000); + + /* disable all irqs, release irq-routine */ + saa7146_write(dev, IER, 0); + + free_irq(pdev->irq, dev); + + for (p = dev_map; p->addr; p++) + dma_free_coherent(&pdev->dev, SAA7146_RPS_MEM, p->addr, + p->dma); + + iounmap(dev->mem); + pci_release_region(pdev, 0); + pci_disable_device(pdev); + kfree(dev); + + saa7146_num--; +} + +/*********************************************************************************/ +/* extension handling functions */ + +int saa7146_register_extension(struct saa7146_extension* ext) +{ + DEB_EE("ext:%p\n", ext); + + ext->driver.name = ext->name; + ext->driver.id_table = ext->pci_tbl; + ext->driver.probe = saa7146_init_one; + ext->driver.remove = saa7146_remove_one; + + pr_info("register extension '%s'\n", ext->name); + return pci_register_driver(&ext->driver); +} + +int saa7146_unregister_extension(struct saa7146_extension* ext) +{ + DEB_EE("ext:%p\n", ext); + pr_info("unregister extension '%s'\n", ext->name); + pci_unregister_driver(&ext->driver); + return 0; +} + +EXPORT_SYMBOL_GPL(saa7146_register_extension); +EXPORT_SYMBOL_GPL(saa7146_unregister_extension); + +/* misc functions used by extension modules */ +EXPORT_SYMBOL_GPL(saa7146_pgtable_alloc); +EXPORT_SYMBOL_GPL(saa7146_pgtable_free); +EXPORT_SYMBOL_GPL(saa7146_pgtable_build_single); +EXPORT_SYMBOL_GPL(saa7146_vmalloc_build_pgtable); +EXPORT_SYMBOL_GPL(saa7146_vfree_destroy_pgtable); +EXPORT_SYMBOL_GPL(saa7146_wait_for_debi_done); + +EXPORT_SYMBOL_GPL(saa7146_setgpio); + +EXPORT_SYMBOL_GPL(saa7146_i2c_adapter_prepare); + +EXPORT_SYMBOL_GPL(saa7146_debug); + +MODULE_AUTHOR("Michael Hunold <michael@mihu.de>"); +MODULE_DESCRIPTION("driver for generic saa7146-based hardware"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/deprecated/saa7146/common/saa7146_fops.c b/drivers/staging/media/deprecated/saa7146/common/saa7146_fops.c new file mode 100644 index 000000000000..aa14698a9c54 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/common/saa7146_fops.c @@ -0,0 +1,658 @@ +// SPDX-License-Identifier: GPL-2.0-only +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include "saa7146_vv.h" + +/****************************************************************************/ +/* resource management functions, shamelessly stolen from saa7134 driver */ + +int saa7146_res_get(struct saa7146_fh *fh, unsigned int bit) +{ + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + + if (fh->resources & bit) { + DEB_D("already allocated! want: 0x%02x, cur:0x%02x\n", + bit, vv->resources); + /* have it already allocated */ + return 1; + } + + /* is it free? */ + if (vv->resources & bit) { + DEB_D("locked! vv->resources:0x%02x, we want:0x%02x\n", + vv->resources, bit); + /* no, someone else uses it */ + return 0; + } + /* it's free, grab it */ + fh->resources |= bit; + vv->resources |= bit; + DEB_D("res: get 0x%02x, cur:0x%02x\n", bit, vv->resources); + return 1; +} + +void saa7146_res_free(struct saa7146_fh *fh, unsigned int bits) +{ + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + + BUG_ON((fh->resources & bits) != bits); + + fh->resources &= ~bits; + vv->resources &= ~bits; + DEB_D("res: put 0x%02x, cur:0x%02x\n", bits, vv->resources); +} + + +/********************************************************************************/ +/* common dma functions */ + +void saa7146_dma_free(struct saa7146_dev *dev,struct videobuf_queue *q, + struct saa7146_buf *buf) +{ + struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); + DEB_EE("dev:%p, buf:%p\n", dev, buf); + + videobuf_waiton(q, &buf->vb, 0, 0); + videobuf_dma_unmap(q->dev, dma); + videobuf_dma_free(dma); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + + +/********************************************************************************/ +/* common buffer functions */ + +int saa7146_buffer_queue(struct saa7146_dev *dev, + struct saa7146_dmaqueue *q, + struct saa7146_buf *buf) +{ + assert_spin_locked(&dev->slock); + DEB_EE("dev:%p, dmaq:%p, buf:%p\n", dev, q, buf); + + BUG_ON(!q); + + if (NULL == q->curr) { + q->curr = buf; + DEB_D("immediately activating buffer %p\n", buf); + buf->activate(dev,buf,NULL); + } else { + list_add_tail(&buf->vb.queue,&q->queue); + buf->vb.state = VIDEOBUF_QUEUED; + DEB_D("adding buffer %p to queue. (active buffer present)\n", + buf); + } + return 0; +} + +void saa7146_buffer_finish(struct saa7146_dev *dev, + struct saa7146_dmaqueue *q, + int state) +{ + assert_spin_locked(&dev->slock); + DEB_EE("dev:%p, dmaq:%p, state:%d\n", dev, q, state); + DEB_EE("q->curr:%p\n", q->curr); + + /* finish current buffer */ + if (NULL == q->curr) { + DEB_D("aiii. no current buffer\n"); + return; + } + + q->curr->vb.state = state; + q->curr->vb.ts = ktime_get_ns(); + wake_up(&q->curr->vb.done); + + q->curr = NULL; +} + +void saa7146_buffer_next(struct saa7146_dev *dev, + struct saa7146_dmaqueue *q, int vbi) +{ + struct saa7146_buf *buf,*next = NULL; + + BUG_ON(!q); + + DEB_INT("dev:%p, dmaq:%p, vbi:%d\n", dev, q, vbi); + + assert_spin_locked(&dev->slock); + if (!list_empty(&q->queue)) { + /* activate next one from queue */ + buf = list_entry(q->queue.next,struct saa7146_buf,vb.queue); + list_del(&buf->vb.queue); + if (!list_empty(&q->queue)) + next = list_entry(q->queue.next,struct saa7146_buf, vb.queue); + q->curr = buf; + DEB_INT("next buffer: buf:%p, prev:%p, next:%p\n", + buf, q->queue.prev, q->queue.next); + buf->activate(dev,buf,next); + } else { + DEB_INT("no next buffer. stopping.\n"); + if( 0 != vbi ) { + /* turn off video-dma3 */ + saa7146_write(dev,MC1, MASK_20); + } else { + /* nothing to do -- just prevent next video-dma1 transfer + by lowering the protection address */ + + // fixme: fix this for vflip != 0 + + saa7146_write(dev, PROT_ADDR1, 0); + saa7146_write(dev, MC2, (MASK_02|MASK_18)); + + /* write the address of the rps-program */ + saa7146_write(dev, RPS_ADDR0, dev->d_rps0.dma_handle); + /* turn on rps */ + saa7146_write(dev, MC1, (MASK_12 | MASK_28)); + +/* + printk("vdma%d.base_even: 0x%08x\n", 1,saa7146_read(dev,BASE_EVEN1)); + printk("vdma%d.base_odd: 0x%08x\n", 1,saa7146_read(dev,BASE_ODD1)); + printk("vdma%d.prot_addr: 0x%08x\n", 1,saa7146_read(dev,PROT_ADDR1)); + printk("vdma%d.base_page: 0x%08x\n", 1,saa7146_read(dev,BASE_PAGE1)); + printk("vdma%d.pitch: 0x%08x\n", 1,saa7146_read(dev,PITCH1)); + printk("vdma%d.num_line_byte: 0x%08x\n", 1,saa7146_read(dev,NUM_LINE_BYTE1)); +*/ + } + del_timer(&q->timeout); + } +} + +void saa7146_buffer_timeout(struct timer_list *t) +{ + struct saa7146_dmaqueue *q = from_timer(q, t, timeout); + struct saa7146_dev *dev = q->dev; + unsigned long flags; + + DEB_EE("dev:%p, dmaq:%p\n", dev, q); + + spin_lock_irqsave(&dev->slock,flags); + if (q->curr) { + DEB_D("timeout on %p\n", q->curr); + saa7146_buffer_finish(dev,q,VIDEOBUF_ERROR); + } + + /* we don't restart the transfer here like other drivers do. when + a streaming capture is disabled, the timeout function will be + called for the current buffer. if we activate the next buffer now, + we mess up our capture logic. if a timeout occurs on another buffer, + then something is seriously broken before, so no need to buffer the + next capture IMHO... */ +/* + saa7146_buffer_next(dev,q); +*/ + spin_unlock_irqrestore(&dev->slock,flags); +} + +/********************************************************************************/ +/* file operations */ + +static int fops_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct saa7146_dev *dev = video_drvdata(file); + struct saa7146_fh *fh = NULL; + int result = 0; + + DEB_EE("file:%p, dev:%s\n", file, video_device_node_name(vdev)); + + if (mutex_lock_interruptible(vdev->lock)) + return -ERESTARTSYS; + + DEB_D("using: %p\n", dev); + + /* check if an extension is registered */ + if( NULL == dev->ext ) { + DEB_S("no extension registered for this device\n"); + result = -ENODEV; + goto out; + } + + /* allocate per open data */ + fh = kzalloc(sizeof(*fh),GFP_KERNEL); + if (NULL == fh) { + DEB_S("cannot allocate memory for per open data\n"); + result = -ENOMEM; + goto out; + } + + v4l2_fh_init(&fh->fh, vdev); + + file->private_data = &fh->fh; + fh->dev = dev; + + if (vdev->vfl_type == VFL_TYPE_VBI) { + DEB_S("initializing vbi...\n"); + if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) + result = saa7146_vbi_uops.open(dev,file); + if (dev->ext_vv_data->vbi_fops.open) + dev->ext_vv_data->vbi_fops.open(file); + } else { + DEB_S("initializing video...\n"); + result = saa7146_video_uops.open(dev,file); + } + + if (0 != result) { + goto out; + } + + if( 0 == try_module_get(dev->ext->module)) { + result = -EINVAL; + goto out; + } + + result = 0; + v4l2_fh_add(&fh->fh); +out: + if (fh && result != 0) { + kfree(fh); + file->private_data = NULL; + } + mutex_unlock(vdev->lock); + return result; +} + +static int fops_release(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct saa7146_fh *fh = file->private_data; + struct saa7146_dev *dev = fh->dev; + + DEB_EE("file:%p\n", file); + + mutex_lock(vdev->lock); + + if (vdev->vfl_type == VFL_TYPE_VBI) { + if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) + saa7146_vbi_uops.release(dev,file); + if (dev->ext_vv_data->vbi_fops.release) + dev->ext_vv_data->vbi_fops.release(file); + } else { + saa7146_video_uops.release(dev,file); + } + + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); + module_put(dev->ext->module); + file->private_data = NULL; + kfree(fh); + + mutex_unlock(vdev->lock); + + return 0; +} + +static int fops_mmap(struct file *file, struct vm_area_struct * vma) +{ + struct video_device *vdev = video_devdata(file); + struct saa7146_fh *fh = file->private_data; + struct videobuf_queue *q; + int res; + + switch (vdev->vfl_type) { + case VFL_TYPE_VIDEO: { + DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, vma:%p\n", + file, vma); + q = &fh->video_q; + break; + } + case VFL_TYPE_VBI: { + DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, vma:%p\n", + file, vma); + if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_SLICED_VBI_OUTPUT) + return -ENODEV; + q = &fh->vbi_q; + break; + } + default: + BUG(); + } + + if (mutex_lock_interruptible(vdev->lock)) + return -ERESTARTSYS; + res = videobuf_mmap_mapper(q, vma); + mutex_unlock(vdev->lock); + return res; +} + +static __poll_t __fops_poll(struct file *file, struct poll_table_struct *wait) +{ + struct video_device *vdev = video_devdata(file); + struct saa7146_fh *fh = file->private_data; + struct videobuf_buffer *buf = NULL; + struct videobuf_queue *q; + __poll_t res = v4l2_ctrl_poll(file, wait); + + DEB_EE("file:%p, poll:%p\n", file, wait); + + if (vdev->vfl_type == VFL_TYPE_VBI) { + if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_SLICED_VBI_OUTPUT) + return res | EPOLLOUT | EPOLLWRNORM; + if( 0 == fh->vbi_q.streaming ) + return res | videobuf_poll_stream(file, &fh->vbi_q, wait); + q = &fh->vbi_q; + } else { + DEB_D("using video queue\n"); + q = &fh->video_q; + } + + if (!list_empty(&q->stream)) + buf = list_entry(q->stream.next, struct videobuf_buffer, stream); + + if (!buf) { + DEB_D("buf == NULL!\n"); + return res | EPOLLERR; + } + + poll_wait(file, &buf->done, wait); + if (buf->state == VIDEOBUF_DONE || buf->state == VIDEOBUF_ERROR) { + DEB_D("poll succeeded!\n"); + return res | EPOLLIN | EPOLLRDNORM; + } + + DEB_D("nothing to poll for, buf->state:%d\n", buf->state); + return res; +} + +static __poll_t fops_poll(struct file *file, struct poll_table_struct *wait) +{ + struct video_device *vdev = video_devdata(file); + __poll_t res; + + mutex_lock(vdev->lock); + res = __fops_poll(file, wait); + mutex_unlock(vdev->lock); + return res; +} + +static ssize_t fops_read(struct file *file, char __user *data, size_t count, loff_t *ppos) +{ + struct video_device *vdev = video_devdata(file); + struct saa7146_fh *fh = file->private_data; + int ret; + + switch (vdev->vfl_type) { + case VFL_TYPE_VIDEO: +/* + DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, data:%p, count:%lun", + file, data, (unsigned long)count); +*/ + return saa7146_video_uops.read(file,data,count,ppos); + case VFL_TYPE_VBI: +/* + DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, data:%p, count:%lu\n", + file, data, (unsigned long)count); +*/ + if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) { + if (mutex_lock_interruptible(vdev->lock)) + return -ERESTARTSYS; + ret = saa7146_vbi_uops.read(file, data, count, ppos); + mutex_unlock(vdev->lock); + return ret; + } + return -EINVAL; + default: + BUG(); + } +} + +static ssize_t fops_write(struct file *file, const char __user *data, size_t count, loff_t *ppos) +{ + struct video_device *vdev = video_devdata(file); + struct saa7146_fh *fh = file->private_data; + int ret; + + switch (vdev->vfl_type) { + case VFL_TYPE_VIDEO: + return -EINVAL; + case VFL_TYPE_VBI: + if (fh->dev->ext_vv_data->vbi_fops.write) { + if (mutex_lock_interruptible(vdev->lock)) + return -ERESTARTSYS; + ret = fh->dev->ext_vv_data->vbi_fops.write(file, data, count, ppos); + mutex_unlock(vdev->lock); + return ret; + } + return -EINVAL; + default: + BUG(); + } +} + +static const struct v4l2_file_operations video_fops = +{ + .owner = THIS_MODULE, + .open = fops_open, + .release = fops_release, + .read = fops_read, + .write = fops_write, + .poll = fops_poll, + .mmap = fops_mmap, + .unlocked_ioctl = video_ioctl2, +}; + +static void vv_callback(struct saa7146_dev *dev, unsigned long status) +{ + u32 isr = status; + + DEB_INT("dev:%p, isr:0x%08x\n", dev, (u32)status); + + if (0 != (isr & (MASK_27))) { + DEB_INT("irq: RPS0 (0x%08x)\n", isr); + saa7146_video_uops.irq_done(dev,isr); + } + + if (0 != (isr & (MASK_28))) { + u32 mc2 = saa7146_read(dev, MC2); + if( 0 != (mc2 & MASK_15)) { + DEB_INT("irq: RPS1 vbi workaround (0x%08x)\n", isr); + wake_up(&dev->vv_data->vbi_wq); + saa7146_write(dev,MC2, MASK_31); + return; + } + DEB_INT("irq: RPS1 (0x%08x)\n", isr); + saa7146_vbi_uops.irq_done(dev,isr); + } +} + +static const struct v4l2_ctrl_ops saa7146_ctrl_ops = { + .s_ctrl = saa7146_s_ctrl, +}; + +int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv) +{ + struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler; + struct v4l2_pix_format *fmt; + struct v4l2_vbi_format *vbi; + struct saa7146_vv *vv; + int err; + + err = v4l2_device_register(&dev->pci->dev, &dev->v4l2_dev); + if (err) + return err; + + v4l2_ctrl_handler_init(hdl, 6); + v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops, + V4L2_CID_CONTRAST, 0, 127, 1, 64); + v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops, + V4L2_CID_SATURATION, 0, 127, 1, 64); + v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + if (hdl->error) { + err = hdl->error; + v4l2_ctrl_handler_free(hdl); + v4l2_device_unregister(&dev->v4l2_dev); + return err; + } + dev->v4l2_dev.ctrl_handler = hdl; + + vv = kzalloc(sizeof(struct saa7146_vv), GFP_KERNEL); + if (vv == NULL) { + ERR("out of memory. aborting.\n"); + v4l2_ctrl_handler_free(hdl); + v4l2_device_unregister(&dev->v4l2_dev); + return -ENOMEM; + } + ext_vv->vid_ops = saa7146_video_ioctl_ops; + ext_vv->vbi_ops = saa7146_vbi_ioctl_ops; + ext_vv->core_ops = &saa7146_video_ioctl_ops; + + DEB_EE("dev:%p\n", dev); + + /* set default values for video parts of the saa7146 */ + saa7146_write(dev, BCS_CTRL, 0x80400040); + + /* enable video-port pins */ + saa7146_write(dev, MC1, (MASK_10 | MASK_26)); + + /* save per-device extension data (one extension can + handle different devices that might need different + configuration data) */ + dev->ext_vv_data = ext_vv; + + vv->d_clipping.cpu_addr = + dma_alloc_coherent(&dev->pci->dev, SAA7146_CLIPPING_MEM, + &vv->d_clipping.dma_handle, GFP_KERNEL); + if( NULL == vv->d_clipping.cpu_addr ) { + ERR("out of memory. aborting.\n"); + kfree(vv); + v4l2_ctrl_handler_free(hdl); + v4l2_device_unregister(&dev->v4l2_dev); + return -ENOMEM; + } + + saa7146_video_uops.init(dev,vv); + if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) + saa7146_vbi_uops.init(dev,vv); + + vv->ov_fb.fmt.width = vv->standard->h_max_out; + vv->ov_fb.fmt.height = vv->standard->v_max_out; + vv->ov_fb.fmt.pixelformat = V4L2_PIX_FMT_RGB565; + vv->ov_fb.fmt.bytesperline = 2 * vv->ov_fb.fmt.width; + vv->ov_fb.fmt.sizeimage = vv->ov_fb.fmt.bytesperline * vv->ov_fb.fmt.height; + vv->ov_fb.fmt.colorspace = V4L2_COLORSPACE_SRGB; + + fmt = &vv->video_fmt; + fmt->width = 384; + fmt->height = 288; + fmt->pixelformat = V4L2_PIX_FMT_BGR24; + fmt->field = V4L2_FIELD_ANY; + fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; + fmt->bytesperline = 3 * fmt->width; + fmt->sizeimage = fmt->bytesperline * fmt->height; + + vbi = &vv->vbi_fmt; + vbi->sampling_rate = 27000000; + vbi->offset = 248; /* todo */ + vbi->samples_per_line = 720 * 2; + vbi->sample_format = V4L2_PIX_FMT_GREY; + + /* fixme: this only works for PAL */ + vbi->start[0] = 5; + vbi->count[0] = 16; + vbi->start[1] = 312; + vbi->count[1] = 16; + + timer_setup(&vv->vbi_read_timeout, NULL, 0); + + vv->ov_fb.capability = V4L2_FBUF_CAP_LIST_CLIPPING; + vv->ov_fb.flags = V4L2_FBUF_FLAG_PRIMARY; + dev->vv_data = vv; + dev->vv_callback = &vv_callback; + + return 0; +} +EXPORT_SYMBOL_GPL(saa7146_vv_init); + +int saa7146_vv_release(struct saa7146_dev* dev) +{ + struct saa7146_vv *vv = dev->vv_data; + + DEB_EE("dev:%p\n", dev); + + v4l2_device_unregister(&dev->v4l2_dev); + dma_free_coherent(&dev->pci->dev, SAA7146_CLIPPING_MEM, + vv->d_clipping.cpu_addr, vv->d_clipping.dma_handle); + v4l2_ctrl_handler_free(&dev->ctrl_handler); + kfree(vv); + dev->vv_data = NULL; + dev->vv_callback = NULL; + + return 0; +} +EXPORT_SYMBOL_GPL(saa7146_vv_release); + +int saa7146_register_device(struct video_device *vfd, struct saa7146_dev *dev, + char *name, int type) +{ + int err; + int i; + + DEB_EE("dev:%p, name:'%s', type:%d\n", dev, name, type); + + vfd->fops = &video_fops; + if (type == VFL_TYPE_VIDEO) + vfd->ioctl_ops = &dev->ext_vv_data->vid_ops; + else + vfd->ioctl_ops = &dev->ext_vv_data->vbi_ops; + vfd->release = video_device_release_empty; + vfd->lock = &dev->v4l2_lock; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->tvnorms = 0; + for (i = 0; i < dev->ext_vv_data->num_stds; i++) + vfd->tvnorms |= dev->ext_vv_data->stds[i].id; + strscpy(vfd->name, name, sizeof(vfd->name)); + vfd->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | + V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + vfd->device_caps |= dev->ext_vv_data->capabilities; + if (type == VFL_TYPE_VIDEO) + vfd->device_caps &= + ~(V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_OUTPUT); + else + vfd->device_caps &= + ~(V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_AUDIO); + video_set_drvdata(vfd, dev); + + err = video_register_device(vfd, type, -1); + if (err < 0) { + ERR("cannot register v4l2 device. skipping.\n"); + return err; + } + + pr_info("%s: registered device %s [v4l2]\n", + dev->name, video_device_node_name(vfd)); + return 0; +} +EXPORT_SYMBOL_GPL(saa7146_register_device); + +int saa7146_unregister_device(struct video_device *vfd, struct saa7146_dev *dev) +{ + DEB_EE("dev:%p\n", dev); + + video_unregister_device(vfd); + return 0; +} +EXPORT_SYMBOL_GPL(saa7146_unregister_device); + +static int __init saa7146_vv_init_module(void) +{ + return 0; +} + + +static void __exit saa7146_vv_cleanup_module(void) +{ +} + +module_init(saa7146_vv_init_module); +module_exit(saa7146_vv_cleanup_module); + +MODULE_AUTHOR("Michael Hunold <michael@mihu.de>"); +MODULE_DESCRIPTION("video4linux driver for saa7146-based hardware"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/deprecated/saa7146/common/saa7146_hlp.c b/drivers/staging/media/deprecated/saa7146/common/saa7146_hlp.c new file mode 100644 index 000000000000..b1222a4cfa4a --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/common/saa7146_hlp.c @@ -0,0 +1,1046 @@ +// SPDX-License-Identifier: GPL-2.0-only +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/export.h> +#include "saa7146_vv.h" + +static void calculate_output_format_register(struct saa7146_dev* saa, u32 palette, u32* clip_format) +{ + /* clear out the necessary bits */ + *clip_format &= 0x0000ffff; + /* set these bits new */ + *clip_format |= (( ((palette&0xf00)>>8) << 30) | ((palette&0x00f) << 24) | (((palette&0x0f0)>>4) << 16)); +} + +static void calculate_hps_source_and_sync(struct saa7146_dev *dev, int source, int sync, u32* hps_ctrl) +{ + *hps_ctrl &= ~(MASK_30 | MASK_31 | MASK_28); + *hps_ctrl |= (source << 30) | (sync << 28); +} + +static void calculate_hxo_and_hyo(struct saa7146_vv *vv, u32* hps_h_scale, u32* hps_ctrl) +{ + int hyo = 0, hxo = 0; + + hyo = vv->standard->v_offset; + hxo = vv->standard->h_offset; + + *hps_h_scale &= ~(MASK_B0 | 0xf00); + *hps_h_scale |= (hxo << 0); + + *hps_ctrl &= ~(MASK_W0 | MASK_B2); + *hps_ctrl |= (hyo << 12); +} + +/* helper functions for the calculation of the horizontal- and vertical + scaling registers, clip-format-register etc ... + these functions take pointers to the (most-likely read-out + original-values) and manipulate them according to the requested + changes. +*/ + +/* hps_coeff used for CXY and CXUV; scale 1/1 -> scale 1/64 */ +static struct { + u16 hps_coeff; + u16 weight_sum; +} hps_h_coeff_tab [] = { + {0x00, 2}, {0x02, 4}, {0x00, 4}, {0x06, 8}, {0x02, 8}, + {0x08, 8}, {0x00, 8}, {0x1E, 16}, {0x0E, 8}, {0x26, 8}, + {0x06, 8}, {0x42, 8}, {0x02, 8}, {0x80, 8}, {0x00, 8}, + {0xFE, 16}, {0xFE, 8}, {0x7E, 8}, {0x7E, 8}, {0x3E, 8}, + {0x3E, 8}, {0x1E, 8}, {0x1E, 8}, {0x0E, 8}, {0x0E, 8}, + {0x06, 8}, {0x06, 8}, {0x02, 8}, {0x02, 8}, {0x00, 8}, + {0x00, 8}, {0xFE, 16}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, + {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, + {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, + {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0x7E, 8}, + {0x7E, 8}, {0x3E, 8}, {0x3E, 8}, {0x1E, 8}, {0x1E, 8}, + {0x0E, 8}, {0x0E, 8}, {0x06, 8}, {0x06, 8}, {0x02, 8}, + {0x02, 8}, {0x00, 8}, {0x00, 8}, {0xFE, 16} +}; + +/* table of attenuation values for horizontal scaling */ +static u8 h_attenuation[] = { 1, 2, 4, 8, 2, 4, 8, 16, 0}; + +/* calculate horizontal scale registers */ +static int calculate_h_scale_registers(struct saa7146_dev *dev, + int in_x, int out_x, int flip_lr, + u32* hps_ctrl, u32* hps_v_gain, u32* hps_h_prescale, u32* hps_h_scale) +{ + /* horizontal prescaler */ + u32 dcgx = 0, xpsc = 0, xacm = 0, cxy = 0, cxuv = 0; + /* horizontal scaler */ + u32 xim = 0, xp = 0, xsci =0; + /* vertical scale & gain */ + u32 pfuv = 0; + + /* helper variables */ + u32 h_atten = 0, i = 0; + + if ( 0 == out_x ) { + return -EINVAL; + } + + /* mask out vanity-bit */ + *hps_ctrl &= ~MASK_29; + + /* calculate prescale-(xspc)-value: [n .. 1/2) : 1 + [1/2 .. 1/3) : 2 + [1/3 .. 1/4) : 3 + ... */ + if (in_x > out_x) { + xpsc = in_x / out_x; + } + else { + /* zooming */ + xpsc = 1; + } + + /* if flip_lr-bit is set, number of pixels after + horizontal prescaling must be < 384 */ + if ( 0 != flip_lr ) { + + /* set vanity bit */ + *hps_ctrl |= MASK_29; + + while (in_x / xpsc >= 384 ) + xpsc++; + } + /* if zooming is wanted, number of pixels after + horizontal prescaling must be < 768 */ + else { + while ( in_x / xpsc >= 768 ) + xpsc++; + } + + /* maximum prescale is 64 (p.69) */ + if ( xpsc > 64 ) + xpsc = 64; + + /* keep xacm clear*/ + xacm = 0; + + /* set horizontal filter parameters (CXY = CXUV) */ + cxy = hps_h_coeff_tab[( (xpsc - 1) < 63 ? (xpsc - 1) : 63 )].hps_coeff; + cxuv = cxy; + + /* calculate and set horizontal fine scale (xsci) */ + + /* bypass the horizontal scaler ? */ + if ( (in_x == out_x) && ( 1 == xpsc ) ) + xsci = 0x400; + else + xsci = ( (1024 * in_x) / (out_x * xpsc) ) + xpsc; + + /* set start phase for horizontal fine scale (xp) to 0 */ + xp = 0; + + /* set xim, if we bypass the horizontal scaler */ + if ( 0x400 == xsci ) + xim = 1; + else + xim = 0; + + /* if the prescaler is bypassed, enable horizontal + accumulation mode (xacm) and clear dcgx */ + if( 1 == xpsc ) { + xacm = 1; + dcgx = 0; + } else { + xacm = 0; + /* get best match in the table of attenuations + for horizontal scaling */ + h_atten = hps_h_coeff_tab[( (xpsc - 1) < 63 ? (xpsc - 1) : 63 )].weight_sum; + + for (i = 0; h_attenuation[i] != 0; i++) { + if (h_attenuation[i] >= h_atten) + break; + } + + dcgx = i; + } + + /* the horizontal scaling increment controls the UV filter + to reduce the bandwidth to improve the display quality, + so set it ... */ + if ( xsci == 0x400) + pfuv = 0x00; + else if ( xsci < 0x600) + pfuv = 0x01; + else if ( xsci < 0x680) + pfuv = 0x11; + else if ( xsci < 0x700) + pfuv = 0x22; + else + pfuv = 0x33; + + + *hps_v_gain &= MASK_W0|MASK_B2; + *hps_v_gain |= (pfuv << 24); + + *hps_h_scale &= ~(MASK_W1 | 0xf000); + *hps_h_scale |= (xim << 31) | (xp << 24) | (xsci << 12); + + *hps_h_prescale |= (dcgx << 27) | ((xpsc-1) << 18) | (xacm << 17) | (cxy << 8) | (cxuv << 0); + + return 0; +} + +static struct { + u16 hps_coeff; + u16 weight_sum; +} hps_v_coeff_tab [] = { + {0x0100, 2}, {0x0102, 4}, {0x0300, 4}, {0x0106, 8}, {0x0502, 8}, + {0x0708, 8}, {0x0F00, 8}, {0x011E, 16}, {0x110E, 16}, {0x1926, 16}, + {0x3906, 16}, {0x3D42, 16}, {0x7D02, 16}, {0x7F80, 16}, {0xFF00, 16}, + {0x01FE, 32}, {0x01FE, 32}, {0x817E, 32}, {0x817E, 32}, {0xC13E, 32}, + {0xC13E, 32}, {0xE11E, 32}, {0xE11E, 32}, {0xF10E, 32}, {0xF10E, 32}, + {0xF906, 32}, {0xF906, 32}, {0xFD02, 32}, {0xFD02, 32}, {0xFF00, 32}, + {0xFF00, 32}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, + {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, + {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, + {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x817E, 64}, + {0x817E, 64}, {0xC13E, 64}, {0xC13E, 64}, {0xE11E, 64}, {0xE11E, 64}, + {0xF10E, 64}, {0xF10E, 64}, {0xF906, 64}, {0xF906, 64}, {0xFD02, 64}, + {0xFD02, 64}, {0xFF00, 64}, {0xFF00, 64}, {0x01FE, 128} +}; + +/* table of attenuation values for vertical scaling */ +static u16 v_attenuation[] = { 2, 4, 8, 16, 32, 64, 128, 256, 0}; + +/* calculate vertical scale registers */ +static int calculate_v_scale_registers(struct saa7146_dev *dev, enum v4l2_field field, + int in_y, int out_y, u32* hps_v_scale, u32* hps_v_gain) +{ + int lpi = 0; + + /* vertical scaling */ + u32 yacm = 0, ysci = 0, yacl = 0, ypo = 0, ype = 0; + /* vertical scale & gain */ + u32 dcgy = 0, cya_cyb = 0; + + /* helper variables */ + u32 v_atten = 0, i = 0; + + /* error, if vertical zooming */ + if ( in_y < out_y ) { + return -EINVAL; + } + + /* linear phase interpolation may be used + if scaling is between 1 and 1/2 (both fields used) + or scaling is between 1/2 and 1/4 (if only one field is used) */ + + if (V4L2_FIELD_HAS_BOTH(field)) { + if( 2*out_y >= in_y) { + lpi = 1; + } + } else if (field == V4L2_FIELD_TOP + || field == V4L2_FIELD_ALTERNATE + || field == V4L2_FIELD_BOTTOM) { + if( 4*out_y >= in_y ) { + lpi = 1; + } + out_y *= 2; + } + if( 0 != lpi ) { + + yacm = 0; + yacl = 0; + cya_cyb = 0x00ff; + + /* calculate scaling increment */ + if ( in_y > out_y ) + ysci = ((1024 * in_y) / (out_y + 1)) - 1024; + else + ysci = 0; + + dcgy = 0; + + /* calculate ype and ypo */ + ype = ysci / 16; + ypo = ype + (ysci / 64); + + } else { + yacm = 1; + + /* calculate scaling increment */ + ysci = (((10 * 1024 * (in_y - out_y - 1)) / in_y) + 9) / 10; + + /* calculate ype and ypo */ + ypo = ype = ((ysci + 15) / 16); + + /* the sequence length interval (yacl) has to be set according + to the prescale value, e.g. [n .. 1/2) : 0 + [1/2 .. 1/3) : 1 + [1/3 .. 1/4) : 2 + ... */ + if ( ysci < 512) { + yacl = 0; + } else { + yacl = ( ysci / (1024 - ysci) ); + } + + /* get filter coefficients for cya, cyb from table hps_v_coeff_tab */ + cya_cyb = hps_v_coeff_tab[ (yacl < 63 ? yacl : 63 ) ].hps_coeff; + + /* get best match in the table of attenuations for vertical scaling */ + v_atten = hps_v_coeff_tab[ (yacl < 63 ? yacl : 63 ) ].weight_sum; + + for (i = 0; v_attenuation[i] != 0; i++) { + if (v_attenuation[i] >= v_atten) + break; + } + + dcgy = i; + } + + /* ypo and ype swapped in spec ? */ + *hps_v_scale |= (yacm << 31) | (ysci << 21) | (yacl << 15) | (ypo << 8 ) | (ype << 1); + + *hps_v_gain &= ~(MASK_W0|MASK_B2); + *hps_v_gain |= (dcgy << 16) | (cya_cyb << 0); + + return 0; +} + +/* simple bubble-sort algorithm with duplicate elimination */ +static int sort_and_eliminate(u32* values, int* count) +{ + int low = 0, high = 0, top = 0; + int cur = 0, next = 0; + + /* sanity checks */ + if( (0 > *count) || (NULL == values) ) { + return -EINVAL; + } + + /* bubble sort the first @count items of the array @values */ + for( top = *count; top > 0; top--) { + for( low = 0, high = 1; high < top; low++, high++) { + if( values[low] > values[high] ) + swap(values[low], values[high]); + } + } + + /* remove duplicate items */ + for( cur = 0, next = 1; next < *count; next++) { + if( values[cur] != values[next]) + values[++cur] = values[next]; + } + + *count = cur + 1; + + return 0; +} + +static void calculate_clipping_registers_rect(struct saa7146_dev *dev, struct saa7146_fh *fh, + struct saa7146_video_dma *vdma2, u32* clip_format, u32* arbtr_ctrl, enum v4l2_field field) +{ + struct saa7146_vv *vv = dev->vv_data; + __le32 *clipping = vv->d_clipping.cpu_addr; + + int width = vv->ov.win.w.width; + int height = vv->ov.win.w.height; + int clipcount = vv->ov.nclips; + + u32 line_list[32]; + u32 pixel_list[32]; + int numdwords = 0; + + int i = 0, j = 0; + int cnt_line = 0, cnt_pixel = 0; + + int x[32], y[32], w[32], h[32]; + + /* clear out memory */ + memset(&line_list[0], 0x00, sizeof(u32)*32); + memset(&pixel_list[0], 0x00, sizeof(u32)*32); + memset(clipping, 0x00, SAA7146_CLIPPING_MEM); + + /* fill the line and pixel-lists */ + for(i = 0; i < clipcount; i++) { + int l = 0, r = 0, t = 0, b = 0; + + x[i] = vv->ov.clips[i].c.left; + y[i] = vv->ov.clips[i].c.top; + w[i] = vv->ov.clips[i].c.width; + h[i] = vv->ov.clips[i].c.height; + + if( w[i] < 0) { + x[i] += w[i]; w[i] = -w[i]; + } + if( h[i] < 0) { + y[i] += h[i]; h[i] = -h[i]; + } + if( x[i] < 0) { + w[i] += x[i]; x[i] = 0; + } + if( y[i] < 0) { + h[i] += y[i]; y[i] = 0; + } + if( 0 != vv->vflip ) { + y[i] = height - y[i] - h[i]; + } + + l = x[i]; + r = x[i]+w[i]; + t = y[i]; + b = y[i]+h[i]; + + /* insert left/right coordinates */ + pixel_list[ 2*i ] = min_t(int, l, width); + pixel_list[(2*i)+1] = min_t(int, r, width); + /* insert top/bottom coordinates */ + line_list[ 2*i ] = min_t(int, t, height); + line_list[(2*i)+1] = min_t(int, b, height); + } + + /* sort and eliminate lists */ + cnt_line = cnt_pixel = 2*clipcount; + sort_and_eliminate( &pixel_list[0], &cnt_pixel ); + sort_and_eliminate( &line_list[0], &cnt_line ); + + /* calculate the number of used u32s */ + numdwords = max_t(int, (cnt_line+1), (cnt_pixel+1))*2; + numdwords = max_t(int, 4, numdwords); + numdwords = min_t(int, 64, numdwords); + + /* fill up cliptable */ + for(i = 0; i < cnt_pixel; i++) { + clipping[2*i] |= cpu_to_le32(pixel_list[i] << 16); + } + for(i = 0; i < cnt_line; i++) { + clipping[(2*i)+1] |= cpu_to_le32(line_list[i] << 16); + } + + /* fill up cliptable with the display infos */ + for(j = 0; j < clipcount; j++) { + + for(i = 0; i < cnt_pixel; i++) { + + if( x[j] < 0) + x[j] = 0; + + if( pixel_list[i] < (x[j] + w[j])) { + + if ( pixel_list[i] >= x[j] ) { + clipping[2*i] |= cpu_to_le32(1 << j); + } + } + } + for(i = 0; i < cnt_line; i++) { + + if( y[j] < 0) + y[j] = 0; + + if( line_list[i] < (y[j] + h[j]) ) { + + if( line_list[i] >= y[j] ) { + clipping[(2*i)+1] |= cpu_to_le32(1 << j); + } + } + } + } + + /* adjust arbitration control register */ + *arbtr_ctrl &= 0xffff00ff; + *arbtr_ctrl |= 0x00001c00; + + vdma2->base_even = vv->d_clipping.dma_handle; + vdma2->base_odd = vv->d_clipping.dma_handle; + vdma2->prot_addr = vv->d_clipping.dma_handle+((sizeof(u32))*(numdwords)); + vdma2->base_page = 0x04; + vdma2->pitch = 0x00; + vdma2->num_line_byte = (0 << 16 | (sizeof(u32))*(numdwords-1) ); + + /* set clipping-mode. this depends on the field(s) used */ + *clip_format &= 0xfffffff7; + if (V4L2_FIELD_HAS_BOTH(field)) { + *clip_format |= 0x00000008; + } else { + *clip_format |= 0x00000000; + } +} + +/* disable clipping */ +static void saa7146_disable_clipping(struct saa7146_dev *dev) +{ + u32 clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL); + + /* mask out relevant bits (=lower word)*/ + clip_format &= MASK_W1; + + /* upload clipping-registers*/ + saa7146_write(dev, CLIP_FORMAT_CTRL,clip_format); + saa7146_write(dev, MC2, (MASK_05 | MASK_21)); + + /* disable video dma2 */ + saa7146_write(dev, MC1, MASK_21); +} + +static void saa7146_set_clipping_rect(struct saa7146_fh *fh) +{ + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + enum v4l2_field field = vv->ov.win.field; + struct saa7146_video_dma vdma2; + u32 clip_format; + u32 arbtr_ctrl; + + /* check clipcount, disable clipping if clipcount == 0*/ + if (vv->ov.nclips == 0) { + saa7146_disable_clipping(dev); + return; + } + + clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL); + arbtr_ctrl = saa7146_read(dev, PCI_BT_V1); + + calculate_clipping_registers_rect(dev, fh, &vdma2, &clip_format, &arbtr_ctrl, field); + + /* set clipping format */ + clip_format &= 0xffff0008; + clip_format |= (SAA7146_CLIPPING_RECT << 4); + + /* prepare video dma2 */ + saa7146_write(dev, BASE_EVEN2, vdma2.base_even); + saa7146_write(dev, BASE_ODD2, vdma2.base_odd); + saa7146_write(dev, PROT_ADDR2, vdma2.prot_addr); + saa7146_write(dev, BASE_PAGE2, vdma2.base_page); + saa7146_write(dev, PITCH2, vdma2.pitch); + saa7146_write(dev, NUM_LINE_BYTE2, vdma2.num_line_byte); + + /* prepare the rest */ + saa7146_write(dev, CLIP_FORMAT_CTRL,clip_format); + saa7146_write(dev, PCI_BT_V1, arbtr_ctrl); + + /* upload clip_control-register, clipping-registers, enable video dma2 */ + saa7146_write(dev, MC2, (MASK_05 | MASK_21 | MASK_03 | MASK_19)); + saa7146_write(dev, MC1, (MASK_05 | MASK_21)); +} + +static void saa7146_set_window(struct saa7146_dev *dev, int width, int height, enum v4l2_field field) +{ + struct saa7146_vv *vv = dev->vv_data; + + int source = vv->current_hps_source; + int sync = vv->current_hps_sync; + + u32 hps_v_scale = 0, hps_v_gain = 0, hps_ctrl = 0, hps_h_prescale = 0, hps_h_scale = 0; + + /* set vertical scale */ + hps_v_scale = 0; /* all bits get set by the function-call */ + hps_v_gain = 0; /* fixme: saa7146_read(dev, HPS_V_GAIN);*/ + calculate_v_scale_registers(dev, field, vv->standard->v_field*2, height, &hps_v_scale, &hps_v_gain); + + /* set horizontal scale */ + hps_ctrl = 0; + hps_h_prescale = 0; /* all bits get set in the function */ + hps_h_scale = 0; + calculate_h_scale_registers(dev, vv->standard->h_pixels, width, vv->hflip, &hps_ctrl, &hps_v_gain, &hps_h_prescale, &hps_h_scale); + + /* set hyo and hxo */ + calculate_hxo_and_hyo(vv, &hps_h_scale, &hps_ctrl); + calculate_hps_source_and_sync(dev, source, sync, &hps_ctrl); + + /* write out new register contents */ + saa7146_write(dev, HPS_V_SCALE, hps_v_scale); + saa7146_write(dev, HPS_V_GAIN, hps_v_gain); + saa7146_write(dev, HPS_CTRL, hps_ctrl); + saa7146_write(dev, HPS_H_PRESCALE,hps_h_prescale); + saa7146_write(dev, HPS_H_SCALE, hps_h_scale); + + /* upload shadow-ram registers */ + saa7146_write(dev, MC2, (MASK_05 | MASK_06 | MASK_21 | MASK_22) ); +} + +/* calculate the new memory offsets for a desired position */ +static void saa7146_set_position(struct saa7146_dev *dev, int w_x, int w_y, int w_height, enum v4l2_field field, u32 pixelformat) +{ + struct saa7146_vv *vv = dev->vv_data; + struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev, pixelformat); + + int b_depth = vv->ov_fmt->depth; + int b_bpl = vv->ov_fb.fmt.bytesperline; + /* The unsigned long cast is to remove a 64-bit compile warning since + it looks like a 64-bit address is cast to a 32-bit value, even + though the base pointer is really a 32-bit physical address that + goes into a 32-bit DMA register. + FIXME: might not work on some 64-bit platforms, but see the FIXME + in struct v4l2_framebuffer (videodev2.h) for that. + */ + u32 base = (u32)(unsigned long)vv->ov_fb.base; + + struct saa7146_video_dma vdma1; + + /* calculate memory offsets for picture, look if we shall top-down-flip */ + vdma1.pitch = 2*b_bpl; + if ( 0 == vv->vflip ) { + vdma1.base_even = base + (w_y * (vdma1.pitch/2)) + (w_x * (b_depth / 8)); + vdma1.base_odd = vdma1.base_even + (vdma1.pitch / 2); + vdma1.prot_addr = vdma1.base_even + (w_height * (vdma1.pitch / 2)); + } + else { + vdma1.base_even = base + ((w_y+w_height) * (vdma1.pitch/2)) + (w_x * (b_depth / 8)); + vdma1.base_odd = vdma1.base_even - (vdma1.pitch / 2); + vdma1.prot_addr = vdma1.base_odd - (w_height * (vdma1.pitch / 2)); + } + + if (V4L2_FIELD_HAS_BOTH(field)) { + } else if (field == V4L2_FIELD_ALTERNATE) { + /* fixme */ + vdma1.base_odd = vdma1.prot_addr; + vdma1.pitch /= 2; + } else if (field == V4L2_FIELD_TOP) { + vdma1.base_odd = vdma1.prot_addr; + vdma1.pitch /= 2; + } else if (field == V4L2_FIELD_BOTTOM) { + vdma1.base_odd = vdma1.base_even; + vdma1.base_even = vdma1.prot_addr; + vdma1.pitch /= 2; + } + + if ( 0 != vv->vflip ) { + vdma1.pitch *= -1; + } + + vdma1.base_page = sfmt->swap; + vdma1.num_line_byte = (vv->standard->v_field<<16)+vv->standard->h_pixels; + + saa7146_write_out_dma(dev, 1, &vdma1); +} + +static void saa7146_set_output_format(struct saa7146_dev *dev, unsigned long palette) +{ + u32 clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL); + + /* call helper function */ + calculate_output_format_register(dev,palette,&clip_format); + + /* update the hps registers */ + saa7146_write(dev, CLIP_FORMAT_CTRL, clip_format); + saa7146_write(dev, MC2, (MASK_05 | MASK_21)); +} + +/* select input-source */ +void saa7146_set_hps_source_and_sync(struct saa7146_dev *dev, int source, int sync) +{ + struct saa7146_vv *vv = dev->vv_data; + u32 hps_ctrl = 0; + + /* read old state */ + hps_ctrl = saa7146_read(dev, HPS_CTRL); + + hps_ctrl &= ~( MASK_31 | MASK_30 | MASK_28 ); + hps_ctrl |= (source << 30) | (sync << 28); + + /* write back & upload register */ + saa7146_write(dev, HPS_CTRL, hps_ctrl); + saa7146_write(dev, MC2, (MASK_05 | MASK_21)); + + vv->current_hps_source = source; + vv->current_hps_sync = sync; +} +EXPORT_SYMBOL_GPL(saa7146_set_hps_source_and_sync); + +int saa7146_enable_overlay(struct saa7146_fh *fh) +{ + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + + saa7146_set_window(dev, vv->ov.win.w.width, vv->ov.win.w.height, vv->ov.win.field); + saa7146_set_position(dev, vv->ov.win.w.left, vv->ov.win.w.top, vv->ov.win.w.height, vv->ov.win.field, vv->ov_fmt->pixelformat); + saa7146_set_output_format(dev, vv->ov_fmt->trans); + saa7146_set_clipping_rect(fh); + + /* enable video dma1 */ + saa7146_write(dev, MC1, (MASK_06 | MASK_22)); + return 0; +} + +void saa7146_disable_overlay(struct saa7146_fh *fh) +{ + struct saa7146_dev *dev = fh->dev; + + /* disable clipping + video dma1 */ + saa7146_disable_clipping(dev); + saa7146_write(dev, MC1, MASK_22); +} + +void saa7146_write_out_dma(struct saa7146_dev* dev, int which, struct saa7146_video_dma* vdma) +{ + int where = 0; + + if( which < 1 || which > 3) { + return; + } + + /* calculate starting address */ + where = (which-1)*0x18; + + saa7146_write(dev, where, vdma->base_odd); + saa7146_write(dev, where+0x04, vdma->base_even); + saa7146_write(dev, where+0x08, vdma->prot_addr); + saa7146_write(dev, where+0x0c, vdma->pitch); + saa7146_write(dev, where+0x10, vdma->base_page); + saa7146_write(dev, where+0x14, vdma->num_line_byte); + + /* upload */ + saa7146_write(dev, MC2, (MASK_02<<(which-1))|(MASK_18<<(which-1))); +/* + printk("vdma%d.base_even: 0x%08x\n", which,vdma->base_even); + printk("vdma%d.base_odd: 0x%08x\n", which,vdma->base_odd); + printk("vdma%d.prot_addr: 0x%08x\n", which,vdma->prot_addr); + printk("vdma%d.base_page: 0x%08x\n", which,vdma->base_page); + printk("vdma%d.pitch: 0x%08x\n", which,vdma->pitch); + printk("vdma%d.num_line_byte: 0x%08x\n", which,vdma->num_line_byte); +*/ +} + +static int calculate_video_dma_grab_packed(struct saa7146_dev* dev, struct saa7146_buf *buf) +{ + struct saa7146_vv *vv = dev->vv_data; + struct saa7146_video_dma vdma1; + + struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat); + + int width = buf->fmt->width; + int height = buf->fmt->height; + int bytesperline = buf->fmt->bytesperline; + enum v4l2_field field = buf->fmt->field; + + int depth = sfmt->depth; + + DEB_CAP("[size=%dx%d,fields=%s]\n", + width, height, v4l2_field_names[field]); + + if( bytesperline != 0) { + vdma1.pitch = bytesperline*2; + } else { + vdma1.pitch = (width*depth*2)/8; + } + vdma1.num_line_byte = ((vv->standard->v_field<<16) + vv->standard->h_pixels); + vdma1.base_page = buf->pt[0].dma | ME1 | sfmt->swap; + + if( 0 != vv->vflip ) { + vdma1.prot_addr = buf->pt[0].offset; + vdma1.base_even = buf->pt[0].offset+(vdma1.pitch/2)*height; + vdma1.base_odd = vdma1.base_even - (vdma1.pitch/2); + } else { + vdma1.base_even = buf->pt[0].offset; + vdma1.base_odd = vdma1.base_even + (vdma1.pitch/2); + vdma1.prot_addr = buf->pt[0].offset+(vdma1.pitch/2)*height; + } + + if (V4L2_FIELD_HAS_BOTH(field)) { + } else if (field == V4L2_FIELD_ALTERNATE) { + /* fixme */ + if ( vv->last_field == V4L2_FIELD_TOP ) { + vdma1.base_odd = vdma1.prot_addr; + vdma1.pitch /= 2; + } else if ( vv->last_field == V4L2_FIELD_BOTTOM ) { + vdma1.base_odd = vdma1.base_even; + vdma1.base_even = vdma1.prot_addr; + vdma1.pitch /= 2; + } + } else if (field == V4L2_FIELD_TOP) { + vdma1.base_odd = vdma1.prot_addr; + vdma1.pitch /= 2; + } else if (field == V4L2_FIELD_BOTTOM) { + vdma1.base_odd = vdma1.base_even; + vdma1.base_even = vdma1.prot_addr; + vdma1.pitch /= 2; + } + + if( 0 != vv->vflip ) { + vdma1.pitch *= -1; + } + + saa7146_write_out_dma(dev, 1, &vdma1); + return 0; +} + +static int calc_planar_422(struct saa7146_vv *vv, struct saa7146_buf *buf, struct saa7146_video_dma *vdma2, struct saa7146_video_dma *vdma3) +{ + int height = buf->fmt->height; + int width = buf->fmt->width; + + vdma2->pitch = width; + vdma3->pitch = width; + + /* fixme: look at bytesperline! */ + + if( 0 != vv->vflip ) { + vdma2->prot_addr = buf->pt[1].offset; + vdma2->base_even = ((vdma2->pitch/2)*height)+buf->pt[1].offset; + vdma2->base_odd = vdma2->base_even - (vdma2->pitch/2); + + vdma3->prot_addr = buf->pt[2].offset; + vdma3->base_even = ((vdma3->pitch/2)*height)+buf->pt[2].offset; + vdma3->base_odd = vdma3->base_even - (vdma3->pitch/2); + } else { + vdma3->base_even = buf->pt[2].offset; + vdma3->base_odd = vdma3->base_even + (vdma3->pitch/2); + vdma3->prot_addr = (vdma3->pitch/2)*height+buf->pt[2].offset; + + vdma2->base_even = buf->pt[1].offset; + vdma2->base_odd = vdma2->base_even + (vdma2->pitch/2); + vdma2->prot_addr = (vdma2->pitch/2)*height+buf->pt[1].offset; + } + + return 0; +} + +static int calc_planar_420(struct saa7146_vv *vv, struct saa7146_buf *buf, struct saa7146_video_dma *vdma2, struct saa7146_video_dma *vdma3) +{ + int height = buf->fmt->height; + int width = buf->fmt->width; + + vdma2->pitch = width/2; + vdma3->pitch = width/2; + + if( 0 != vv->vflip ) { + vdma2->prot_addr = buf->pt[2].offset; + vdma2->base_even = ((vdma2->pitch/2)*height)+buf->pt[2].offset; + vdma2->base_odd = vdma2->base_even - (vdma2->pitch/2); + + vdma3->prot_addr = buf->pt[1].offset; + vdma3->base_even = ((vdma3->pitch/2)*height)+buf->pt[1].offset; + vdma3->base_odd = vdma3->base_even - (vdma3->pitch/2); + + } else { + vdma3->base_even = buf->pt[2].offset; + vdma3->base_odd = vdma3->base_even + (vdma3->pitch); + vdma3->prot_addr = (vdma3->pitch/2)*height+buf->pt[2].offset; + + vdma2->base_even = buf->pt[1].offset; + vdma2->base_odd = vdma2->base_even + (vdma2->pitch); + vdma2->prot_addr = (vdma2->pitch/2)*height+buf->pt[1].offset; + } + return 0; +} + +static int calculate_video_dma_grab_planar(struct saa7146_dev* dev, struct saa7146_buf *buf) +{ + struct saa7146_vv *vv = dev->vv_data; + struct saa7146_video_dma vdma1; + struct saa7146_video_dma vdma2; + struct saa7146_video_dma vdma3; + + struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat); + + int width = buf->fmt->width; + int height = buf->fmt->height; + enum v4l2_field field = buf->fmt->field; + + BUG_ON(0 == buf->pt[0].dma); + BUG_ON(0 == buf->pt[1].dma); + BUG_ON(0 == buf->pt[2].dma); + + DEB_CAP("[size=%dx%d,fields=%s]\n", + width, height, v4l2_field_names[field]); + + /* fixme: look at bytesperline! */ + + /* fixme: what happens for user space buffers here?. The offsets are + most likely wrong, this version here only works for page-aligned + buffers, modifications to the pagetable-functions are necessary...*/ + + vdma1.pitch = width*2; + vdma1.num_line_byte = ((vv->standard->v_field<<16) + vv->standard->h_pixels); + vdma1.base_page = buf->pt[0].dma | ME1; + + if( 0 != vv->vflip ) { + vdma1.prot_addr = buf->pt[0].offset; + vdma1.base_even = ((vdma1.pitch/2)*height)+buf->pt[0].offset; + vdma1.base_odd = vdma1.base_even - (vdma1.pitch/2); + } else { + vdma1.base_even = buf->pt[0].offset; + vdma1.base_odd = vdma1.base_even + (vdma1.pitch/2); + vdma1.prot_addr = (vdma1.pitch/2)*height+buf->pt[0].offset; + } + + vdma2.num_line_byte = 0; /* unused */ + vdma2.base_page = buf->pt[1].dma | ME1; + + vdma3.num_line_byte = 0; /* unused */ + vdma3.base_page = buf->pt[2].dma | ME1; + + switch( sfmt->depth ) { + case 12: { + calc_planar_420(vv,buf,&vdma2,&vdma3); + break; + } + case 16: { + calc_planar_422(vv,buf,&vdma2,&vdma3); + break; + } + default: { + return -1; + } + } + + if (V4L2_FIELD_HAS_BOTH(field)) { + } else if (field == V4L2_FIELD_ALTERNATE) { + /* fixme */ + vdma1.base_odd = vdma1.prot_addr; + vdma1.pitch /= 2; + vdma2.base_odd = vdma2.prot_addr; + vdma2.pitch /= 2; + vdma3.base_odd = vdma3.prot_addr; + vdma3.pitch /= 2; + } else if (field == V4L2_FIELD_TOP) { + vdma1.base_odd = vdma1.prot_addr; + vdma1.pitch /= 2; + vdma2.base_odd = vdma2.prot_addr; + vdma2.pitch /= 2; + vdma3.base_odd = vdma3.prot_addr; + vdma3.pitch /= 2; + } else if (field == V4L2_FIELD_BOTTOM) { + vdma1.base_odd = vdma1.base_even; + vdma1.base_even = vdma1.prot_addr; + vdma1.pitch /= 2; + vdma2.base_odd = vdma2.base_even; + vdma2.base_even = vdma2.prot_addr; + vdma2.pitch /= 2; + vdma3.base_odd = vdma3.base_even; + vdma3.base_even = vdma3.prot_addr; + vdma3.pitch /= 2; + } + + if( 0 != vv->vflip ) { + vdma1.pitch *= -1; + vdma2.pitch *= -1; + vdma3.pitch *= -1; + } + + saa7146_write_out_dma(dev, 1, &vdma1); + if( (sfmt->flags & FORMAT_BYTE_SWAP) != 0 ) { + saa7146_write_out_dma(dev, 3, &vdma2); + saa7146_write_out_dma(dev, 2, &vdma3); + } else { + saa7146_write_out_dma(dev, 2, &vdma2); + saa7146_write_out_dma(dev, 3, &vdma3); + } + return 0; +} + +static void program_capture_engine(struct saa7146_dev *dev, int planar) +{ + struct saa7146_vv *vv = dev->vv_data; + int count = 0; + + unsigned long e_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_E_FID_A : CMD_E_FID_B; + unsigned long o_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_O_FID_A : CMD_O_FID_B; + + /* wait for o_fid_a/b / e_fid_a/b toggle only if rps register 0 is not set*/ + WRITE_RPS0(CMD_PAUSE | CMD_OAN | CMD_SIG0 | o_wait); + WRITE_RPS0(CMD_PAUSE | CMD_OAN | CMD_SIG0 | e_wait); + + /* set rps register 0 */ + WRITE_RPS0(CMD_WR_REG | (1 << 8) | (MC2/4)); + WRITE_RPS0(MASK_27 | MASK_11); + + /* turn on video-dma1 */ + WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4)); + WRITE_RPS0(MASK_06 | MASK_22); /* => mask */ + WRITE_RPS0(MASK_06 | MASK_22); /* => values */ + if( 0 != planar ) { + /* turn on video-dma2 */ + WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4)); + WRITE_RPS0(MASK_05 | MASK_21); /* => mask */ + WRITE_RPS0(MASK_05 | MASK_21); /* => values */ + + /* turn on video-dma3 */ + WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4)); + WRITE_RPS0(MASK_04 | MASK_20); /* => mask */ + WRITE_RPS0(MASK_04 | MASK_20); /* => values */ + } + + /* wait for o_fid_a/b / e_fid_a/b toggle */ + if ( vv->last_field == V4L2_FIELD_INTERLACED ) { + WRITE_RPS0(CMD_PAUSE | o_wait); + WRITE_RPS0(CMD_PAUSE | e_wait); + } else if ( vv->last_field == V4L2_FIELD_TOP ) { + WRITE_RPS0(CMD_PAUSE | (vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? MASK_10 : MASK_09)); + WRITE_RPS0(CMD_PAUSE | o_wait); + } else if ( vv->last_field == V4L2_FIELD_BOTTOM ) { + WRITE_RPS0(CMD_PAUSE | (vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? MASK_10 : MASK_09)); + WRITE_RPS0(CMD_PAUSE | e_wait); + } + + /* turn off video-dma1 */ + WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4)); + WRITE_RPS0(MASK_22 | MASK_06); /* => mask */ + WRITE_RPS0(MASK_22); /* => values */ + if( 0 != planar ) { + /* turn off video-dma2 */ + WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4)); + WRITE_RPS0(MASK_05 | MASK_21); /* => mask */ + WRITE_RPS0(MASK_21); /* => values */ + + /* turn off video-dma3 */ + WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4)); + WRITE_RPS0(MASK_04 | MASK_20); /* => mask */ + WRITE_RPS0(MASK_20); /* => values */ + } + + /* generate interrupt */ + WRITE_RPS0(CMD_INTERRUPT); + + /* stop */ + WRITE_RPS0(CMD_STOP); +} + +void saa7146_set_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struct saa7146_buf *next) +{ + struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat); + struct saa7146_vv *vv = dev->vv_data; + u32 vdma1_prot_addr; + + DEB_CAP("buf:%p, next:%p\n", buf, next); + + vdma1_prot_addr = saa7146_read(dev, PROT_ADDR1); + if( 0 == vdma1_prot_addr ) { + /* clear out beginning of streaming bit (rps register 0)*/ + DEB_CAP("forcing sync to new frame\n"); + saa7146_write(dev, MC2, MASK_27 ); + } + + saa7146_set_window(dev, buf->fmt->width, buf->fmt->height, buf->fmt->field); + saa7146_set_output_format(dev, sfmt->trans); + saa7146_disable_clipping(dev); + + if ( vv->last_field == V4L2_FIELD_INTERLACED ) { + } else if ( vv->last_field == V4L2_FIELD_TOP ) { + vv->last_field = V4L2_FIELD_BOTTOM; + } else if ( vv->last_field == V4L2_FIELD_BOTTOM ) { + vv->last_field = V4L2_FIELD_TOP; + } + + if( 0 != IS_PLANAR(sfmt->trans)) { + calculate_video_dma_grab_planar(dev, buf); + program_capture_engine(dev,1); + } else { + calculate_video_dma_grab_packed(dev, buf); + program_capture_engine(dev,0); + } + +/* + printk("vdma%d.base_even: 0x%08x\n", 1,saa7146_read(dev,BASE_EVEN1)); + printk("vdma%d.base_odd: 0x%08x\n", 1,saa7146_read(dev,BASE_ODD1)); + printk("vdma%d.prot_addr: 0x%08x\n", 1,saa7146_read(dev,PROT_ADDR1)); + printk("vdma%d.base_page: 0x%08x\n", 1,saa7146_read(dev,BASE_PAGE1)); + printk("vdma%d.pitch: 0x%08x\n", 1,saa7146_read(dev,PITCH1)); + printk("vdma%d.num_line_byte: 0x%08x\n", 1,saa7146_read(dev,NUM_LINE_BYTE1)); + printk("vdma%d => vptr : 0x%08x\n", 1,saa7146_read(dev,PCI_VDP1)); +*/ + + /* write the address of the rps-program */ + saa7146_write(dev, RPS_ADDR0, dev->d_rps0.dma_handle); + + /* turn on rps */ + saa7146_write(dev, MC1, (MASK_12 | MASK_28)); +} diff --git a/drivers/staging/media/deprecated/saa7146/common/saa7146_i2c.c b/drivers/staging/media/deprecated/saa7146/common/saa7146_i2c.c new file mode 100644 index 000000000000..7a33fe51775a --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/common/saa7146_i2c.c @@ -0,0 +1,421 @@ +// SPDX-License-Identifier: GPL-2.0 +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "saa7146_vv.h" + +static u32 saa7146_i2c_func(struct i2c_adapter *adapter) +{ + /* DEB_I2C("'%s'\n", adapter->name); */ + + return I2C_FUNC_I2C + | I2C_FUNC_SMBUS_QUICK + | I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE + | I2C_FUNC_SMBUS_READ_BYTE_DATA | I2C_FUNC_SMBUS_WRITE_BYTE_DATA; +} + +/* this function returns the status-register of our i2c-device */ +static inline u32 saa7146_i2c_status(struct saa7146_dev *dev) +{ + u32 iicsta = saa7146_read(dev, I2C_STATUS); + /* DEB_I2C("status: 0x%08x\n", iicsta); */ + return iicsta; +} + +/* this function runs through the i2c-messages and prepares the data to be + sent through the saa7146. have a look at the specifications p. 122 ff + to understand this. it returns the number of u32s to send, or -1 + in case of an error. */ +static int saa7146_i2c_msg_prepare(const struct i2c_msg *m, int num, __le32 *op) +{ + int h1, h2; + int i, j, addr; + int mem = 0, op_count = 0; + + /* first determine size of needed memory */ + for(i = 0; i < num; i++) { + mem += m[i].len + 1; + } + + /* worst case: we need one u32 for three bytes to be send + plus one extra byte to address the device */ + mem = 1 + ((mem-1) / 3); + + /* we assume that op points to a memory of at least + * SAA7146_I2C_MEM bytes size. if we exceed this limit... + */ + if ((4 * mem) > SAA7146_I2C_MEM) { + /* DEB_I2C("cannot prepare i2c-message\n"); */ + return -ENOMEM; + } + + /* be careful: clear out the i2c-mem first */ + memset(op,0,sizeof(__le32)*mem); + + /* loop through all messages */ + for(i = 0; i < num; i++) { + + addr = i2c_8bit_addr_from_msg(&m[i]); + h1 = op_count/3; h2 = op_count%3; + op[h1] |= cpu_to_le32( (u8)addr << ((3-h2)*8)); + op[h1] |= cpu_to_le32(SAA7146_I2C_START << ((3-h2)*2)); + op_count++; + + /* loop through all bytes of message i */ + for(j = 0; j < m[i].len; j++) { + /* insert the data bytes */ + h1 = op_count/3; h2 = op_count%3; + op[h1] |= cpu_to_le32( (u32)((u8)m[i].buf[j]) << ((3-h2)*8)); + op[h1] |= cpu_to_le32( SAA7146_I2C_CONT << ((3-h2)*2)); + op_count++; + } + + } + + /* have a look at the last byte inserted: + if it was: ...CONT change it to ...STOP */ + h1 = (op_count-1)/3; h2 = (op_count-1)%3; + if ( SAA7146_I2C_CONT == (0x3 & (le32_to_cpu(op[h1]) >> ((3-h2)*2))) ) { + op[h1] &= ~cpu_to_le32(0x2 << ((3-h2)*2)); + op[h1] |= cpu_to_le32(SAA7146_I2C_STOP << ((3-h2)*2)); + } + + /* return the number of u32s to send */ + return mem; +} + +/* this functions loops through all i2c-messages. normally, it should determine + which bytes were read through the adapter and write them back to the corresponding + i2c-message. but instead, we simply write back all bytes. + fixme: this could be improved. */ +static int saa7146_i2c_msg_cleanup(const struct i2c_msg *m, int num, __le32 *op) +{ + int i, j; + int op_count = 0; + + /* loop through all messages */ + for(i = 0; i < num; i++) { + + op_count++; + + /* loop through all bytes of message i */ + for(j = 0; j < m[i].len; j++) { + /* write back all bytes that could have been read */ + m[i].buf[j] = (le32_to_cpu(op[op_count/3]) >> ((3-(op_count%3))*8)); + op_count++; + } + } + + return 0; +} + +/* this functions resets the i2c-device and returns 0 if everything was fine, otherwise -1 */ +static int saa7146_i2c_reset(struct saa7146_dev *dev) +{ + /* get current status */ + u32 status = saa7146_i2c_status(dev); + + /* clear registers for sure */ + saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); + saa7146_write(dev, I2C_TRANSFER, 0); + + /* check if any operation is still in progress */ + if ( 0 != ( status & SAA7146_I2C_BUSY) ) { + + /* yes, kill ongoing operation */ + DEB_I2C("busy_state detected\n"); + + /* set "ABORT-OPERATION"-bit (bit 7)*/ + saa7146_write(dev, I2C_STATUS, (dev->i2c_bitrate | MASK_07)); + saa7146_write(dev, MC2, (MASK_00 | MASK_16)); + msleep(SAA7146_I2C_DELAY); + + /* clear all error-bits pending; this is needed because p.123, note 1 */ + saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); + saa7146_write(dev, MC2, (MASK_00 | MASK_16)); + msleep(SAA7146_I2C_DELAY); + } + + /* check if any error is (still) present. (this can be necessary because p.123, note 1) */ + status = saa7146_i2c_status(dev); + + if ( dev->i2c_bitrate != status ) { + + DEB_I2C("error_state detected. status:0x%08x\n", status); + + /* Repeat the abort operation. This seems to be necessary + after serious protocol errors caused by e.g. the SAA7740 */ + saa7146_write(dev, I2C_STATUS, (dev->i2c_bitrate | MASK_07)); + saa7146_write(dev, MC2, (MASK_00 | MASK_16)); + msleep(SAA7146_I2C_DELAY); + + /* clear all error-bits pending */ + saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); + saa7146_write(dev, MC2, (MASK_00 | MASK_16)); + msleep(SAA7146_I2C_DELAY); + + /* the data sheet says it might be necessary to clear the status + twice after an abort */ + saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); + saa7146_write(dev, MC2, (MASK_00 | MASK_16)); + msleep(SAA7146_I2C_DELAY); + } + + /* if any error is still present, a fatal error has occurred ... */ + status = saa7146_i2c_status(dev); + if ( dev->i2c_bitrate != status ) { + DEB_I2C("fatal error. status:0x%08x\n", status); + return -1; + } + + return 0; +} + +/* this functions writes out the data-byte 'dword' to the i2c-device. + it returns 0 if ok, -1 if the transfer failed, -2 if the transfer + failed badly (e.g. address error) */ +static int saa7146_i2c_writeout(struct saa7146_dev *dev, __le32 *dword, int short_delay) +{ + u32 status = 0, mc2 = 0; + int trial = 0; + unsigned long timeout; + + /* write out i2c-command */ + DEB_I2C("before: 0x%08x (status: 0x%08x), %d\n", + *dword, saa7146_read(dev, I2C_STATUS), dev->i2c_op); + + if( 0 != (SAA7146_USE_I2C_IRQ & dev->ext->flags)) { + + saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); + saa7146_write(dev, I2C_TRANSFER, le32_to_cpu(*dword)); + + dev->i2c_op = 1; + SAA7146_ISR_CLEAR(dev, MASK_16|MASK_17); + SAA7146_IER_ENABLE(dev, MASK_16|MASK_17); + saa7146_write(dev, MC2, (MASK_00 | MASK_16)); + + timeout = HZ/100 + 1; /* 10ms */ + timeout = wait_event_interruptible_timeout(dev->i2c_wq, dev->i2c_op == 0, timeout); + if (timeout == -ERESTARTSYS || dev->i2c_op) { + SAA7146_IER_DISABLE(dev, MASK_16|MASK_17); + SAA7146_ISR_CLEAR(dev, MASK_16|MASK_17); + if (timeout == -ERESTARTSYS) + /* a signal arrived */ + return -ERESTARTSYS; + + pr_warn("%s %s [irq]: timed out waiting for end of xfer\n", + dev->name, __func__); + return -EIO; + } + status = saa7146_read(dev, I2C_STATUS); + } else { + saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); + saa7146_write(dev, I2C_TRANSFER, le32_to_cpu(*dword)); + saa7146_write(dev, MC2, (MASK_00 | MASK_16)); + + /* do not poll for i2c-status before upload is complete */ + timeout = jiffies + HZ/100 + 1; /* 10ms */ + while(1) { + mc2 = (saa7146_read(dev, MC2) & 0x1); + if( 0 != mc2 ) { + break; + } + if (time_after(jiffies,timeout)) { + pr_warn("%s %s: timed out waiting for MC2\n", + dev->name, __func__); + return -EIO; + } + } + /* wait until we get a transfer done or error */ + timeout = jiffies + HZ/100 + 1; /* 10ms */ + /* first read usually delivers bogus results... */ + saa7146_i2c_status(dev); + while(1) { + status = saa7146_i2c_status(dev); + if ((status & 0x3) != 1) + break; + if (time_after(jiffies,timeout)) { + /* this is normal when probing the bus + * (no answer from nonexisistant device...) + */ + pr_warn("%s %s [poll]: timed out waiting for end of xfer\n", + dev->name, __func__); + return -EIO; + } + if (++trial < 50 && short_delay) + udelay(10); + else + msleep(1); + } + } + + /* give a detailed status report */ + if ( 0 != (status & (SAA7146_I2C_SPERR | SAA7146_I2C_APERR | + SAA7146_I2C_DTERR | SAA7146_I2C_DRERR | + SAA7146_I2C_AL | SAA7146_I2C_ERR | + SAA7146_I2C_BUSY)) ) { + + if ( 0 == (status & SAA7146_I2C_ERR) || + 0 == (status & SAA7146_I2C_BUSY) ) { + /* it may take some time until ERR goes high - ignore */ + DEB_I2C("unexpected i2c status %04x\n", status); + } + if( 0 != (status & SAA7146_I2C_SPERR) ) { + DEB_I2C("error due to invalid start/stop condition\n"); + } + if( 0 != (status & SAA7146_I2C_DTERR) ) { + DEB_I2C("error in data transmission\n"); + } + if( 0 != (status & SAA7146_I2C_DRERR) ) { + DEB_I2C("error when receiving data\n"); + } + if( 0 != (status & SAA7146_I2C_AL) ) { + DEB_I2C("error because arbitration lost\n"); + } + + /* we handle address-errors here */ + if( 0 != (status & SAA7146_I2C_APERR) ) { + DEB_I2C("error in address phase\n"); + return -EREMOTEIO; + } + + return -EIO; + } + + /* read back data, just in case we were reading ... */ + *dword = cpu_to_le32(saa7146_read(dev, I2C_TRANSFER)); + + DEB_I2C("after: 0x%08x\n", *dword); + return 0; +} + +static int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg *msgs, int num, int retries) +{ + int i = 0, count = 0; + __le32 *buffer = dev->d_i2c.cpu_addr; + int err = 0; + int short_delay = 0; + + if (mutex_lock_interruptible(&dev->i2c_lock)) + return -ERESTARTSYS; + + for(i=0;i<num;i++) { + DEB_I2C("msg:%d/%d\n", i+1, num); + } + + /* prepare the message(s), get number of u32s to transfer */ + count = saa7146_i2c_msg_prepare(msgs, num, buffer); + if ( 0 > count ) { + err = -EIO; + goto out; + } + + if ( count > 3 || 0 != (SAA7146_I2C_SHORT_DELAY & dev->ext->flags) ) + short_delay = 1; + + do { + /* reset the i2c-device if necessary */ + err = saa7146_i2c_reset(dev); + if ( 0 > err ) { + DEB_I2C("could not reset i2c-device\n"); + goto out; + } + + /* write out the u32s one after another */ + for(i = 0; i < count; i++) { + err = saa7146_i2c_writeout(dev, &buffer[i], short_delay); + if ( 0 != err) { + /* this one is unsatisfying: some i2c slaves on some + dvb cards don't acknowledge correctly, so the saa7146 + thinks that an address error occurred. in that case, the + transaction should be retrying, even if an address error + occurred. analog saa7146 based cards extensively rely on + i2c address probing, however, and address errors indicate that a + device is really *not* there. retrying in that case + increases the time the device needs to probe greatly, so + it should be avoided. So we bail out in irq mode after an + address error and trust the saa7146 address error detection. */ + if (-EREMOTEIO == err && 0 != (SAA7146_USE_I2C_IRQ & dev->ext->flags)) + goto out; + DEB_I2C("error while sending message(s). starting again\n"); + break; + } + } + if( 0 == err ) { + err = num; + break; + } + + /* delay a bit before retrying */ + msleep(10); + + } while (err != num && retries--); + + /* quit if any error occurred */ + if (err != num) + goto out; + + /* if any things had to be read, get the results */ + if ( 0 != saa7146_i2c_msg_cleanup(msgs, num, buffer)) { + DEB_I2C("could not cleanup i2c-message\n"); + err = -EIO; + goto out; + } + + /* return the number of delivered messages */ + DEB_I2C("transmission successful. (msg:%d)\n", err); +out: + /* another bug in revision 0: the i2c-registers get uploaded randomly by other + uploads, so we better clear them out before continuing */ + if( 0 == dev->revision ) { + __le32 zero = 0; + saa7146_i2c_reset(dev); + if( 0 != saa7146_i2c_writeout(dev, &zero, short_delay)) { + pr_info("revision 0 error. this should never happen\n"); + } + } + + mutex_unlock(&dev->i2c_lock); + return err; +} + +/* utility functions */ +static int saa7146_i2c_xfer(struct i2c_adapter* adapter, struct i2c_msg *msg, int num) +{ + struct v4l2_device *v4l2_dev = i2c_get_adapdata(adapter); + struct saa7146_dev *dev = to_saa7146_dev(v4l2_dev); + + /* use helper function to transfer data */ + return saa7146_i2c_transfer(dev, msg, num, adapter->retries); +} + + +/*****************************************************************************/ +/* i2c-adapter helper functions */ + +/* exported algorithm data */ +static const struct i2c_algorithm saa7146_algo = { + .master_xfer = saa7146_i2c_xfer, + .functionality = saa7146_i2c_func, +}; + +int saa7146_i2c_adapter_prepare(struct saa7146_dev *dev, struct i2c_adapter *i2c_adapter, u32 bitrate) +{ + DEB_EE("bitrate: 0x%08x\n", bitrate); + + /* enable i2c-port pins */ + saa7146_write(dev, MC1, (MASK_08 | MASK_24)); + + dev->i2c_bitrate = bitrate; + saa7146_i2c_reset(dev); + + if (i2c_adapter) { + i2c_set_adapdata(i2c_adapter, &dev->v4l2_dev); + i2c_adapter->dev.parent = &dev->pci->dev; + i2c_adapter->algo = &saa7146_algo; + i2c_adapter->algo_data = NULL; + i2c_adapter->timeout = SAA7146_I2C_TIMEOUT; + i2c_adapter->retries = SAA7146_I2C_RETRIES; + } + + return 0; +} diff --git a/drivers/staging/media/deprecated/saa7146/common/saa7146_vbi.c b/drivers/staging/media/deprecated/saa7146/common/saa7146_vbi.c new file mode 100644 index 000000000000..2d4a05d7bc5b --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/common/saa7146_vbi.c @@ -0,0 +1,498 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "saa7146_vv.h" + +static int vbi_pixel_to_capture = 720 * 2; + +static int vbi_workaround(struct saa7146_dev *dev) +{ + struct saa7146_vv *vv = dev->vv_data; + + u32 *cpu; + dma_addr_t dma_addr; + + int count = 0; + int i; + + DECLARE_WAITQUEUE(wait, current); + + DEB_VBI("dev:%p\n", dev); + + /* once again, a bug in the saa7146: the brs acquisition + is buggy and especially the BXO-counter does not work + as specified. there is this workaround, but please + don't let me explain it. ;-) */ + + cpu = dma_alloc_coherent(&dev->pci->dev, 4096, &dma_addr, GFP_KERNEL); + if (NULL == cpu) + return -ENOMEM; + + /* setup some basic programming, just for the workaround */ + saa7146_write(dev, BASE_EVEN3, dma_addr); + saa7146_write(dev, BASE_ODD3, dma_addr+vbi_pixel_to_capture); + saa7146_write(dev, PROT_ADDR3, dma_addr+4096); + saa7146_write(dev, PITCH3, vbi_pixel_to_capture); + saa7146_write(dev, BASE_PAGE3, 0x0); + saa7146_write(dev, NUM_LINE_BYTE3, (2<<16)|((vbi_pixel_to_capture)<<0)); + saa7146_write(dev, MC2, MASK_04|MASK_20); + + /* load brs-control register */ + WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4)); + /* BXO = 1h, BRS to outbound */ + WRITE_RPS1(0xc000008c); + /* wait for vbi_a or vbi_b*/ + if ( 0 != (SAA7146_USE_PORT_B_FOR_VBI & dev->ext_vv_data->flags)) { + DEB_D("...using port b\n"); + WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | CMD_E_FID_B); + WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | CMD_O_FID_B); +/* + WRITE_RPS1(CMD_PAUSE | MASK_09); +*/ + } else { + DEB_D("...using port a\n"); + WRITE_RPS1(CMD_PAUSE | MASK_10); + } + /* upload brs */ + WRITE_RPS1(CMD_UPLOAD | MASK_08); + /* load brs-control register */ + WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4)); + /* BYO = 1, BXO = NQBIL (=1728 for PAL, for NTSC this is 858*2) - NumByte3 (=1440) = 288 */ + WRITE_RPS1(((1728-(vbi_pixel_to_capture)) << 7) | MASK_19); + /* wait for brs_done */ + WRITE_RPS1(CMD_PAUSE | MASK_08); + /* upload brs */ + WRITE_RPS1(CMD_UPLOAD | MASK_08); + /* load video-dma3 NumLines3 and NumBytes3 */ + WRITE_RPS1(CMD_WR_REG | (1 << 8) | (NUM_LINE_BYTE3/4)); + /* dev->vbi_count*2 lines, 720 pixel (= 1440 Bytes) */ + WRITE_RPS1((2 << 16) | (vbi_pixel_to_capture)); + /* load brs-control register */ + WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4)); + /* Set BRS right: note: this is an experimental value for BXO (=> PAL!) */ + WRITE_RPS1((540 << 7) | (5 << 19)); // 5 == vbi_start + /* wait for brs_done */ + WRITE_RPS1(CMD_PAUSE | MASK_08); + /* upload brs and video-dma3*/ + WRITE_RPS1(CMD_UPLOAD | MASK_08 | MASK_04); + /* load mc2 register: enable dma3 */ + WRITE_RPS1(CMD_WR_REG | (1 << 8) | (MC1/4)); + WRITE_RPS1(MASK_20 | MASK_04); + /* generate interrupt */ + WRITE_RPS1(CMD_INTERRUPT); + /* stop rps1 */ + WRITE_RPS1(CMD_STOP); + + /* we have to do the workaround twice to be sure that + everything is ok */ + for(i = 0; i < 2; i++) { + + /* indicate to the irq handler that we do the workaround */ + saa7146_write(dev, MC2, MASK_31|MASK_15); + + saa7146_write(dev, NUM_LINE_BYTE3, (1<<16)|(2<<0)); + saa7146_write(dev, MC2, MASK_04|MASK_20); + + /* enable rps1 irqs */ + SAA7146_IER_ENABLE(dev,MASK_28); + + /* prepare to wait to be woken up by the irq-handler */ + add_wait_queue(&vv->vbi_wq, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + /* start rps1 to enable workaround */ + saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); + saa7146_write(dev, MC1, (MASK_13 | MASK_29)); + + schedule(); + + DEB_VBI("brs bug workaround %d/1\n", i); + + remove_wait_queue(&vv->vbi_wq, &wait); + __set_current_state(TASK_RUNNING); + + /* disable rps1 irqs */ + SAA7146_IER_DISABLE(dev,MASK_28); + + /* stop video-dma3 */ + saa7146_write(dev, MC1, MASK_20); + + if(signal_pending(current)) { + + DEB_VBI("aborted (rps:0x%08x)\n", + saa7146_read(dev, RPS_ADDR1)); + + /* stop rps1 for sure */ + saa7146_write(dev, MC1, MASK_29); + + dma_free_coherent(&dev->pci->dev, 4096, cpu, dma_addr); + return -EINTR; + } + } + + dma_free_coherent(&dev->pci->dev, 4096, cpu, dma_addr); + return 0; +} + +static void saa7146_set_vbi_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struct saa7146_buf *next) +{ + struct saa7146_vv *vv = dev->vv_data; + + struct saa7146_video_dma vdma3; + + int count = 0; + unsigned long e_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_E_FID_A : CMD_E_FID_B; + unsigned long o_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_O_FID_A : CMD_O_FID_B; + +/* + vdma3.base_even = 0xc8000000+2560*70; + vdma3.base_odd = 0xc8000000; + vdma3.prot_addr = 0xc8000000+2560*164; + vdma3.pitch = 2560; + vdma3.base_page = 0; + vdma3.num_line_byte = (64<<16)|((vbi_pixel_to_capture)<<0); // set above! +*/ + vdma3.base_even = buf->pt[2].offset; + vdma3.base_odd = buf->pt[2].offset + 16 * vbi_pixel_to_capture; + vdma3.prot_addr = buf->pt[2].offset + 16 * 2 * vbi_pixel_to_capture; + vdma3.pitch = vbi_pixel_to_capture; + vdma3.base_page = buf->pt[2].dma | ME1; + vdma3.num_line_byte = (16 << 16) | vbi_pixel_to_capture; + + saa7146_write_out_dma(dev, 3, &vdma3); + + /* write beginning of rps-program */ + count = 0; + + /* wait for o_fid_a/b / e_fid_a/b toggle only if bit 1 is not set */ + + /* we don't wait here for the first field anymore. this is different from the video + capture and might cause that the first buffer is only half filled (with only + one field). but since this is some sort of streaming data, this is not that negative. + but by doing this, we can use the whole engine from videobuf-dma-sg.c... */ + +/* + WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | e_wait); + WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | o_wait); +*/ + /* set bit 1 */ + WRITE_RPS1(CMD_WR_REG | (1 << 8) | (MC2/4)); + WRITE_RPS1(MASK_28 | MASK_12); + + /* turn on video-dma3 */ + WRITE_RPS1(CMD_WR_REG_MASK | (MC1/4)); + WRITE_RPS1(MASK_04 | MASK_20); /* => mask */ + WRITE_RPS1(MASK_04 | MASK_20); /* => values */ + + /* wait for o_fid_a/b / e_fid_a/b toggle */ + WRITE_RPS1(CMD_PAUSE | o_wait); + WRITE_RPS1(CMD_PAUSE | e_wait); + + /* generate interrupt */ + WRITE_RPS1(CMD_INTERRUPT); + + /* stop */ + WRITE_RPS1(CMD_STOP); + + /* enable rps1 irqs */ + SAA7146_IER_ENABLE(dev, MASK_28); + + /* write the address of the rps-program */ + saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); + + /* turn on rps */ + saa7146_write(dev, MC1, (MASK_13 | MASK_29)); +} + +static int buffer_activate(struct saa7146_dev *dev, + struct saa7146_buf *buf, + struct saa7146_buf *next) +{ + struct saa7146_vv *vv = dev->vv_data; + buf->vb.state = VIDEOBUF_ACTIVE; + + DEB_VBI("dev:%p, buf:%p, next:%p\n", dev, buf, next); + saa7146_set_vbi_capture(dev,buf,next); + + mod_timer(&vv->vbi_dmaq.timeout, jiffies+BUFFER_TIMEOUT); + return 0; +} + +static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,enum v4l2_field field) +{ + struct file *file = q->priv_data; + struct saa7146_fh *fh = file->private_data; + struct saa7146_dev *dev = fh->dev; + struct saa7146_buf *buf = (struct saa7146_buf *)vb; + + int err = 0; + int lines, llength, size; + + lines = 16 * 2 ; /* 2 fields */ + llength = vbi_pixel_to_capture; + size = lines * llength; + + DEB_VBI("vb:%p\n", vb); + + if (0 != buf->vb.baddr && buf->vb.bsize < size) { + DEB_VBI("size mismatch\n"); + return -EINVAL; + } + + if (buf->vb.size != size) + saa7146_dma_free(dev,q,buf); + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); + + buf->vb.width = llength; + buf->vb.height = lines; + buf->vb.size = size; + buf->vb.field = field; // FIXME: check this + + saa7146_pgtable_free(dev->pci, &buf->pt[2]); + saa7146_pgtable_alloc(dev->pci, &buf->pt[2]); + + err = videobuf_iolock(q,&buf->vb, NULL); + if (err) + goto oops; + err = saa7146_pgtable_build_single(dev->pci, &buf->pt[2], + dma->sglist, dma->sglen); + if (0 != err) + return err; + } + buf->vb.state = VIDEOBUF_PREPARED; + buf->activate = buffer_activate; + + return 0; + + oops: + DEB_VBI("error out\n"); + saa7146_dma_free(dev,q,buf); + + return err; +} + +static int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) +{ + int llength,lines; + + lines = 16 * 2 ; /* 2 fields */ + llength = vbi_pixel_to_capture; + + *size = lines * llength; + *count = 2; + + DEB_VBI("count:%d, size:%d\n", *count, *size); + + return 0; +} + +static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) +{ + struct file *file = q->priv_data; + struct saa7146_fh *fh = file->private_data; + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + struct saa7146_buf *buf = (struct saa7146_buf *)vb; + + DEB_VBI("vb:%p\n", vb); + saa7146_buffer_queue(dev, &vv->vbi_dmaq, buf); +} + +static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) +{ + struct file *file = q->priv_data; + struct saa7146_fh *fh = file->private_data; + struct saa7146_dev *dev = fh->dev; + struct saa7146_buf *buf = (struct saa7146_buf *)vb; + + DEB_VBI("vb:%p\n", vb); + saa7146_dma_free(dev,q,buf); +} + +static const struct videobuf_queue_ops vbi_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +/* ------------------------------------------------------------------ */ + +static void vbi_stop(struct saa7146_fh *fh, struct file *file) +{ + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + unsigned long flags; + DEB_VBI("dev:%p, fh:%p\n", dev, fh); + + spin_lock_irqsave(&dev->slock,flags); + + /* disable rps1 */ + saa7146_write(dev, MC1, MASK_29); + + /* disable rps1 irqs */ + SAA7146_IER_DISABLE(dev, MASK_28); + + /* shut down dma 3 transfers */ + saa7146_write(dev, MC1, MASK_20); + + if (vv->vbi_dmaq.curr) + saa7146_buffer_finish(dev, &vv->vbi_dmaq, VIDEOBUF_DONE); + + videobuf_queue_cancel(&fh->vbi_q); + + vv->vbi_streaming = NULL; + + del_timer(&vv->vbi_dmaq.timeout); + del_timer(&vv->vbi_read_timeout); + + spin_unlock_irqrestore(&dev->slock, flags); +} + +static void vbi_read_timeout(struct timer_list *t) +{ + struct saa7146_vv *vv = from_timer(vv, t, vbi_read_timeout); + struct file *file = vv->vbi_read_timeout_file; + struct saa7146_fh *fh = file->private_data; + struct saa7146_dev *dev = fh->dev; + + DEB_VBI("dev:%p, fh:%p\n", dev, fh); + + vbi_stop(fh, file); +} + +static void vbi_init(struct saa7146_dev *dev, struct saa7146_vv *vv) +{ + DEB_VBI("dev:%p\n", dev); + + INIT_LIST_HEAD(&vv->vbi_dmaq.queue); + + timer_setup(&vv->vbi_dmaq.timeout, saa7146_buffer_timeout, 0); + vv->vbi_dmaq.dev = dev; + + init_waitqueue_head(&vv->vbi_wq); +} + +static int vbi_open(struct saa7146_dev *dev, struct file *file) +{ + struct saa7146_fh *fh = file->private_data; + struct saa7146_vv *vv = fh->dev->vv_data; + + u32 arbtr_ctrl = saa7146_read(dev, PCI_BT_V1); + int ret = 0; + + DEB_VBI("dev:%p, fh:%p\n", dev, fh); + + ret = saa7146_res_get(fh, RESOURCE_DMA3_BRS); + if (0 == ret) { + DEB_S("cannot get vbi RESOURCE_DMA3_BRS resource\n"); + return -EBUSY; + } + + /* adjust arbitrition control for video dma 3 */ + arbtr_ctrl &= ~0x1f0000; + arbtr_ctrl |= 0x1d0000; + saa7146_write(dev, PCI_BT_V1, arbtr_ctrl); + saa7146_write(dev, MC2, (MASK_04|MASK_20)); + + videobuf_queue_sg_init(&fh->vbi_q, &vbi_qops, + &dev->pci->dev, &dev->slock, + V4L2_BUF_TYPE_VBI_CAPTURE, + V4L2_FIELD_SEQ_TB, // FIXME: does this really work? + sizeof(struct saa7146_buf), + file, &dev->v4l2_lock); + + vv->vbi_read_timeout.function = vbi_read_timeout; + vv->vbi_read_timeout_file = file; + + /* initialize the brs */ + if ( 0 != (SAA7146_USE_PORT_B_FOR_VBI & dev->ext_vv_data->flags)) { + saa7146_write(dev, BRS_CTRL, MASK_30|MASK_29 | (7 << 19)); + } else { + saa7146_write(dev, BRS_CTRL, 0x00000001); + + if (0 != (ret = vbi_workaround(dev))) { + DEB_VBI("vbi workaround failed!\n"); + /* return ret;*/ + } + } + + /* upload brs register */ + saa7146_write(dev, MC2, (MASK_08|MASK_24)); + return 0; +} + +static void vbi_close(struct saa7146_dev *dev, struct file *file) +{ + struct saa7146_fh *fh = file->private_data; + struct saa7146_vv *vv = dev->vv_data; + DEB_VBI("dev:%p, fh:%p\n", dev, fh); + + if( fh == vv->vbi_streaming ) { + vbi_stop(fh, file); + } + saa7146_res_free(fh, RESOURCE_DMA3_BRS); +} + +static void vbi_irq_done(struct saa7146_dev *dev, unsigned long status) +{ + struct saa7146_vv *vv = dev->vv_data; + spin_lock(&dev->slock); + + if (vv->vbi_dmaq.curr) { + DEB_VBI("dev:%p, curr:%p\n", dev, vv->vbi_dmaq.curr); + /* this must be += 2, one count for each field */ + vv->vbi_fieldcount+=2; + vv->vbi_dmaq.curr->vb.field_count = vv->vbi_fieldcount; + saa7146_buffer_finish(dev, &vv->vbi_dmaq, VIDEOBUF_DONE); + } else { + DEB_VBI("dev:%p\n", dev); + } + saa7146_buffer_next(dev, &vv->vbi_dmaq, 1); + + spin_unlock(&dev->slock); +} + +static ssize_t vbi_read(struct file *file, char __user *data, size_t count, loff_t *ppos) +{ + struct saa7146_fh *fh = file->private_data; + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + ssize_t ret = 0; + + DEB_VBI("dev:%p, fh:%p\n", dev, fh); + + if( NULL == vv->vbi_streaming ) { + // fixme: check if dma3 is available + // fixme: activate vbi engine here if necessary. (really?) + vv->vbi_streaming = fh; + } + + if( fh != vv->vbi_streaming ) { + DEB_VBI("open %p is already using vbi capture\n", + vv->vbi_streaming); + return -EBUSY; + } + + mod_timer(&vv->vbi_read_timeout, jiffies+BUFFER_TIMEOUT); + ret = videobuf_read_stream(&fh->vbi_q, data, count, ppos, 1, + file->f_flags & O_NONBLOCK); +/* + printk("BASE_ODD3: 0x%08x\n", saa7146_read(dev, BASE_ODD3)); + printk("BASE_EVEN3: 0x%08x\n", saa7146_read(dev, BASE_EVEN3)); + printk("PROT_ADDR3: 0x%08x\n", saa7146_read(dev, PROT_ADDR3)); + printk("PITCH3: 0x%08x\n", saa7146_read(dev, PITCH3)); + printk("BASE_PAGE3: 0x%08x\n", saa7146_read(dev, BASE_PAGE3)); + printk("NUM_LINE_BYTE3: 0x%08x\n", saa7146_read(dev, NUM_LINE_BYTE3)); + printk("BRS_CTRL: 0x%08x\n", saa7146_read(dev, BRS_CTRL)); +*/ + return ret; +} + +const struct saa7146_use_ops saa7146_vbi_uops = { + .init = vbi_init, + .open = vbi_open, + .release = vbi_close, + .irq_done = vbi_irq_done, + .read = vbi_read, +}; diff --git a/drivers/staging/media/deprecated/saa7146/common/saa7146_video.c b/drivers/staging/media/deprecated/saa7146/common/saa7146_video.c new file mode 100644 index 000000000000..4598a44231fa --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/common/saa7146_video.c @@ -0,0 +1,1286 @@ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <media/v4l2-event.h> +#include <media/v4l2-ctrls.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include "saa7146_vv.h" + +static int max_memory = 32; + +module_param(max_memory, int, 0644); +MODULE_PARM_DESC(max_memory, "maximum memory usage for capture buffers (default: 32Mb)"); + +#define IS_CAPTURE_ACTIVE(fh) \ + (((vv->video_status & STATUS_CAPTURE) != 0) && (vv->video_fh == fh)) + +#define IS_OVERLAY_ACTIVE(fh) \ + (((vv->video_status & STATUS_OVERLAY) != 0) && (vv->video_fh == fh)) + +/* format descriptions for capture and preview */ +static struct saa7146_format formats[] = { + { + .pixelformat = V4L2_PIX_FMT_RGB332, + .trans = RGB08_COMPOSED, + .depth = 8, + .flags = 0, + }, { + .pixelformat = V4L2_PIX_FMT_RGB565, + .trans = RGB16_COMPOSED, + .depth = 16, + .flags = 0, + }, { + .pixelformat = V4L2_PIX_FMT_BGR24, + .trans = RGB24_COMPOSED, + .depth = 24, + .flags = 0, + }, { + .pixelformat = V4L2_PIX_FMT_BGR32, + .trans = RGB32_COMPOSED, + .depth = 32, + .flags = 0, + }, { + .pixelformat = V4L2_PIX_FMT_RGB32, + .trans = RGB32_COMPOSED, + .depth = 32, + .flags = 0, + .swap = 0x2, + }, { + .pixelformat = V4L2_PIX_FMT_GREY, + .trans = Y8, + .depth = 8, + .flags = 0, + }, { + .pixelformat = V4L2_PIX_FMT_YUV422P, + .trans = YUV422_DECOMPOSED, + .depth = 16, + .flags = FORMAT_BYTE_SWAP|FORMAT_IS_PLANAR, + }, { + .pixelformat = V4L2_PIX_FMT_YVU420, + .trans = YUV420_DECOMPOSED, + .depth = 12, + .flags = FORMAT_BYTE_SWAP|FORMAT_IS_PLANAR, + }, { + .pixelformat = V4L2_PIX_FMT_YUV420, + .trans = YUV420_DECOMPOSED, + .depth = 12, + .flags = FORMAT_IS_PLANAR, + }, { + .pixelformat = V4L2_PIX_FMT_UYVY, + .trans = YUV422_COMPOSED, + .depth = 16, + .flags = 0, + } +}; + +/* unfortunately, the saa7146 contains a bug which prevents it from doing on-the-fly byte swaps. + due to this, it's impossible to provide additional *packed* formats, which are simply byte swapped + (like V4L2_PIX_FMT_YUYV) ... 8-( */ + +struct saa7146_format* saa7146_format_by_fourcc(struct saa7146_dev *dev, int fourcc) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + if (formats[i].pixelformat == fourcc) { + return formats+i; + } + } + + DEB_D("unknown pixelformat:'%4.4s'\n", (char *)&fourcc); + return NULL; +} + +static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f); + +int saa7146_start_preview(struct saa7146_fh *fh) +{ + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + struct v4l2_format fmt; + int ret = 0, err = 0; + + DEB_EE("dev:%p, fh:%p\n", dev, fh); + + /* check if we have overlay information */ + if (vv->ov.fh == NULL) { + DEB_D("no overlay data available. try S_FMT first.\n"); + return -EAGAIN; + } + + /* check if streaming capture is running */ + if (IS_CAPTURE_ACTIVE(fh) != 0) { + DEB_D("streaming capture is active\n"); + return -EBUSY; + } + + /* check if overlay is running */ + if (IS_OVERLAY_ACTIVE(fh) != 0) { + if (vv->video_fh == fh) { + DEB_D("overlay is already active\n"); + return 0; + } + DEB_D("overlay is already active in another open\n"); + return -EBUSY; + } + + if (0 == saa7146_res_get(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP)) { + DEB_D("cannot get necessary overlay resources\n"); + return -EBUSY; + } + + fmt.fmt.win = vv->ov.win; + err = vidioc_try_fmt_vid_overlay(NULL, fh, &fmt); + if (0 != err) { + saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP); + return -EBUSY; + } + vv->ov.win = fmt.fmt.win; + + DEB_D("%dx%d+%d+%d 0x%08x field=%s\n", + vv->ov.win.w.width, vv->ov.win.w.height, + vv->ov.win.w.left, vv->ov.win.w.top, + vv->ov_fmt->pixelformat, v4l2_field_names[vv->ov.win.field]); + + if (0 != (ret = saa7146_enable_overlay(fh))) { + DEB_D("enabling overlay failed: %d\n", ret); + saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP); + return ret; + } + + vv->video_status = STATUS_OVERLAY; + vv->video_fh = fh; + + return 0; +} +EXPORT_SYMBOL_GPL(saa7146_start_preview); + +int saa7146_stop_preview(struct saa7146_fh *fh) +{ + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + + DEB_EE("dev:%p, fh:%p\n", dev, fh); + + /* check if streaming capture is running */ + if (IS_CAPTURE_ACTIVE(fh) != 0) { + DEB_D("streaming capture is active\n"); + return -EBUSY; + } + + /* check if overlay is running at all */ + if ((vv->video_status & STATUS_OVERLAY) == 0) { + DEB_D("no active overlay\n"); + return 0; + } + + if (vv->video_fh != fh) { + DEB_D("overlay is active, but in another open\n"); + return -EBUSY; + } + + vv->video_status = 0; + vv->video_fh = NULL; + + saa7146_disable_overlay(fh); + + saa7146_res_free(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP); + + return 0; +} +EXPORT_SYMBOL_GPL(saa7146_stop_preview); + +/********************************************************************************/ +/* common pagetable functions */ + +static int saa7146_pgtable_build(struct saa7146_dev *dev, struct saa7146_buf *buf) +{ + struct pci_dev *pci = dev->pci; + struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); + struct scatterlist *list = dma->sglist; + int length = dma->sglen; + struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat); + + DEB_EE("dev:%p, buf:%p, sg_len:%d\n", dev, buf, length); + + if( 0 != IS_PLANAR(sfmt->trans)) { + struct saa7146_pgtable *pt1 = &buf->pt[0]; + struct saa7146_pgtable *pt2 = &buf->pt[1]; + struct saa7146_pgtable *pt3 = &buf->pt[2]; + __le32 *ptr1, *ptr2, *ptr3; + __le32 fill; + + int size = buf->fmt->width*buf->fmt->height; + int i,p,m1,m2,m3,o1,o2; + + switch( sfmt->depth ) { + case 12: { + /* create some offsets inside the page table */ + m1 = ((size+PAGE_SIZE)/PAGE_SIZE)-1; + m2 = ((size+(size/4)+PAGE_SIZE)/PAGE_SIZE)-1; + m3 = ((size+(size/2)+PAGE_SIZE)/PAGE_SIZE)-1; + o1 = size%PAGE_SIZE; + o2 = (size+(size/4))%PAGE_SIZE; + DEB_CAP("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n", + size, m1, m2, m3, o1, o2); + break; + } + case 16: { + /* create some offsets inside the page table */ + m1 = ((size+PAGE_SIZE)/PAGE_SIZE)-1; + m2 = ((size+(size/2)+PAGE_SIZE)/PAGE_SIZE)-1; + m3 = ((2*size+PAGE_SIZE)/PAGE_SIZE)-1; + o1 = size%PAGE_SIZE; + o2 = (size+(size/2))%PAGE_SIZE; + DEB_CAP("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n", + size, m1, m2, m3, o1, o2); + break; + } + default: { + return -1; + } + } + + ptr1 = pt1->cpu; + ptr2 = pt2->cpu; + ptr3 = pt3->cpu; + + /* walk all pages, copy all page addresses to ptr1 */ + for (i = 0; i < length; i++, list++) { + for (p = 0; p * 4096 < sg_dma_len(list); p++, ptr1++) + *ptr1 = cpu_to_le32(sg_dma_address(list) - list->offset); + } +/* + ptr1 = pt1->cpu; + for(j=0;j<40;j++) { + printk("ptr1 %d: 0x%08x\n",j,ptr1[j]); + } +*/ + + /* if we have a user buffer, the first page may not be + aligned to a page boundary. */ + pt1->offset = dma->sglist->offset; + pt2->offset = pt1->offset+o1; + pt3->offset = pt1->offset+o2; + + /* create video-dma2 page table */ + ptr1 = pt1->cpu; + for(i = m1; i <= m2 ; i++, ptr2++) { + *ptr2 = ptr1[i]; + } + fill = *(ptr2-1); + for(;i<1024;i++,ptr2++) { + *ptr2 = fill; + } + /* create video-dma3 page table */ + ptr1 = pt1->cpu; + for(i = m2; i <= m3; i++,ptr3++) { + *ptr3 = ptr1[i]; + } + fill = *(ptr3-1); + for(;i<1024;i++,ptr3++) { + *ptr3 = fill; + } + /* finally: finish up video-dma1 page table */ + ptr1 = pt1->cpu+m1; + fill = pt1->cpu[m1]; + for(i=m1;i<1024;i++,ptr1++) { + *ptr1 = fill; + } +/* + ptr1 = pt1->cpu; + ptr2 = pt2->cpu; + ptr3 = pt3->cpu; + for(j=0;j<40;j++) { + printk("ptr1 %d: 0x%08x\n",j,ptr1[j]); + } + for(j=0;j<40;j++) { + printk("ptr2 %d: 0x%08x\n",j,ptr2[j]); + } + for(j=0;j<40;j++) { + printk("ptr3 %d: 0x%08x\n",j,ptr3[j]); + } +*/ + } else { + struct saa7146_pgtable *pt = &buf->pt[0]; + return saa7146_pgtable_build_single(pci, pt, list, length); + } + + return 0; +} + + +/********************************************************************************/ +/* file operations */ + +static int video_begin(struct saa7146_fh *fh) +{ + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + struct saa7146_format *fmt = NULL; + unsigned int resource; + int ret = 0, err = 0; + + DEB_EE("dev:%p, fh:%p\n", dev, fh); + + if ((vv->video_status & STATUS_CAPTURE) != 0) { + if (vv->video_fh == fh) { + DEB_S("already capturing\n"); + return 0; + } + DEB_S("already capturing in another open\n"); + return -EBUSY; + } + + if ((vv->video_status & STATUS_OVERLAY) != 0) { + DEB_S("warning: suspending overlay video for streaming capture\n"); + vv->ov_suspend = vv->video_fh; + err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */ + if (0 != err) { + DEB_D("suspending video failed. aborting\n"); + return err; + } + } + + fmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat); + /* we need to have a valid format set here */ + if (!fmt) + return -EINVAL; + + if (0 != (fmt->flags & FORMAT_IS_PLANAR)) { + resource = RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP|RESOURCE_DMA3_BRS; + } else { + resource = RESOURCE_DMA1_HPS; + } + + ret = saa7146_res_get(fh, resource); + if (0 == ret) { + DEB_S("cannot get capture resource %d\n", resource); + if (vv->ov_suspend != NULL) { + saa7146_start_preview(vv->ov_suspend); + vv->ov_suspend = NULL; + } + return -EBUSY; + } + + /* clear out beginning of streaming bit (rps register 0)*/ + saa7146_write(dev, MC2, MASK_27 ); + + /* enable rps0 irqs */ + SAA7146_IER_ENABLE(dev, MASK_27); + + vv->video_fh = fh; + vv->video_status = STATUS_CAPTURE; + + return 0; +} + +static int video_end(struct saa7146_fh *fh, struct file *file) +{ + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + struct saa7146_dmaqueue *q = &vv->video_dmaq; + struct saa7146_format *fmt = NULL; + unsigned long flags; + unsigned int resource; + u32 dmas = 0; + DEB_EE("dev:%p, fh:%p\n", dev, fh); + + if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) { + DEB_S("not capturing\n"); + return 0; + } + + if (vv->video_fh != fh) { + DEB_S("capturing, but in another open\n"); + return -EBUSY; + } + + fmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat); + /* we need to have a valid format set here */ + if (!fmt) + return -EINVAL; + + if (0 != (fmt->flags & FORMAT_IS_PLANAR)) { + resource = RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP|RESOURCE_DMA3_BRS; + dmas = MASK_22 | MASK_21 | MASK_20; + } else { + resource = RESOURCE_DMA1_HPS; + dmas = MASK_22; + } + spin_lock_irqsave(&dev->slock,flags); + + /* disable rps0 */ + saa7146_write(dev, MC1, MASK_28); + + /* disable rps0 irqs */ + SAA7146_IER_DISABLE(dev, MASK_27); + + /* shut down all used video dma transfers */ + saa7146_write(dev, MC1, dmas); + + if (q->curr) + saa7146_buffer_finish(dev, q, VIDEOBUF_DONE); + + spin_unlock_irqrestore(&dev->slock, flags); + + vv->video_fh = NULL; + vv->video_status = 0; + + saa7146_res_free(fh, resource); + + if (vv->ov_suspend != NULL) { + saa7146_start_preview(vv->ov_suspend); + vv->ov_suspend = NULL; + } + + return 0; +} + +static int vidioc_querycap(struct file *file, void *fh, struct v4l2_capability *cap) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + + strscpy((char *)cap->driver, "saa7146 v4l2", sizeof(cap->driver)); + strscpy((char *)cap->card, dev->ext->name, sizeof(cap->card)); + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | + V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | + V4L2_CAP_DEVICE_CAPS; + cap->capabilities |= dev->ext_vv_data->capabilities; + return 0; +} + +static int vidioc_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct saa7146_vv *vv = dev->vv_data; + + *fb = vv->ov_fb; + fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; + fb->flags = V4L2_FBUF_FLAG_PRIMARY; + return 0; +} + +static int vidioc_s_fbuf(struct file *file, void *fh, const struct v4l2_framebuffer *fb) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct saa7146_vv *vv = dev->vv_data; + struct saa7146_format *fmt; + + DEB_EE("VIDIOC_S_FBUF\n"); + + if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO)) + return -EPERM; + + /* check args */ + fmt = saa7146_format_by_fourcc(dev, fb->fmt.pixelformat); + if (NULL == fmt) + return -EINVAL; + + /* planar formats are not allowed for overlay video, clipping and video dma would clash */ + if (fmt->flags & FORMAT_IS_PLANAR) + DEB_S("planar pixelformat '%4.4s' not allowed for overlay\n", + (char *)&fmt->pixelformat); + + /* check if overlay is running */ + if (IS_OVERLAY_ACTIVE(fh) != 0) { + if (vv->video_fh != fh) { + DEB_D("refusing to change framebuffer information while overlay is active in another open\n"); + return -EBUSY; + } + } + + /* ok, accept it */ + vv->ov_fb = *fb; + vv->ov_fmt = fmt; + + if (vv->ov_fb.fmt.bytesperline < vv->ov_fb.fmt.width) { + vv->ov_fb.fmt.bytesperline = vv->ov_fb.fmt.width * fmt->depth / 8; + DEB_D("setting bytesperline to %d\n", vv->ov_fb.fmt.bytesperline); + } + return 0; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + if (f->index >= ARRAY_SIZE(formats)) + return -EINVAL; + f->pixelformat = formats[f->index].pixelformat; + return 0; +} + +int saa7146_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct saa7146_dev *dev = container_of(ctrl->handler, + struct saa7146_dev, ctrl_handler); + struct saa7146_vv *vv = dev->vv_data; + u32 val; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + val = saa7146_read(dev, BCS_CTRL); + val &= 0x00ffffff; + val |= (ctrl->val << 24); + saa7146_write(dev, BCS_CTRL, val); + saa7146_write(dev, MC2, MASK_22 | MASK_06); + break; + + case V4L2_CID_CONTRAST: + val = saa7146_read(dev, BCS_CTRL); + val &= 0xff00ffff; + val |= (ctrl->val << 16); + saa7146_write(dev, BCS_CTRL, val); + saa7146_write(dev, MC2, MASK_22 | MASK_06); + break; + + case V4L2_CID_SATURATION: + val = saa7146_read(dev, BCS_CTRL); + val &= 0xffffff00; + val |= (ctrl->val << 0); + saa7146_write(dev, BCS_CTRL, val); + saa7146_write(dev, MC2, MASK_22 | MASK_06); + break; + + case V4L2_CID_HFLIP: + /* fixme: we can support changing VFLIP and HFLIP here... */ + if ((vv->video_status & STATUS_CAPTURE)) + return -EBUSY; + vv->hflip = ctrl->val; + break; + + case V4L2_CID_VFLIP: + if ((vv->video_status & STATUS_CAPTURE)) + return -EBUSY; + vv->vflip = ctrl->val; + break; + + default: + return -EINVAL; + } + + if ((vv->video_status & STATUS_OVERLAY) != 0) { /* CHECK: && (vv->video_fh == fh)) */ + struct saa7146_fh *fh = vv->video_fh; + + saa7146_stop_preview(fh); + saa7146_start_preview(fh); + } + return 0; +} + +static int vidioc_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *parm) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct saa7146_vv *vv = dev->vv_data; + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + parm->parm.capture.readbuffers = 1; + v4l2_video_std_frame_period(vv->standard->id, + &parm->parm.capture.timeperframe); + return 0; +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct saa7146_vv *vv = dev->vv_data; + + f->fmt.pix = vv->video_fmt; + return 0; +} + +static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct saa7146_vv *vv = dev->vv_data; + + f->fmt.win = vv->ov.win; + return 0; +} + +static int vidioc_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct saa7146_vv *vv = dev->vv_data; + + f->fmt.vbi = vv->vbi_fmt; + return 0; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct saa7146_vv *vv = dev->vv_data; + struct saa7146_format *fmt; + enum v4l2_field field; + int maxw, maxh; + int calc_bpl; + + DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n", dev, fh); + + fmt = saa7146_format_by_fourcc(dev, f->fmt.pix.pixelformat); + if (NULL == fmt) + return -EINVAL; + + field = f->fmt.pix.field; + maxw = vv->standard->h_max_out; + maxh = vv->standard->v_max_out; + + if (V4L2_FIELD_ANY == field) { + field = (f->fmt.pix.height > maxh / 2) + ? V4L2_FIELD_INTERLACED + : V4L2_FIELD_BOTTOM; + } + switch (field) { + case V4L2_FIELD_ALTERNATE: + vv->last_field = V4L2_FIELD_TOP; + maxh = maxh / 2; + break; + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + vv->last_field = V4L2_FIELD_INTERLACED; + maxh = maxh / 2; + break; + case V4L2_FIELD_INTERLACED: + vv->last_field = V4L2_FIELD_INTERLACED; + break; + default: + DEB_D("no known field mode '%d'\n", field); + return -EINVAL; + } + + f->fmt.pix.field = field; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + if (f->fmt.pix.width > maxw) + f->fmt.pix.width = maxw; + if (f->fmt.pix.height > maxh) + f->fmt.pix.height = maxh; + + calc_bpl = (f->fmt.pix.width * fmt->depth) / 8; + + if (f->fmt.pix.bytesperline < calc_bpl) + f->fmt.pix.bytesperline = calc_bpl; + + if (f->fmt.pix.bytesperline > (2 * PAGE_SIZE * fmt->depth) / 8) /* arbitrary constraint */ + f->fmt.pix.bytesperline = calc_bpl; + + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height; + DEB_D("w:%d, h:%d, bytesperline:%d, sizeimage:%d\n", + f->fmt.pix.width, f->fmt.pix.height, + f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); + + return 0; +} + + +static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct saa7146_vv *vv = dev->vv_data; + struct v4l2_window *win = &f->fmt.win; + enum v4l2_field field; + int maxw, maxh; + + DEB_EE("dev:%p\n", dev); + + if (NULL == vv->ov_fb.base) { + DEB_D("no fb base set\n"); + return -EINVAL; + } + if (NULL == vv->ov_fmt) { + DEB_D("no fb fmt set\n"); + return -EINVAL; + } + if (win->w.width < 48 || win->w.height < 32) { + DEB_D("min width/height. (%d,%d)\n", + win->w.width, win->w.height); + return -EINVAL; + } + if (win->clipcount > 16) { + DEB_D("clipcount too big\n"); + return -EINVAL; + } + + field = win->field; + maxw = vv->standard->h_max_out; + maxh = vv->standard->v_max_out; + + if (V4L2_FIELD_ANY == field) { + field = (win->w.height > maxh / 2) + ? V4L2_FIELD_INTERLACED + : V4L2_FIELD_TOP; + } + switch (field) { + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + case V4L2_FIELD_ALTERNATE: + maxh = maxh / 2; + break; + case V4L2_FIELD_INTERLACED: + break; + default: + DEB_D("no known field mode '%d'\n", field); + return -EINVAL; + } + + win->field = field; + if (win->w.width > maxw) + win->w.width = maxw; + if (win->w.height > maxh) + win->w.height = maxh; + + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *__fh, struct v4l2_format *f) +{ + struct saa7146_fh *fh = __fh; + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + int err; + + DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n", dev, fh); + if (IS_CAPTURE_ACTIVE(fh) != 0) { + DEB_EE("streaming capture is active\n"); + return -EBUSY; + } + err = vidioc_try_fmt_vid_cap(file, fh, f); + if (0 != err) + return err; + vv->video_fmt = f->fmt.pix; + DEB_EE("set to pixelformat '%4.4s'\n", + (char *)&vv->video_fmt.pixelformat); + return 0; +} + +static int vidioc_s_fmt_vid_overlay(struct file *file, void *__fh, struct v4l2_format *f) +{ + struct saa7146_fh *fh = __fh; + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + int err; + + DEB_EE("V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p\n", dev, fh); + err = vidioc_try_fmt_vid_overlay(file, fh, f); + if (0 != err) + return err; + vv->ov.win = f->fmt.win; + vv->ov.nclips = f->fmt.win.clipcount; + if (vv->ov.nclips > 16) + vv->ov.nclips = 16; + memcpy(vv->ov.clips, f->fmt.win.clips, + sizeof(struct v4l2_clip) * vv->ov.nclips); + + /* vv->ov.fh is used to indicate that we have valid overlay information, too */ + vv->ov.fh = fh; + + /* check if our current overlay is active */ + if (IS_OVERLAY_ACTIVE(fh) != 0) { + saa7146_stop_preview(fh); + saa7146_start_preview(fh); + } + return 0; +} + +static int vidioc_g_std(struct file *file, void *fh, v4l2_std_id *norm) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct saa7146_vv *vv = dev->vv_data; + + *norm = vv->standard->id; + return 0; +} + + /* the saa7146 supfhrts (used in conjunction with the saa7111a for example) + PAL / NTSC / SECAM. if your hardware does not (or does more) + -- override this function in your extension */ +/* + case VIDIOC_ENUMSTD: + { + struct v4l2_standard *e = arg; + if (e->index < 0 ) + return -EINVAL; + if( e->index < dev->ext_vv_data->num_stds ) { + DEB_EE("VIDIOC_ENUMSTD: index:%d\n", e->index); + v4l2_video_std_construct(e, dev->ext_vv_data->stds[e->index].id, dev->ext_vv_data->stds[e->index].name); + return 0; + } + return -EINVAL; + } + */ + +static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id id) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct saa7146_vv *vv = dev->vv_data; + int found = 0; + int err, i; + + DEB_EE("VIDIOC_S_STD\n"); + + if ((vv->video_status & STATUS_CAPTURE) == STATUS_CAPTURE) { + DEB_D("cannot change video standard while streaming capture is active\n"); + return -EBUSY; + } + + if ((vv->video_status & STATUS_OVERLAY) != 0) { + vv->ov_suspend = vv->video_fh; + err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */ + if (0 != err) { + DEB_D("suspending video failed. aborting\n"); + return err; + } + } + + for (i = 0; i < dev->ext_vv_data->num_stds; i++) + if (id & dev->ext_vv_data->stds[i].id) + break; + if (i != dev->ext_vv_data->num_stds) { + vv->standard = &dev->ext_vv_data->stds[i]; + if (NULL != dev->ext_vv_data->std_callback) + dev->ext_vv_data->std_callback(dev, vv->standard); + found = 1; + } + + if (vv->ov_suspend != NULL) { + saa7146_start_preview(vv->ov_suspend); + vv->ov_suspend = NULL; + } + + if (!found) { + DEB_EE("VIDIOC_S_STD: standard not found\n"); + return -EINVAL; + } + + DEB_EE("VIDIOC_S_STD: set to standard to '%s'\n", vv->standard->name); + return 0; +} + +static int vidioc_overlay(struct file *file, void *fh, unsigned int on) +{ + int err; + + DEB_D("VIDIOC_OVERLAY on:%d\n", on); + if (on) + err = saa7146_start_preview(fh); + else + err = saa7146_stop_preview(fh); + return err; +} + +static int vidioc_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffers *b) +{ + struct saa7146_fh *fh = __fh; + + if (b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return videobuf_reqbufs(&fh->video_q, b); + if (b->type == V4L2_BUF_TYPE_VBI_CAPTURE) + return videobuf_reqbufs(&fh->vbi_q, b); + return -EINVAL; +} + +static int vidioc_querybuf(struct file *file, void *__fh, struct v4l2_buffer *buf) +{ + struct saa7146_fh *fh = __fh; + + if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return videobuf_querybuf(&fh->video_q, buf); + if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE) + return videobuf_querybuf(&fh->vbi_q, buf); + return -EINVAL; +} + +static int vidioc_qbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) +{ + struct saa7146_fh *fh = __fh; + + if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return videobuf_qbuf(&fh->video_q, buf); + if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE) + return videobuf_qbuf(&fh->vbi_q, buf); + return -EINVAL; +} + +static int vidioc_dqbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) +{ + struct saa7146_fh *fh = __fh; + + if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return videobuf_dqbuf(&fh->video_q, buf, file->f_flags & O_NONBLOCK); + if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE) + return videobuf_dqbuf(&fh->vbi_q, buf, file->f_flags & O_NONBLOCK); + return -EINVAL; +} + +static int vidioc_streamon(struct file *file, void *__fh, enum v4l2_buf_type type) +{ + struct saa7146_fh *fh = __fh; + int err; + + DEB_D("VIDIOC_STREAMON, type:%d\n", type); + + err = video_begin(fh); + if (err) + return err; + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return videobuf_streamon(&fh->video_q); + if (type == V4L2_BUF_TYPE_VBI_CAPTURE) + return videobuf_streamon(&fh->vbi_q); + return -EINVAL; +} + +static int vidioc_streamoff(struct file *file, void *__fh, enum v4l2_buf_type type) +{ + struct saa7146_fh *fh = __fh; + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + int err; + + DEB_D("VIDIOC_STREAMOFF, type:%d\n", type); + + /* ugly: we need to copy some checks from video_end(), + because videobuf_streamoff() relies on the capture running. + check and fix this */ + if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) { + DEB_S("not capturing\n"); + return 0; + } + + if (vv->video_fh != fh) { + DEB_S("capturing, but in another open\n"); + return -EBUSY; + } + + err = -EINVAL; + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + err = videobuf_streamoff(&fh->video_q); + else if (type == V4L2_BUF_TYPE_VBI_CAPTURE) + err = videobuf_streamoff(&fh->vbi_q); + if (0 != err) { + DEB_D("warning: videobuf_streamoff() failed\n"); + video_end(fh, file); + } else { + err = video_end(fh, file); + } + return err; +} + +const struct v4l2_ioctl_ops saa7146_video_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_enum_fmt_vid_overlay = 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_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay, + .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay, + .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay, + + .vidioc_overlay = vidioc_overlay, + .vidioc_g_fbuf = vidioc_g_fbuf, + .vidioc_s_fbuf = vidioc_s_fbuf, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_g_std = vidioc_g_std, + .vidioc_s_std = vidioc_s_std, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_g_parm = vidioc_g_parm, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +const struct v4l2_ioctl_ops saa7146_vbi_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, + + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_g_std = vidioc_g_std, + .vidioc_s_std = vidioc_s_std, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_g_parm = vidioc_g_parm, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/*********************************************************************************/ +/* buffer handling functions */ + +static int buffer_activate (struct saa7146_dev *dev, + struct saa7146_buf *buf, + struct saa7146_buf *next) +{ + struct saa7146_vv *vv = dev->vv_data; + + buf->vb.state = VIDEOBUF_ACTIVE; + saa7146_set_capture(dev,buf,next); + + mod_timer(&vv->video_dmaq.timeout, jiffies+BUFFER_TIMEOUT); + return 0; +} + +static void release_all_pagetables(struct saa7146_dev *dev, struct saa7146_buf *buf) +{ + saa7146_pgtable_free(dev->pci, &buf->pt[0]); + saa7146_pgtable_free(dev->pci, &buf->pt[1]); + saa7146_pgtable_free(dev->pci, &buf->pt[2]); +} + +static int buffer_prepare(struct videobuf_queue *q, + struct videobuf_buffer *vb, enum v4l2_field field) +{ + struct file *file = q->priv_data; + struct saa7146_fh *fh = file->private_data; + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + struct saa7146_buf *buf = (struct saa7146_buf *)vb; + int size,err = 0; + + DEB_CAP("vbuf:%p\n", vb); + + /* sanity checks */ + if (vv->video_fmt.width < 48 || + vv->video_fmt.height < 32 || + vv->video_fmt.width > vv->standard->h_max_out || + vv->video_fmt.height > vv->standard->v_max_out) { + DEB_D("w (%d) / h (%d) out of bounds\n", + vv->video_fmt.width, vv->video_fmt.height); + return -EINVAL; + } + + size = vv->video_fmt.sizeimage; + if (0 != buf->vb.baddr && buf->vb.bsize < size) { + DEB_D("size mismatch\n"); + return -EINVAL; + } + + DEB_CAP("buffer_prepare [size=%dx%d,bytes=%d,fields=%s]\n", + vv->video_fmt.width, vv->video_fmt.height, + size, v4l2_field_names[vv->video_fmt.field]); + if (buf->vb.width != vv->video_fmt.width || + buf->vb.bytesperline != vv->video_fmt.bytesperline || + buf->vb.height != vv->video_fmt.height || + buf->vb.size != size || + buf->vb.field != field || + buf->vb.field != vv->video_fmt.field || + buf->fmt != &vv->video_fmt) { + saa7146_dma_free(dev,q,buf); + } + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + struct saa7146_format *sfmt; + + buf->vb.bytesperline = vv->video_fmt.bytesperline; + buf->vb.width = vv->video_fmt.width; + buf->vb.height = vv->video_fmt.height; + buf->vb.size = size; + buf->vb.field = field; + buf->fmt = &vv->video_fmt; + buf->vb.field = vv->video_fmt.field; + + sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat); + + release_all_pagetables(dev, buf); + if( 0 != IS_PLANAR(sfmt->trans)) { + saa7146_pgtable_alloc(dev->pci, &buf->pt[0]); + saa7146_pgtable_alloc(dev->pci, &buf->pt[1]); + saa7146_pgtable_alloc(dev->pci, &buf->pt[2]); + } else { + saa7146_pgtable_alloc(dev->pci, &buf->pt[0]); + } + + err = videobuf_iolock(q,&buf->vb, &vv->ov_fb); + if (err) + goto oops; + err = saa7146_pgtable_build(dev,buf); + if (err) + goto oops; + } + buf->vb.state = VIDEOBUF_PREPARED; + buf->activate = buffer_activate; + + return 0; + + oops: + DEB_D("error out\n"); + saa7146_dma_free(dev,q,buf); + + return err; +} + +static int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) +{ + struct file *file = q->priv_data; + struct saa7146_fh *fh = file->private_data; + struct saa7146_vv *vv = fh->dev->vv_data; + + if (0 == *count || *count > MAX_SAA7146_CAPTURE_BUFFERS) + *count = MAX_SAA7146_CAPTURE_BUFFERS; + + *size = vv->video_fmt.sizeimage; + + /* check if we exceed the "max_memory" parameter */ + if( (*count * *size) > (max_memory*1048576) ) { + *count = (max_memory*1048576) / *size; + } + + DEB_CAP("%d buffers, %d bytes each\n", *count, *size); + + return 0; +} + +static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) +{ + struct file *file = q->priv_data; + struct saa7146_fh *fh = file->private_data; + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + struct saa7146_buf *buf = (struct saa7146_buf *)vb; + + DEB_CAP("vbuf:%p\n", vb); + saa7146_buffer_queue(fh->dev, &vv->video_dmaq, buf); +} + +static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) +{ + struct file *file = q->priv_data; + struct saa7146_fh *fh = file->private_data; + struct saa7146_dev *dev = fh->dev; + struct saa7146_buf *buf = (struct saa7146_buf *)vb; + + DEB_CAP("vbuf:%p\n", vb); + + saa7146_dma_free(dev,q,buf); + + release_all_pagetables(dev, buf); +} + +static const struct videobuf_queue_ops video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +/********************************************************************************/ +/* file operations */ + +static void video_init(struct saa7146_dev *dev, struct saa7146_vv *vv) +{ + INIT_LIST_HEAD(&vv->video_dmaq.queue); + + timer_setup(&vv->video_dmaq.timeout, saa7146_buffer_timeout, 0); + vv->video_dmaq.dev = dev; + + /* set some default values */ + vv->standard = &dev->ext_vv_data->stds[0]; + + /* FIXME: what's this? */ + vv->current_hps_source = SAA7146_HPS_SOURCE_PORT_A; + vv->current_hps_sync = SAA7146_HPS_SYNC_PORT_A; +} + + +static int video_open(struct saa7146_dev *dev, struct file *file) +{ + struct saa7146_fh *fh = file->private_data; + + videobuf_queue_sg_init(&fh->video_q, &video_qops, + &dev->pci->dev, &dev->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_INTERLACED, + sizeof(struct saa7146_buf), + file, &dev->v4l2_lock); + + return 0; +} + + +static void video_close(struct saa7146_dev *dev, struct file *file) +{ + struct saa7146_fh *fh = file->private_data; + struct saa7146_vv *vv = dev->vv_data; + struct videobuf_queue *q = &fh->video_q; + + if (IS_CAPTURE_ACTIVE(fh) != 0) + video_end(fh, file); + else if (IS_OVERLAY_ACTIVE(fh) != 0) + saa7146_stop_preview(fh); + + videobuf_stop(q); + /* hmm, why is this function declared void? */ +} + + +static void video_irq_done(struct saa7146_dev *dev, unsigned long st) +{ + struct saa7146_vv *vv = dev->vv_data; + struct saa7146_dmaqueue *q = &vv->video_dmaq; + + spin_lock(&dev->slock); + DEB_CAP("called\n"); + + /* only finish the buffer if we have one... */ + if( NULL != q->curr ) { + saa7146_buffer_finish(dev,q,VIDEOBUF_DONE); + } + saa7146_buffer_next(dev,q,0); + + spin_unlock(&dev->slock); +} + +static ssize_t video_read(struct file *file, char __user *data, size_t count, loff_t *ppos) +{ + struct saa7146_fh *fh = file->private_data; + struct saa7146_dev *dev = fh->dev; + struct saa7146_vv *vv = dev->vv_data; + ssize_t ret = 0; + + DEB_EE("called\n"); + + if ((vv->video_status & STATUS_CAPTURE) != 0) { + /* fixme: should we allow read() captures while streaming capture? */ + if (vv->video_fh == fh) { + DEB_S("already capturing\n"); + return -EBUSY; + } + DEB_S("already capturing in another open\n"); + return -EBUSY; + } + + ret = video_begin(fh); + if( 0 != ret) { + goto out; + } + + ret = videobuf_read_one(&fh->video_q , data, count, ppos, + file->f_flags & O_NONBLOCK); + if (ret != 0) { + video_end(fh, file); + } else { + ret = video_end(fh, file); + } +out: + /* restart overlay if it was active before */ + if (vv->ov_suspend != NULL) { + saa7146_start_preview(vv->ov_suspend); + vv->ov_suspend = NULL; + } + + return ret; +} + +const struct saa7146_use_ops saa7146_video_uops = { + .init = video_init, + .open = video_open, + .release = video_close, + .irq_done = video_irq_done, + .read = video_read, +}; diff --git a/drivers/staging/media/deprecated/saa7146/common/saa7146_vv.h b/drivers/staging/media/deprecated/saa7146/common/saa7146_vv.h new file mode 100644 index 000000000000..d7bd916fe3ad --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/common/saa7146_vv.h @@ -0,0 +1,266 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __SAA7146_VV__ +#define __SAA7146_VV__ + +#include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-fh.h> +#include <media/videobuf-dma-sg.h> +#include "saa7146.h" + +#define MAX_SAA7146_CAPTURE_BUFFERS 32 /* arbitrary */ +#define BUFFER_TIMEOUT (HZ/2) /* 0.5 seconds */ + +#define WRITE_RPS0(x) do { \ + dev->d_rps0.cpu_addr[ count++ ] = cpu_to_le32(x); \ + } while (0); + +#define WRITE_RPS1(x) do { \ + dev->d_rps1.cpu_addr[ count++ ] = cpu_to_le32(x); \ + } while (0); + +struct saa7146_video_dma { + u32 base_odd; + u32 base_even; + u32 prot_addr; + u32 pitch; + u32 base_page; + u32 num_line_byte; +}; + +#define FORMAT_BYTE_SWAP 0x1 +#define FORMAT_IS_PLANAR 0x2 + +struct saa7146_format { + u32 pixelformat; + u32 trans; + u8 depth; + u8 flags; + u8 swap; +}; + +struct saa7146_standard +{ + char *name; + v4l2_std_id id; + + int v_offset; /* number of lines of vertical offset before processing */ + int v_field; /* number of lines in a field for HPS to process */ + + int h_offset; /* horizontal offset of processing window */ + int h_pixels; /* number of horizontal pixels to process */ + + int v_max_out; + int h_max_out; +}; + +/* buffer for one video/vbi frame */ +struct saa7146_buf { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + + /* saa7146 specific */ + struct v4l2_pix_format *fmt; + int (*activate)(struct saa7146_dev *dev, + struct saa7146_buf *buf, + struct saa7146_buf *next); + + /* page tables */ + struct saa7146_pgtable pt[3]; +}; + +struct saa7146_dmaqueue { + struct saa7146_dev *dev; + struct saa7146_buf *curr; + struct list_head queue; + struct timer_list timeout; +}; + +struct saa7146_overlay { + struct saa7146_fh *fh; + struct v4l2_window win; + struct v4l2_clip clips[16]; + int nclips; +}; + +/* per open data */ +struct saa7146_fh { + /* Must be the first field! */ + struct v4l2_fh fh; + struct saa7146_dev *dev; + + /* video capture */ + struct videobuf_queue video_q; + + /* vbi capture */ + struct videobuf_queue vbi_q; + + unsigned int resources; /* resource management for device open */ +}; + +#define STATUS_OVERLAY 0x01 +#define STATUS_CAPTURE 0x02 + +struct saa7146_vv +{ + /* vbi capture */ + struct saa7146_dmaqueue vbi_dmaq; + struct v4l2_vbi_format vbi_fmt; + struct timer_list vbi_read_timeout; + struct file *vbi_read_timeout_file; + /* vbi workaround interrupt queue */ + wait_queue_head_t vbi_wq; + int vbi_fieldcount; + struct saa7146_fh *vbi_streaming; + + int video_status; + struct saa7146_fh *video_fh; + + /* video overlay */ + struct saa7146_overlay ov; + struct v4l2_framebuffer ov_fb; + struct saa7146_format *ov_fmt; + struct saa7146_fh *ov_suspend; + + /* video capture */ + struct saa7146_dmaqueue video_dmaq; + struct v4l2_pix_format video_fmt; + enum v4l2_field last_field; + + /* common: fixme? shouldn't this be in saa7146_fh? + (this leads to a more complicated question: shall the driver + store the different settings (for example S_INPUT) for every open + and restore it appropriately, or should all settings be common for + all opens? currently, we do the latter, like all other + drivers do... */ + struct saa7146_standard *standard; + + int vflip; + int hflip; + int current_hps_source; + int current_hps_sync; + + struct saa7146_dma d_clipping; /* pointer to clipping memory */ + + unsigned int resources; /* resource management for device */ +}; + +/* flags */ +#define SAA7146_USE_PORT_B_FOR_VBI 0x2 /* use input port b for vbi hardware bug workaround */ + +struct saa7146_ext_vv +{ + /* information about the video capabilities of the device */ + int inputs; + int audios; + u32 capabilities; + int flags; + + /* additionally supported transmission standards */ + struct saa7146_standard *stds; + int num_stds; + int (*std_callback)(struct saa7146_dev*, struct saa7146_standard *); + + /* the extension can override this */ + struct v4l2_ioctl_ops vid_ops; + struct v4l2_ioctl_ops vbi_ops; + /* pointer to the saa7146 core ops */ + const struct v4l2_ioctl_ops *core_ops; + + struct v4l2_file_operations vbi_fops; +}; + +struct saa7146_use_ops { + void (*init)(struct saa7146_dev *, struct saa7146_vv *); + int(*open)(struct saa7146_dev *, struct file *); + void (*release)(struct saa7146_dev *, struct file *); + void (*irq_done)(struct saa7146_dev *, unsigned long status); + ssize_t (*read)(struct file *, char __user *, size_t, loff_t *); +}; + +/* from saa7146_fops.c */ +int saa7146_register_device(struct video_device *vid, struct saa7146_dev *dev, char *name, int type); +int saa7146_unregister_device(struct video_device *vid, struct saa7146_dev *dev); +void saa7146_buffer_finish(struct saa7146_dev *dev, struct saa7146_dmaqueue *q, int state); +void saa7146_buffer_next(struct saa7146_dev *dev, struct saa7146_dmaqueue *q,int vbi); +int saa7146_buffer_queue(struct saa7146_dev *dev, struct saa7146_dmaqueue *q, struct saa7146_buf *buf); +void saa7146_buffer_timeout(struct timer_list *t); +void saa7146_dma_free(struct saa7146_dev* dev,struct videobuf_queue *q, + struct saa7146_buf *buf); + +int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv); +int saa7146_vv_release(struct saa7146_dev* dev); + +/* from saa7146_hlp.c */ +int saa7146_enable_overlay(struct saa7146_fh *fh); +void saa7146_disable_overlay(struct saa7146_fh *fh); + +void saa7146_set_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struct saa7146_buf *next); +void saa7146_write_out_dma(struct saa7146_dev* dev, int which, struct saa7146_video_dma* vdma) ; +void saa7146_set_hps_source_and_sync(struct saa7146_dev *saa, int source, int sync); +void saa7146_set_gpio(struct saa7146_dev *saa, u8 pin, u8 data); + +/* from saa7146_video.c */ +extern const struct v4l2_ioctl_ops saa7146_video_ioctl_ops; +extern const struct v4l2_ioctl_ops saa7146_vbi_ioctl_ops; +extern const struct saa7146_use_ops saa7146_video_uops; +int saa7146_start_preview(struct saa7146_fh *fh); +int saa7146_stop_preview(struct saa7146_fh *fh); +long saa7146_video_do_ioctl(struct file *file, unsigned int cmd, void *arg); +int saa7146_s_ctrl(struct v4l2_ctrl *ctrl); + +/* from saa7146_vbi.c */ +extern const struct saa7146_use_ops saa7146_vbi_uops; + +/* resource management functions */ +int saa7146_res_get(struct saa7146_fh *fh, unsigned int bit); +void saa7146_res_free(struct saa7146_fh *fh, unsigned int bits); + +#define RESOURCE_DMA1_HPS 0x1 +#define RESOURCE_DMA2_CLP 0x2 +#define RESOURCE_DMA3_BRS 0x4 + +/* saa7146 source inputs */ +#define SAA7146_HPS_SOURCE_PORT_A 0x00 +#define SAA7146_HPS_SOURCE_PORT_B 0x01 +#define SAA7146_HPS_SOURCE_YPB_CPA 0x02 +#define SAA7146_HPS_SOURCE_YPA_CPB 0x03 + +/* sync inputs */ +#define SAA7146_HPS_SYNC_PORT_A 0x00 +#define SAA7146_HPS_SYNC_PORT_B 0x01 + +/* some memory sizes */ +/* max. 16 clipping rectangles */ +#define SAA7146_CLIPPING_MEM (16 * 4 * sizeof(u32)) + +/* some defines for the various clipping-modes */ +#define SAA7146_CLIPPING_RECT 0x4 +#define SAA7146_CLIPPING_RECT_INVERTED 0x5 +#define SAA7146_CLIPPING_MASK 0x6 +#define SAA7146_CLIPPING_MASK_INVERTED 0x7 + +/* output formats: each entry holds four information */ +#define RGB08_COMPOSED 0x0217 /* composed is used in the sense of "not-planar" */ +/* this means: planar?=0, yuv2rgb-conversation-mode=2, dither=yes(=1), format-mode = 7 */ +#define RGB15_COMPOSED 0x0213 +#define RGB16_COMPOSED 0x0210 +#define RGB24_COMPOSED 0x0201 +#define RGB32_COMPOSED 0x0202 + +#define Y8 0x0006 +#define YUV411_COMPOSED 0x0003 +#define YUV422_COMPOSED 0x0000 +/* this means: planar?=1, yuv2rgb-conversion-mode=0, dither=no(=0), format-mode = b */ +#define YUV411_DECOMPOSED 0x100b +#define YUV422_DECOMPOSED 0x1009 +#define YUV420_DECOMPOSED 0x100a + +#define IS_PLANAR(x) (x & 0xf000) + +/* misc defines */ +#define SAA7146_NO_SWAP (0x0) +#define SAA7146_TWO_BYTE_SWAP (0x1) +#define SAA7146_FOUR_BYTE_SWAP (0x2) + +#endif diff --git a/drivers/staging/media/deprecated/saa7146/saa7146/Kconfig b/drivers/staging/media/deprecated/saa7146/saa7146/Kconfig new file mode 100644 index 000000000000..228e8d3f8d2b --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/saa7146/Kconfig @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_HEXIUM_GEMINI + tristate "Hexium Gemini frame grabber (DEPRECATED)" + depends on PCI && VIDEO_DEV && I2C + select VIDEO_SAA7146_VV + help + This is a video4linux driver for the Hexium Gemini frame + grabber card by Hexium. Please note that the Gemini Dual + card is *not* fully supported. + + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + + To compile this driver as a module, choose M here: the + module will be called hexium_gemini. + +config VIDEO_HEXIUM_ORION + tristate "Hexium HV-PCI6 and Orion frame grabber (DEPRECATED)" + depends on PCI && VIDEO_DEV && I2C + select VIDEO_SAA7146_VV + help + This is a video4linux driver for the Hexium HV-PCI6 and + Orion frame grabber cards by Hexium. + + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + + To compile this driver as a module, choose M here: the + module will be called hexium_orion. + +config VIDEO_MXB + tristate "Siemens-Nixdorf 'Multimedia eXtension Board' (DEPRECATED)" + depends on PCI && VIDEO_DEV && I2C + select VIDEO_SAA7146_VV + select VIDEO_TUNER + select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_TDA9840 if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_TEA6415C if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_TEA6420 if MEDIA_SUBDRV_AUTOSELECT + help + This is a video4linux driver for the 'Multimedia eXtension Board' + TV card by Siemens-Nixdorf. + + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + + To compile this driver as a module, choose M here: the + module will be called mxb. diff --git a/drivers/staging/media/deprecated/saa7146/saa7146/Makefile b/drivers/staging/media/deprecated/saa7146/saa7146/Makefile new file mode 100644 index 000000000000..37c9336f83d5 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/saa7146/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_VIDEO_MXB) += mxb.o +obj-$(CONFIG_VIDEO_HEXIUM_ORION) += hexium_orion.o +obj-$(CONFIG_VIDEO_HEXIUM_GEMINI) += hexium_gemini.o + +ccflags-y += -I$(srctree)/drivers/media/i2c diff --git a/drivers/staging/media/deprecated/saa7146/saa7146/TODO b/drivers/staging/media/deprecated/saa7146/saa7146/TODO new file mode 100644 index 000000000000..c9ae2ec79cea --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/saa7146/TODO @@ -0,0 +1,7 @@ +The saa7146-based drivers are one of the few drivers still not using +the vb2 framework, so these drivers are now deprecated with the intent of +removing them altogether by the beginning of 2023. + +In order to keep these drivers they have to be converted to vb2. +If someone is interested in doing this work, then contact the +linux-media mailinglist (https://linuxtv.org/lists.php). diff --git a/drivers/staging/media/deprecated/saa7146/saa7146/hexium_gemini.c b/drivers/staging/media/deprecated/saa7146/saa7146/hexium_gemini.c new file mode 100644 index 000000000000..124e82bd4507 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/saa7146/hexium_gemini.c @@ -0,0 +1,425 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + hexium_gemini.c - v4l2 driver for Hexium Gemini frame grabber cards + + Visit http://www.mihu.de/linux/saa7146/ and follow the link + to "hexium" for further details about this card. + + Copyright (C) 2003 Michael Hunold <michael@mihu.de> + +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define DEBUG_VARIABLE debug + +#include <linux/module.h> +#include <linux/kernel.h> +#include "../common/saa7146_vv.h" + +static int debug; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "debug verbosity"); + +/* global variables */ +static int hexium_num; + +#define HEXIUM_GEMINI 4 +#define HEXIUM_GEMINI_DUAL 5 + +#define HEXIUM_INPUTS 9 +static struct v4l2_input hexium_inputs[HEXIUM_INPUTS] = { + { 0, "CVBS 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 1, "CVBS 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 2, "CVBS 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 3, "CVBS 4", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 4, "CVBS 5", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 5, "CVBS 6", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 6, "Y/C 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 7, "Y/C 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 8, "Y/C 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, +}; + +#define HEXIUM_AUDIOS 0 + +struct hexium_data +{ + s8 adr; + u8 byte; +}; + +#define HEXIUM_GEMINI_V_1_0 1 +#define HEXIUM_GEMINI_DUAL_V_1_0 2 + +struct hexium +{ + int type; + + struct video_device video_dev; + struct i2c_adapter i2c_adapter; + + int cur_input; /* current input */ + v4l2_std_id cur_std; /* current standard */ +}; + +/* Samsung KS0127B decoder default registers */ +static u8 hexium_ks0127b[0x100]={ +/*00*/ 0x00,0x52,0x30,0x40,0x01,0x0C,0x2A,0x10, +/*08*/ 0x00,0x00,0x00,0x60,0x00,0x00,0x0F,0x06, +/*10*/ 0x00,0x00,0xE4,0xC0,0x00,0x00,0x00,0x00, +/*18*/ 0x14,0x9B,0xFE,0xFF,0xFC,0xFF,0x03,0x22, +/*20*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*28*/ 0x00,0x00,0x00,0x00,0x00,0x2C,0x9B,0x00, +/*30*/ 0x00,0x00,0x10,0x80,0x80,0x10,0x80,0x80, +/*38*/ 0x01,0x04,0x00,0x00,0x00,0x29,0xC0,0x00, +/*40*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*48*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*50*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*58*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*60*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*68*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*70*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*78*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*80*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*88*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*90*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*98*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*A0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*A8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*B0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*B8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*C0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*C8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*D0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*D8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*E0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*E8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*F0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*F8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +}; + +static struct hexium_data hexium_pal[] = { + { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF } +}; + +static struct hexium_data hexium_ntsc[] = { + { 0x01, 0x53 }, { 0x12, 0x04 }, { 0x2D, 0x23 }, { 0x2E, 0x81 }, { -1 , 0xFF } +}; + +static struct hexium_data hexium_secam[] = { + { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF } +}; + +static struct hexium_data hexium_input_select[] = { + { 0x02, 0x60 }, + { 0x02, 0x64 }, + { 0x02, 0x61 }, + { 0x02, 0x65 }, + { 0x02, 0x62 }, + { 0x02, 0x66 }, + { 0x02, 0x68 }, + { 0x02, 0x69 }, + { 0x02, 0x6A }, +}; + +/* fixme: h_offset = 0 for Hexium Gemini *Dual*, which + are currently *not* supported*/ +static struct saa7146_standard hexium_standards[] = { + { + .name = "PAL", .id = V4L2_STD_PAL, + .v_offset = 28, .v_field = 288, + .h_offset = 1, .h_pixels = 680, + .v_max_out = 576, .h_max_out = 768, + }, { + .name = "NTSC", .id = V4L2_STD_NTSC, + .v_offset = 28, .v_field = 240, + .h_offset = 1, .h_pixels = 640, + .v_max_out = 480, .h_max_out = 640, + }, { + .name = "SECAM", .id = V4L2_STD_SECAM, + .v_offset = 28, .v_field = 288, + .h_offset = 1, .h_pixels = 720, + .v_max_out = 576, .h_max_out = 768, + } +}; + +/* bring hardware to a sane state. this has to be done, just in case someone + wants to capture from this device before it has been properly initialized. + the capture engine would badly fail, because no valid signal arrives on the + saa7146, thus leading to timeouts and stuff. */ +static int hexium_init_done(struct saa7146_dev *dev) +{ + struct hexium *hexium = (struct hexium *) dev->ext_priv; + union i2c_smbus_data data; + int i = 0; + + DEB_D("hexium_init_done called\n"); + + /* initialize the helper ics to useful values */ + for (i = 0; i < sizeof(hexium_ks0127b); i++) { + data.byte = hexium_ks0127b[i]; + if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, i, I2C_SMBUS_BYTE_DATA, &data)) { + pr_err("hexium_init_done() failed for address 0x%02x\n", + i); + } + } + + return 0; +} + +static int hexium_set_input(struct hexium *hexium, int input) +{ + union i2c_smbus_data data; + + DEB_D("\n"); + + data.byte = hexium_input_select[input].byte; + if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, hexium_input_select[input].adr, I2C_SMBUS_BYTE_DATA, &data)) { + return -1; + } + + return 0; +} + +static int hexium_set_standard(struct hexium *hexium, struct hexium_data *vdec) +{ + union i2c_smbus_data data; + int i = 0; + + DEB_D("\n"); + + while (vdec[i].adr != -1) { + data.byte = vdec[i].byte; + if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, vdec[i].adr, I2C_SMBUS_BYTE_DATA, &data)) { + pr_err("hexium_init_done: hexium_set_standard() failed for address 0x%02x\n", + i); + return -1; + } + i++; + } + return 0; +} + +static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) +{ + DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index); + + if (i->index >= HEXIUM_INPUTS) + return -EINVAL; + + memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input)); + + DEB_D("v4l2_ioctl: VIDIOC_ENUMINPUT %d\n", i->index); + return 0; +} + +static int vidioc_g_input(struct file *file, void *fh, unsigned int *input) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct hexium *hexium = (struct hexium *) dev->ext_priv; + + *input = hexium->cur_input; + + DEB_D("VIDIOC_G_INPUT: %d\n", *input); + return 0; +} + +static int vidioc_s_input(struct file *file, void *fh, unsigned int input) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct hexium *hexium = (struct hexium *) dev->ext_priv; + + DEB_EE("VIDIOC_S_INPUT %d\n", input); + + if (input >= HEXIUM_INPUTS) + return -EINVAL; + + hexium->cur_input = input; + hexium_set_input(hexium, input); + return 0; +} + +static struct saa7146_ext_vv vv_data; + +/* this function only gets called when the probing was successful */ +static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) +{ + struct hexium *hexium; + int ret; + + DEB_EE("\n"); + + hexium = kzalloc(sizeof(*hexium), GFP_KERNEL); + if (!hexium) + return -ENOMEM; + + dev->ext_priv = hexium; + + /* enable i2c-port pins */ + saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26)); + + strscpy(hexium->i2c_adapter.name, "hexium gemini", + sizeof(hexium->i2c_adapter.name)); + saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480); + if (i2c_add_adapter(&hexium->i2c_adapter) < 0) { + DEB_S("cannot register i2c-device. skipping.\n"); + kfree(hexium); + return -EFAULT; + } + + /* set HWControl GPIO number 2 */ + saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI); + + saa7146_write(dev, DD1_INIT, 0x07000700); + saa7146_write(dev, DD1_STREAM_B, 0x00000000); + saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + + /* the rest */ + hexium->cur_input = 0; + hexium_init_done(dev); + + hexium_set_standard(hexium, hexium_pal); + hexium->cur_std = V4L2_STD_PAL; + + hexium_set_input(hexium, 0); + hexium->cur_input = 0; + + ret = saa7146_vv_init(dev, &vv_data); + if (ret) { + i2c_del_adapter(&hexium->i2c_adapter); + kfree(hexium); + return ret; + } + + vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; + vv_data.vid_ops.vidioc_g_input = vidioc_g_input; + vv_data.vid_ops.vidioc_s_input = vidioc_s_input; + ret = saa7146_register_device(&hexium->video_dev, dev, "hexium gemini", VFL_TYPE_VIDEO); + if (ret < 0) { + pr_err("cannot register capture v4l2 device. skipping.\n"); + saa7146_vv_release(dev); + i2c_del_adapter(&hexium->i2c_adapter); + kfree(hexium); + return ret; + } + + pr_info("found 'hexium gemini' frame grabber-%d\n", hexium_num); + hexium_num++; + + return 0; +} + +static int hexium_detach(struct saa7146_dev *dev) +{ + struct hexium *hexium = (struct hexium *) dev->ext_priv; + + DEB_EE("dev:%p\n", dev); + + saa7146_unregister_device(&hexium->video_dev, dev); + saa7146_vv_release(dev); + + hexium_num--; + + i2c_del_adapter(&hexium->i2c_adapter); + kfree(hexium); + return 0; +} + +static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std) +{ + struct hexium *hexium = (struct hexium *) dev->ext_priv; + + if (V4L2_STD_PAL == std->id) { + hexium_set_standard(hexium, hexium_pal); + hexium->cur_std = V4L2_STD_PAL; + return 0; + } else if (V4L2_STD_NTSC == std->id) { + hexium_set_standard(hexium, hexium_ntsc); + hexium->cur_std = V4L2_STD_NTSC; + return 0; + } else if (V4L2_STD_SECAM == std->id) { + hexium_set_standard(hexium, hexium_secam); + hexium->cur_std = V4L2_STD_SECAM; + return 0; + } + + return -1; +} + +static struct saa7146_extension hexium_extension; + +static struct saa7146_pci_extension_data hexium_gemini_4bnc = { + .ext_priv = "Hexium Gemini (4 BNC)", + .ext = &hexium_extension, +}; + +static struct saa7146_pci_extension_data hexium_gemini_dual_4bnc = { + .ext_priv = "Hexium Gemini Dual (4 BNC)", + .ext = &hexium_extension, +}; + +static const struct pci_device_id pci_tbl[] = { + { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7146, + .subvendor = 0x17c8, + .subdevice = 0x2401, + .driver_data = (unsigned long) &hexium_gemini_4bnc, + }, + { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7146, + .subvendor = 0x17c8, + .subdevice = 0x2402, + .driver_data = (unsigned long) &hexium_gemini_dual_4bnc, + }, + { + .vendor = 0, + } +}; + +MODULE_DEVICE_TABLE(pci, pci_tbl); + +static struct saa7146_ext_vv vv_data = { + .inputs = HEXIUM_INPUTS, + .capabilities = 0, + .stds = &hexium_standards[0], + .num_stds = ARRAY_SIZE(hexium_standards), + .std_callback = &std_callback, +}; + +static struct saa7146_extension hexium_extension = { + .name = "hexium gemini", + .flags = SAA7146_USE_I2C_IRQ, + + .pci_tbl = &pci_tbl[0], + .module = THIS_MODULE, + + .attach = hexium_attach, + .detach = hexium_detach, + + .irq_mask = 0, + .irq_func = NULL, +}; + +static int __init hexium_init_module(void) +{ + if (0 != saa7146_register_extension(&hexium_extension)) { + DEB_S("failed to register extension\n"); + return -ENODEV; + } + + return 0; +} + +static void __exit hexium_cleanup_module(void) +{ + saa7146_unregister_extension(&hexium_extension); +} + +module_init(hexium_init_module); +module_exit(hexium_cleanup_module); + +MODULE_DESCRIPTION("video4linux-2 driver for Hexium Gemini frame grabber cards"); +MODULE_AUTHOR("Michael Hunold <michael@mihu.de>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/deprecated/saa7146/saa7146/hexium_orion.c b/drivers/staging/media/deprecated/saa7146/saa7146/hexium_orion.c new file mode 100644 index 000000000000..ebd63998ac79 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/saa7146/hexium_orion.c @@ -0,0 +1,496 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + hexium_orion.c - v4l2 driver for the Hexium Orion frame grabber cards + + Visit http://www.mihu.de/linux/saa7146/ and follow the link + to "hexium" for further details about this card. + + Copyright (C) 2003 Michael Hunold <michael@mihu.de> + +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define DEBUG_VARIABLE debug + +#include <linux/module.h> +#include <linux/kernel.h> +#include "../common/saa7146_vv.h" + +static int debug; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "debug verbosity"); + +/* global variables */ +static int hexium_num; + +#define HEXIUM_HV_PCI6_ORION 1 +#define HEXIUM_ORION_1SVHS_3BNC 2 +#define HEXIUM_ORION_4BNC 3 + +#define HEXIUM_INPUTS 9 +static struct v4l2_input hexium_inputs[HEXIUM_INPUTS] = { + { 0, "CVBS 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 1, "CVBS 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 2, "CVBS 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 3, "CVBS 4", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 4, "CVBS 5", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 5, "CVBS 6", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 6, "Y/C 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 7, "Y/C 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 8, "Y/C 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, +}; + +#define HEXIUM_AUDIOS 0 + +struct hexium_data +{ + s8 adr; + u8 byte; +}; + +struct hexium +{ + int type; + struct video_device video_dev; + struct i2c_adapter i2c_adapter; + + int cur_input; /* current input */ +}; + +/* Philips SAA7110 decoder default registers */ +static u8 hexium_saa7110[53]={ +/*00*/ 0x4C,0x3C,0x0D,0xEF,0xBD,0xF0,0x00,0x00, +/*08*/ 0xF8,0xF8,0x60,0x60,0x40,0x86,0x18,0x90, +/*10*/ 0x00,0x2C,0x40,0x46,0x42,0x1A,0xFF,0xDA, +/*18*/ 0xF0,0x8B,0x00,0x00,0x00,0x00,0x00,0x00, +/*20*/ 0xD9,0x17,0x40,0x41,0x80,0x41,0x80,0x4F, +/*28*/ 0xFE,0x01,0x0F,0x0F,0x03,0x01,0x81,0x03, +/*30*/ 0x44,0x75,0x01,0x8C,0x03 +}; + +static struct { + struct hexium_data data[8]; +} hexium_input_select[] = { +{ + { /* cvbs 1 */ + { 0x06, 0x00 }, + { 0x20, 0xD9 }, + { 0x21, 0x17 }, // 0x16, + { 0x22, 0x40 }, + { 0x2C, 0x03 }, + { 0x30, 0x44 }, + { 0x31, 0x75 }, // ?? + { 0x21, 0x16 }, // 0x03, + } +}, { + { /* cvbs 2 */ + { 0x06, 0x00 }, + { 0x20, 0x78 }, + { 0x21, 0x07 }, // 0x03, + { 0x22, 0xD2 }, + { 0x2C, 0x83 }, + { 0x30, 0x60 }, + { 0x31, 0xB5 }, // ? + { 0x21, 0x03 }, + } +}, { + { /* cvbs 3 */ + { 0x06, 0x00 }, + { 0x20, 0xBA }, + { 0x21, 0x07 }, // 0x05, + { 0x22, 0x91 }, + { 0x2C, 0x03 }, + { 0x30, 0x60 }, + { 0x31, 0xB5 }, // ?? + { 0x21, 0x05 }, // 0x03, + } +}, { + { /* cvbs 4 */ + { 0x06, 0x00 }, + { 0x20, 0xD8 }, + { 0x21, 0x17 }, // 0x16, + { 0x22, 0x40 }, + { 0x2C, 0x03 }, + { 0x30, 0x44 }, + { 0x31, 0x75 }, // ?? + { 0x21, 0x16 }, // 0x03, + } +}, { + { /* cvbs 5 */ + { 0x06, 0x00 }, + { 0x20, 0xB8 }, + { 0x21, 0x07 }, // 0x05, + { 0x22, 0x91 }, + { 0x2C, 0x03 }, + { 0x30, 0x60 }, + { 0x31, 0xB5 }, // ?? + { 0x21, 0x05 }, // 0x03, + } +}, { + { /* cvbs 6 */ + { 0x06, 0x00 }, + { 0x20, 0x7C }, + { 0x21, 0x07 }, // 0x03 + { 0x22, 0xD2 }, + { 0x2C, 0x83 }, + { 0x30, 0x60 }, + { 0x31, 0xB5 }, // ?? + { 0x21, 0x03 }, + } +}, { + { /* y/c 1 */ + { 0x06, 0x80 }, + { 0x20, 0x59 }, + { 0x21, 0x17 }, + { 0x22, 0x42 }, + { 0x2C, 0xA3 }, + { 0x30, 0x44 }, + { 0x31, 0x75 }, + { 0x21, 0x12 }, + } +}, { + { /* y/c 2 */ + { 0x06, 0x80 }, + { 0x20, 0x9A }, + { 0x21, 0x17 }, + { 0x22, 0xB1 }, + { 0x2C, 0x13 }, + { 0x30, 0x60 }, + { 0x31, 0xB5 }, + { 0x21, 0x14 }, + } +}, { + { /* y/c 3 */ + { 0x06, 0x80 }, + { 0x20, 0x3C }, + { 0x21, 0x27 }, + { 0x22, 0xC1 }, + { 0x2C, 0x23 }, + { 0x30, 0x44 }, + { 0x31, 0x75 }, + { 0x21, 0x21 }, + } +} +}; + +static struct saa7146_standard hexium_standards[] = { + { + .name = "PAL", .id = V4L2_STD_PAL, + .v_offset = 16, .v_field = 288, + .h_offset = 1, .h_pixels = 680, + .v_max_out = 576, .h_max_out = 768, + }, { + .name = "NTSC", .id = V4L2_STD_NTSC, + .v_offset = 16, .v_field = 240, + .h_offset = 1, .h_pixels = 640, + .v_max_out = 480, .h_max_out = 640, + }, { + .name = "SECAM", .id = V4L2_STD_SECAM, + .v_offset = 16, .v_field = 288, + .h_offset = 1, .h_pixels = 720, + .v_max_out = 576, .h_max_out = 768, + } +}; + +/* this is only called for old HV-PCI6/Orion cards + without eeprom */ +static int hexium_probe(struct saa7146_dev *dev) +{ + struct hexium *hexium = NULL; + union i2c_smbus_data data; + int err = 0; + + DEB_EE("\n"); + + /* there are no hexium orion cards with revision 0 saa7146s */ + if (0 == dev->revision) { + return -EFAULT; + } + + hexium = kzalloc(sizeof(*hexium), GFP_KERNEL); + if (!hexium) + return -ENOMEM; + + /* enable i2c-port pins */ + saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26)); + + saa7146_write(dev, DD1_INIT, 0x01000100); + saa7146_write(dev, DD1_STREAM_B, 0x00000000); + saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + + strscpy(hexium->i2c_adapter.name, "hexium orion", + sizeof(hexium->i2c_adapter.name)); + saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480); + if (i2c_add_adapter(&hexium->i2c_adapter) < 0) { + DEB_S("cannot register i2c-device. skipping.\n"); + kfree(hexium); + return -EFAULT; + } + + /* set SAA7110 control GPIO 0 */ + saa7146_setgpio(dev, 0, SAA7146_GPIO_OUTHI); + /* set HWControl GPIO number 2 */ + saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI); + + mdelay(10); + + /* detect newer Hexium Orion cards by subsystem ids */ + if (0x17c8 == dev->pci->subsystem_vendor && 0x0101 == dev->pci->subsystem_device) { + pr_info("device is a Hexium Orion w/ 1 SVHS + 3 BNC inputs\n"); + /* we store the pointer in our private data field */ + dev->ext_priv = hexium; + hexium->type = HEXIUM_ORION_1SVHS_3BNC; + return 0; + } + + if (0x17c8 == dev->pci->subsystem_vendor && 0x2101 == dev->pci->subsystem_device) { + pr_info("device is a Hexium Orion w/ 4 BNC inputs\n"); + /* we store the pointer in our private data field */ + dev->ext_priv = hexium; + hexium->type = HEXIUM_ORION_4BNC; + return 0; + } + + /* check if this is an old hexium Orion card by looking at + a saa7110 at address 0x4e */ + err = i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_READ, + 0x00, I2C_SMBUS_BYTE_DATA, &data); + if (err == 0) { + pr_info("device is a Hexium HV-PCI6/Orion (old)\n"); + /* we store the pointer in our private data field */ + dev->ext_priv = hexium; + hexium->type = HEXIUM_HV_PCI6_ORION; + return 0; + } + + i2c_del_adapter(&hexium->i2c_adapter); + kfree(hexium); + return -EFAULT; +} + +/* bring hardware to a sane state. this has to be done, just in case someone + wants to capture from this device before it has been properly initialized. + the capture engine would badly fail, because no valid signal arrives on the + saa7146, thus leading to timeouts and stuff. */ +static int hexium_init_done(struct saa7146_dev *dev) +{ + struct hexium *hexium = (struct hexium *) dev->ext_priv; + union i2c_smbus_data data; + int i = 0; + + DEB_D("hexium_init_done called\n"); + + /* initialize the helper ics to useful values */ + for (i = 0; i < sizeof(hexium_saa7110); i++) { + data.byte = hexium_saa7110[i]; + if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, i, I2C_SMBUS_BYTE_DATA, &data)) { + pr_err("failed for address 0x%02x\n", i); + } + } + + return 0; +} + +static int hexium_set_input(struct hexium *hexium, int input) +{ + union i2c_smbus_data data; + int i = 0; + + DEB_D("\n"); + + for (i = 0; i < 8; i++) { + int adr = hexium_input_select[input].data[i].adr; + data.byte = hexium_input_select[input].data[i].byte; + if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, adr, I2C_SMBUS_BYTE_DATA, &data)) { + return -1; + } + pr_debug("%d: 0x%02x => 0x%02x\n", input, adr, data.byte); + } + + return 0; +} + +static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) +{ + DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index); + + if (i->index >= HEXIUM_INPUTS) + return -EINVAL; + + memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input)); + + DEB_D("v4l2_ioctl: VIDIOC_ENUMINPUT %d\n", i->index); + return 0; +} + +static int vidioc_g_input(struct file *file, void *fh, unsigned int *input) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct hexium *hexium = (struct hexium *) dev->ext_priv; + + *input = hexium->cur_input; + + DEB_D("VIDIOC_G_INPUT: %d\n", *input); + return 0; +} + +static int vidioc_s_input(struct file *file, void *fh, unsigned int input) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct hexium *hexium = (struct hexium *) dev->ext_priv; + + if (input >= HEXIUM_INPUTS) + return -EINVAL; + + hexium->cur_input = input; + hexium_set_input(hexium, input); + + return 0; +} + +static struct saa7146_ext_vv vv_data; + +/* this function only gets called when the probing was successful */ +static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) +{ + struct hexium *hexium = (struct hexium *) dev->ext_priv; + int ret; + + DEB_EE("\n"); + + ret = saa7146_vv_init(dev, &vv_data); + if (ret) { + pr_err("Error in saa7146_vv_init()\n"); + return ret; + } + + vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; + vv_data.vid_ops.vidioc_g_input = vidioc_g_input; + vv_data.vid_ops.vidioc_s_input = vidioc_s_input; + if (0 != saa7146_register_device(&hexium->video_dev, dev, "hexium orion", VFL_TYPE_VIDEO)) { + pr_err("cannot register capture v4l2 device. skipping.\n"); + return -1; + } + + pr_err("found 'hexium orion' frame grabber-%d\n", hexium_num); + hexium_num++; + + /* the rest */ + hexium->cur_input = 0; + hexium_init_done(dev); + + return 0; +} + +static int hexium_detach(struct saa7146_dev *dev) +{ + struct hexium *hexium = (struct hexium *) dev->ext_priv; + + DEB_EE("dev:%p\n", dev); + + saa7146_unregister_device(&hexium->video_dev, dev); + saa7146_vv_release(dev); + + hexium_num--; + + i2c_del_adapter(&hexium->i2c_adapter); + kfree(hexium); + return 0; +} + +static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std) +{ + return 0; +} + +static struct saa7146_extension extension; + +static struct saa7146_pci_extension_data hexium_hv_pci6 = { + .ext_priv = "Hexium HV-PCI6 / Orion", + .ext = &extension, +}; + +static struct saa7146_pci_extension_data hexium_orion_1svhs_3bnc = { + .ext_priv = "Hexium HV-PCI6 / Orion (1 SVHS/3 BNC)", + .ext = &extension, +}; + +static struct saa7146_pci_extension_data hexium_orion_4bnc = { + .ext_priv = "Hexium HV-PCI6 / Orion (4 BNC)", + .ext = &extension, +}; + +static const struct pci_device_id pci_tbl[] = { + { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7146, + .subvendor = 0x0000, + .subdevice = 0x0000, + .driver_data = (unsigned long) &hexium_hv_pci6, + }, + { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7146, + .subvendor = 0x17c8, + .subdevice = 0x0101, + .driver_data = (unsigned long) &hexium_orion_1svhs_3bnc, + }, + { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7146, + .subvendor = 0x17c8, + .subdevice = 0x2101, + .driver_data = (unsigned long) &hexium_orion_4bnc, + }, + { + .vendor = 0, + } +}; + +MODULE_DEVICE_TABLE(pci, pci_tbl); + +static struct saa7146_ext_vv vv_data = { + .inputs = HEXIUM_INPUTS, + .capabilities = 0, + .stds = &hexium_standards[0], + .num_stds = ARRAY_SIZE(hexium_standards), + .std_callback = &std_callback, +}; + +static struct saa7146_extension extension = { + .name = "hexium HV-PCI6 Orion", + .flags = 0, // SAA7146_USE_I2C_IRQ, + + .pci_tbl = &pci_tbl[0], + .module = THIS_MODULE, + + .probe = hexium_probe, + .attach = hexium_attach, + .detach = hexium_detach, + + .irq_mask = 0, + .irq_func = NULL, +}; + +static int __init hexium_init_module(void) +{ + if (0 != saa7146_register_extension(&extension)) { + DEB_S("failed to register extension\n"); + return -ENODEV; + } + + return 0; +} + +static void __exit hexium_cleanup_module(void) +{ + saa7146_unregister_extension(&extension); +} + +module_init(hexium_init_module); +module_exit(hexium_cleanup_module); + +MODULE_DESCRIPTION("video4linux-2 driver for Hexium Orion frame grabber cards"); +MODULE_AUTHOR("Michael Hunold <michael@mihu.de>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/deprecated/saa7146/saa7146/mxb.c b/drivers/staging/media/deprecated/saa7146/saa7146/mxb.c new file mode 100644 index 000000000000..3e568f952dae --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/saa7146/mxb.c @@ -0,0 +1,873 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + mxb - v4l2 driver for the Multimedia eXtension Board + + Copyright (C) 1998-2006 Michael Hunold <michael@mihu.de> + + Visit http://www.themm.net/~mihu/linux/saa7146/mxb.html + for further details about this card. + +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define DEBUG_VARIABLE debug + +#include <media/tuner.h> +#include <media/v4l2-common.h> +#include <media/i2c/saa7115.h> +#include <linux/module.h> +#include <linux/kernel.h> + +#include "../common/saa7146_vv.h" +#include "tea6415c.h" +#include "tea6420.h" + +#define MXB_AUDIOS 6 + +#define I2C_SAA7111A 0x24 +#define I2C_TDA9840 0x42 +#define I2C_TEA6415C 0x43 +#define I2C_TEA6420_1 0x4c +#define I2C_TEA6420_2 0x4d +#define I2C_TUNER 0x60 + +#define MXB_BOARD_CAN_DO_VBI(dev) (dev->revision != 0) + +/* global variable */ +static int mxb_num; + +/* initial frequence the tuner will be tuned to. + in verden (lower saxony, germany) 4148 is a + channel called "phoenix" */ +static int freq = 4148; +module_param(freq, int, 0644); +MODULE_PARM_DESC(freq, "initial frequency the tuner will be tuned to while setup"); + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off)."); + +#define MXB_INPUTS 4 +enum { TUNER, AUX1, AUX3, AUX3_YC }; + +static struct v4l2_input mxb_inputs[MXB_INPUTS] = { + { TUNER, "Tuner", V4L2_INPUT_TYPE_TUNER, 0x3f, 0, + V4L2_STD_PAL_BG | V4L2_STD_PAL_I, 0, V4L2_IN_CAP_STD }, + { AUX1, "AUX1", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0, + V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { AUX3, "AUX3 Composite", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0, + V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { AUX3_YC, "AUX3 S-Video", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0, + V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, +}; + +/* this array holds the information, which port of the saa7146 each + input actually uses. the mxb uses port 0 for every input */ +static struct { + int hps_source; + int hps_sync; +} input_port_selection[MXB_INPUTS] = { + { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A }, + { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A }, + { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A }, + { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A }, +}; + +/* this array holds the information of the audio source (mxb_audios), + which has to be switched corresponding to the video source (mxb_channels) */ +static int video_audio_connect[MXB_INPUTS] = + { 0, 1, 3, 3 }; + +struct mxb_routing { + u32 input; + u32 output; +}; + +/* these are the available audio sources, which can switched + to the line- and cd-output individually */ +static struct v4l2_audio mxb_audios[MXB_AUDIOS] = { + { + .index = 0, + .name = "Tuner", + .capability = V4L2_AUDCAP_STEREO, + } , { + .index = 1, + .name = "AUX1", + .capability = V4L2_AUDCAP_STEREO, + } , { + .index = 2, + .name = "AUX2", + .capability = V4L2_AUDCAP_STEREO, + } , { + .index = 3, + .name = "AUX3", + .capability = V4L2_AUDCAP_STEREO, + } , { + .index = 4, + .name = "Radio (X9)", + .capability = V4L2_AUDCAP_STEREO, + } , { + .index = 5, + .name = "CD-ROM (X10)", + .capability = V4L2_AUDCAP_STEREO, + } +}; + +/* These are the necessary input-output-pins for bringing one audio source + (see above) to the CD-output. Note that gain is set to 0 in this table. */ +static struct mxb_routing TEA6420_cd[MXB_AUDIOS + 1][2] = { + { { 1, 1 }, { 1, 1 } }, /* Tuner */ + { { 5, 1 }, { 6, 1 } }, /* AUX 1 */ + { { 4, 1 }, { 6, 1 } }, /* AUX 2 */ + { { 3, 1 }, { 6, 1 } }, /* AUX 3 */ + { { 1, 1 }, { 3, 1 } }, /* Radio */ + { { 1, 1 }, { 2, 1 } }, /* CD-Rom */ + { { 6, 1 }, { 6, 1 } } /* Mute */ +}; + +/* These are the necessary input-output-pins for bringing one audio source + (see above) to the line-output. Note that gain is set to 0 in this table. */ +static struct mxb_routing TEA6420_line[MXB_AUDIOS + 1][2] = { + { { 2, 3 }, { 1, 2 } }, + { { 5, 3 }, { 6, 2 } }, + { { 4, 3 }, { 6, 2 } }, + { { 3, 3 }, { 6, 2 } }, + { { 2, 3 }, { 3, 2 } }, + { { 2, 3 }, { 2, 2 } }, + { { 6, 3 }, { 6, 2 } } /* Mute */ +}; + +struct mxb +{ + struct video_device video_dev; + struct video_device vbi_dev; + + struct i2c_adapter i2c_adapter; + + struct v4l2_subdev *saa7111a; + struct v4l2_subdev *tda9840; + struct v4l2_subdev *tea6415c; + struct v4l2_subdev *tuner; + struct v4l2_subdev *tea6420_1; + struct v4l2_subdev *tea6420_2; + + int cur_mode; /* current audio mode (mono, stereo, ...) */ + int cur_input; /* current input */ + int cur_audinput; /* current audio input */ + int cur_mute; /* current mute status */ + struct v4l2_frequency cur_freq; /* current frequency the tuner is tuned to */ +}; + +#define saa7111a_call(mxb, o, f, args...) \ + v4l2_subdev_call(mxb->saa7111a, o, f, ##args) +#define tda9840_call(mxb, o, f, args...) \ + v4l2_subdev_call(mxb->tda9840, o, f, ##args) +#define tea6415c_call(mxb, o, f, args...) \ + v4l2_subdev_call(mxb->tea6415c, o, f, ##args) +#define tuner_call(mxb, o, f, args...) \ + v4l2_subdev_call(mxb->tuner, o, f, ##args) +#define call_all(dev, o, f, args...) \ + v4l2_device_call_until_err(&dev->v4l2_dev, 0, o, f, ##args) + +static void mxb_update_audmode(struct mxb *mxb) +{ + struct v4l2_tuner t = { + .audmode = mxb->cur_mode, + }; + + tda9840_call(mxb, tuner, s_tuner, &t); +} + +static inline void tea6420_route(struct mxb *mxb, int idx) +{ + v4l2_subdev_call(mxb->tea6420_1, audio, s_routing, + TEA6420_cd[idx][0].input, TEA6420_cd[idx][0].output, 0); + v4l2_subdev_call(mxb->tea6420_2, audio, s_routing, + TEA6420_cd[idx][1].input, TEA6420_cd[idx][1].output, 0); + v4l2_subdev_call(mxb->tea6420_1, audio, s_routing, + TEA6420_line[idx][0].input, TEA6420_line[idx][0].output, 0); + v4l2_subdev_call(mxb->tea6420_2, audio, s_routing, + TEA6420_line[idx][1].input, TEA6420_line[idx][1].output, 0); +} + +static struct saa7146_extension extension; + +static int mxb_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct saa7146_dev *dev = container_of(ctrl->handler, + struct saa7146_dev, ctrl_handler); + struct mxb *mxb = dev->ext_priv; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + mxb->cur_mute = ctrl->val; + /* switch the audio-source */ + tea6420_route(mxb, ctrl->val ? 6 : + video_audio_connect[mxb->cur_input]); + break; + default: + return -EINVAL; + } + return 0; +} + +static const struct v4l2_ctrl_ops mxb_ctrl_ops = { + .s_ctrl = mxb_s_ctrl, +}; + +static int mxb_probe(struct saa7146_dev *dev) +{ + struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler; + struct mxb *mxb = NULL; + + v4l2_ctrl_new_std(hdl, &mxb_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); + if (hdl->error) + return hdl->error; + mxb = kzalloc(sizeof(struct mxb), GFP_KERNEL); + if (mxb == NULL) { + DEB_D("not enough kernel memory\n"); + return -ENOMEM; + } + + + snprintf(mxb->i2c_adapter.name, sizeof(mxb->i2c_adapter.name), "mxb%d", mxb_num); + + saa7146_i2c_adapter_prepare(dev, &mxb->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480); + if (i2c_add_adapter(&mxb->i2c_adapter) < 0) { + DEB_S("cannot register i2c-device. skipping.\n"); + kfree(mxb); + return -EFAULT; + } + + mxb->saa7111a = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, + "saa7111", I2C_SAA7111A, NULL); + mxb->tea6420_1 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, + "tea6420", I2C_TEA6420_1, NULL); + mxb->tea6420_2 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, + "tea6420", I2C_TEA6420_2, NULL); + mxb->tea6415c = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, + "tea6415c", I2C_TEA6415C, NULL); + mxb->tda9840 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, + "tda9840", I2C_TDA9840, NULL); + mxb->tuner = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, + "tuner", I2C_TUNER, NULL); + + /* check if all devices are present */ + if (!mxb->tea6420_1 || !mxb->tea6420_2 || !mxb->tea6415c || + !mxb->tda9840 || !mxb->saa7111a || !mxb->tuner) { + pr_err("did not find all i2c devices. aborting\n"); + i2c_del_adapter(&mxb->i2c_adapter); + kfree(mxb); + return -ENODEV; + } + + /* all devices are present, probe was successful */ + + /* we store the pointer in our private data field */ + dev->ext_priv = mxb; + + v4l2_ctrl_handler_setup(hdl); + + return 0; +} + +/* some init data for the saa7740, the so-called 'sound arena module'. + there are no specs available, so we simply use some init values */ +static struct { + int length; + char data[9]; +} mxb_saa7740_init[] = { + { 3, { 0x80, 0x00, 0x00 } },{ 3, { 0x80, 0x89, 0x00 } }, + { 3, { 0x80, 0xb0, 0x0a } },{ 3, { 0x00, 0x00, 0x00 } }, + { 3, { 0x49, 0x00, 0x00 } },{ 3, { 0x4a, 0x00, 0x00 } }, + { 3, { 0x4b, 0x00, 0x00 } },{ 3, { 0x4c, 0x00, 0x00 } }, + { 3, { 0x4d, 0x00, 0x00 } },{ 3, { 0x4e, 0x00, 0x00 } }, + { 3, { 0x4f, 0x00, 0x00 } },{ 3, { 0x50, 0x00, 0x00 } }, + { 3, { 0x51, 0x00, 0x00 } },{ 3, { 0x52, 0x00, 0x00 } }, + { 3, { 0x53, 0x00, 0x00 } },{ 3, { 0x54, 0x00, 0x00 } }, + { 3, { 0x55, 0x00, 0x00 } },{ 3, { 0x56, 0x00, 0x00 } }, + { 3, { 0x57, 0x00, 0x00 } },{ 3, { 0x58, 0x00, 0x00 } }, + { 3, { 0x59, 0x00, 0x00 } },{ 3, { 0x5a, 0x00, 0x00 } }, + { 3, { 0x5b, 0x00, 0x00 } },{ 3, { 0x5c, 0x00, 0x00 } }, + { 3, { 0x5d, 0x00, 0x00 } },{ 3, { 0x5e, 0x00, 0x00 } }, + { 3, { 0x5f, 0x00, 0x00 } },{ 3, { 0x60, 0x00, 0x00 } }, + { 3, { 0x61, 0x00, 0x00 } },{ 3, { 0x62, 0x00, 0x00 } }, + { 3, { 0x63, 0x00, 0x00 } },{ 3, { 0x64, 0x00, 0x00 } }, + { 3, { 0x65, 0x00, 0x00 } },{ 3, { 0x66, 0x00, 0x00 } }, + { 3, { 0x67, 0x00, 0x00 } },{ 3, { 0x68, 0x00, 0x00 } }, + { 3, { 0x69, 0x00, 0x00 } },{ 3, { 0x6a, 0x00, 0x00 } }, + { 3, { 0x6b, 0x00, 0x00 } },{ 3, { 0x6c, 0x00, 0x00 } }, + { 3, { 0x6d, 0x00, 0x00 } },{ 3, { 0x6e, 0x00, 0x00 } }, + { 3, { 0x6f, 0x00, 0x00 } },{ 3, { 0x70, 0x00, 0x00 } }, + { 3, { 0x71, 0x00, 0x00 } },{ 3, { 0x72, 0x00, 0x00 } }, + { 3, { 0x73, 0x00, 0x00 } },{ 3, { 0x74, 0x00, 0x00 } }, + { 3, { 0x75, 0x00, 0x00 } },{ 3, { 0x76, 0x00, 0x00 } }, + { 3, { 0x77, 0x00, 0x00 } },{ 3, { 0x41, 0x00, 0x42 } }, + { 3, { 0x42, 0x10, 0x42 } },{ 3, { 0x43, 0x20, 0x42 } }, + { 3, { 0x44, 0x30, 0x42 } },{ 3, { 0x45, 0x00, 0x01 } }, + { 3, { 0x46, 0x00, 0x01 } },{ 3, { 0x47, 0x00, 0x01 } }, + { 3, { 0x48, 0x00, 0x01 } }, + { 9, { 0x01, 0x03, 0xc5, 0x5c, 0x7a, 0x85, 0x01, 0x00, 0x54 } }, + { 9, { 0x21, 0x03, 0xc5, 0x5c, 0x7a, 0x85, 0x01, 0x00, 0x54 } }, + { 9, { 0x09, 0x0b, 0xb4, 0x6b, 0x74, 0x85, 0x95, 0x00, 0x34 } }, + { 9, { 0x29, 0x0b, 0xb4, 0x6b, 0x74, 0x85, 0x95, 0x00, 0x34 } }, + { 9, { 0x11, 0x17, 0x43, 0x62, 0x68, 0x89, 0xd1, 0xff, 0xb0 } }, + { 9, { 0x31, 0x17, 0x43, 0x62, 0x68, 0x89, 0xd1, 0xff, 0xb0 } }, + { 9, { 0x19, 0x20, 0x62, 0x51, 0x5a, 0x95, 0x19, 0x01, 0x50 } }, + { 9, { 0x39, 0x20, 0x62, 0x51, 0x5a, 0x95, 0x19, 0x01, 0x50 } }, + { 9, { 0x05, 0x3e, 0xd2, 0x69, 0x4e, 0x9a, 0x51, 0x00, 0xf0 } }, + { 9, { 0x25, 0x3e, 0xd2, 0x69, 0x4e, 0x9a, 0x51, 0x00, 0xf0 } }, + { 9, { 0x0d, 0x3d, 0xa1, 0x40, 0x7d, 0x9f, 0x29, 0xfe, 0x14 } }, + { 9, { 0x2d, 0x3d, 0xa1, 0x40, 0x7d, 0x9f, 0x29, 0xfe, 0x14 } }, + { 9, { 0x15, 0x73, 0xa1, 0x50, 0x5d, 0xa6, 0xf5, 0xfe, 0x38 } }, + { 9, { 0x35, 0x73, 0xa1, 0x50, 0x5d, 0xa6, 0xf5, 0xfe, 0x38 } }, + { 9, { 0x1d, 0xed, 0xd0, 0x68, 0x29, 0xb4, 0xe1, 0x00, 0xb8 } }, + { 9, { 0x3d, 0xed, 0xd0, 0x68, 0x29, 0xb4, 0xe1, 0x00, 0xb8 } }, + { 3, { 0x80, 0xb3, 0x0a } }, + {-1, { 0 } } +}; + +/* bring hardware to a sane state. this has to be done, just in case someone + wants to capture from this device before it has been properly initialized. + the capture engine would badly fail, because no valid signal arrives on the + saa7146, thus leading to timeouts and stuff. */ +static int mxb_init_done(struct saa7146_dev* dev) +{ + struct mxb* mxb = (struct mxb*)dev->ext_priv; + struct i2c_msg msg; + struct tuner_setup tun_setup; + v4l2_std_id std = V4L2_STD_PAL_BG; + + int i, err = 0; + + /* mute audio on tea6420s */ + tea6420_route(mxb, 6); + + /* select video mode in saa7111a */ + saa7111a_call(mxb, video, s_std, std); + + /* select tuner-output on saa7111a */ + saa7111a_call(mxb, video, s_routing, SAA7115_COMPOSITE0, + SAA7111_FMT_CCIR, 0); + + /* select a tuner type */ + tun_setup.mode_mask = T_ANALOG_TV; + tun_setup.addr = ADDR_UNSET; + tun_setup.type = TUNER_PHILIPS_PAL; + tuner_call(mxb, tuner, s_type_addr, &tun_setup); + /* tune in some frequency on tuner */ + mxb->cur_freq.tuner = 0; + mxb->cur_freq.type = V4L2_TUNER_ANALOG_TV; + mxb->cur_freq.frequency = freq; + tuner_call(mxb, tuner, s_frequency, &mxb->cur_freq); + + /* set a default video standard */ + /* These two gpio calls set the GPIO pins that control the tda9820 */ + saa7146_write(dev, GPIO_CTRL, 0x00404050); + saa7111a_call(mxb, core, s_gpio, 1); + saa7111a_call(mxb, video, s_std, std); + tuner_call(mxb, video, s_std, std); + + /* switch to tuner-channel on tea6415c */ + tea6415c_call(mxb, video, s_routing, 3, 17, 0); + + /* select tuner-output on multicable on tea6415c */ + tea6415c_call(mxb, video, s_routing, 3, 13, 0); + + /* the rest for mxb */ + mxb->cur_input = 0; + mxb->cur_audinput = video_audio_connect[mxb->cur_input]; + mxb->cur_mute = 1; + + mxb->cur_mode = V4L2_TUNER_MODE_STEREO; + mxb_update_audmode(mxb); + + /* check if the saa7740 (aka 'sound arena module') is present + on the mxb. if so, we must initialize it. due to lack of + information about the saa7740, the values were reverse + engineered. */ + msg.addr = 0x1b; + msg.flags = 0; + msg.len = mxb_saa7740_init[0].length; + msg.buf = &mxb_saa7740_init[0].data[0]; + + err = i2c_transfer(&mxb->i2c_adapter, &msg, 1); + if (err == 1) { + /* the sound arena module is a pos, that's probably the reason + philips refuses to hand out a datasheet for the saa7740... + it seems to screw up the i2c bus, so we disable fast irq + based i2c transactions here and rely on the slow and safe + polling method ... */ + extension.flags &= ~SAA7146_USE_I2C_IRQ; + for (i = 1; ; i++) { + if (-1 == mxb_saa7740_init[i].length) + break; + + msg.len = mxb_saa7740_init[i].length; + msg.buf = &mxb_saa7740_init[i].data[0]; + err = i2c_transfer(&mxb->i2c_adapter, &msg, 1); + if (err != 1) { + DEB_D("failed to initialize 'sound arena module'\n"); + goto err; + } + } + pr_info("'sound arena module' detected\n"); + } +err: + /* the rest for saa7146: you should definitely set some basic values + for the input-port handling of the saa7146. */ + + /* ext->saa has been filled by the core driver */ + + /* some stuff is done via variables */ + saa7146_set_hps_source_and_sync(dev, input_port_selection[mxb->cur_input].hps_source, + input_port_selection[mxb->cur_input].hps_sync); + + /* some stuff is done via direct write to the registers */ + + /* this is ugly, but because of the fact that this is completely + hardware dependend, it should be done directly... */ + saa7146_write(dev, DD1_STREAM_B, 0x00000000); + saa7146_write(dev, DD1_INIT, 0x02000200); + saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + + return 0; +} + +/* interrupt-handler. this gets called when irq_mask is != 0. + it must clear the interrupt-bits in irq_mask it has handled */ +/* +void mxb_irq_bh(struct saa7146_dev* dev, u32* irq_mask) +{ + struct mxb* mxb = (struct mxb*)dev->ext_priv; +} +*/ + +static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) +{ + DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index); + if (i->index >= MXB_INPUTS) + return -EINVAL; + memcpy(i, &mxb_inputs[i->index], sizeof(struct v4l2_input)); + return 0; +} + +static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct mxb *mxb = (struct mxb *)dev->ext_priv; + *i = mxb->cur_input; + + DEB_EE("VIDIOC_G_INPUT %d\n", *i); + return 0; +} + +static int vidioc_s_input(struct file *file, void *fh, unsigned int input) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct mxb *mxb = (struct mxb *)dev->ext_priv; + int err = 0; + int i = 0; + + DEB_EE("VIDIOC_S_INPUT %d\n", input); + + if (input >= MXB_INPUTS) + return -EINVAL; + + mxb->cur_input = input; + + saa7146_set_hps_source_and_sync(dev, input_port_selection[input].hps_source, + input_port_selection[input].hps_sync); + + /* prepare switching of tea6415c and saa7111a; + have a look at the 'background'-file for further information */ + switch (input) { + case TUNER: + i = SAA7115_COMPOSITE0; + + err = tea6415c_call(mxb, video, s_routing, 3, 17, 0); + + /* connect tuner-output always to multicable */ + if (!err) + err = tea6415c_call(mxb, video, s_routing, 3, 13, 0); + break; + case AUX3_YC: + /* nothing to be done here. aux3_yc is + directly connected to the saa711a */ + i = SAA7115_SVIDEO1; + break; + case AUX3: + /* nothing to be done here. aux3 is + directly connected to the saa711a */ + i = SAA7115_COMPOSITE1; + break; + case AUX1: + i = SAA7115_COMPOSITE0; + err = tea6415c_call(mxb, video, s_routing, 1, 17, 0); + break; + } + + if (err) + return err; + + /* switch video in saa7111a */ + if (saa7111a_call(mxb, video, s_routing, i, SAA7111_FMT_CCIR, 0)) + pr_err("VIDIOC_S_INPUT: could not address saa7111a\n"); + + mxb->cur_audinput = video_audio_connect[input]; + /* switch the audio-source only if necessary */ + if (0 == mxb->cur_mute) + tea6420_route(mxb, mxb->cur_audinput); + if (mxb->cur_audinput == 0) + mxb_update_audmode(mxb); + + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *t) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct mxb *mxb = (struct mxb *)dev->ext_priv; + + if (t->index) { + DEB_D("VIDIOC_G_TUNER: channel %d does not have a tuner attached\n", + t->index); + return -EINVAL; + } + + DEB_EE("VIDIOC_G_TUNER: %d\n", t->index); + + memset(t, 0, sizeof(*t)); + strscpy(t->name, "TV Tuner", sizeof(t->name)); + t->type = V4L2_TUNER_ANALOG_TV; + t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; + t->audmode = mxb->cur_mode; + return call_all(dev, tuner, g_tuner, t); +} + +static int vidioc_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *t) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct mxb *mxb = (struct mxb *)dev->ext_priv; + + if (t->index) { + DEB_D("VIDIOC_S_TUNER: channel %d does not have a tuner attached\n", + t->index); + return -EINVAL; + } + + mxb->cur_mode = t->audmode; + return call_all(dev, tuner, s_tuner, t); +} + +static int vidioc_querystd(struct file *file, void *fh, v4l2_std_id *norm) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + + return call_all(dev, video, querystd, norm); +} + +static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *f) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct mxb *mxb = (struct mxb *)dev->ext_priv; + + if (f->tuner) + return -EINVAL; + *f = mxb->cur_freq; + + DEB_EE("VIDIOC_G_FREQ: freq:0x%08x\n", mxb->cur_freq.frequency); + return 0; +} + +static int vidioc_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *f) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct mxb *mxb = (struct mxb *)dev->ext_priv; + struct saa7146_vv *vv = dev->vv_data; + + if (f->tuner) + return -EINVAL; + + if (V4L2_TUNER_ANALOG_TV != f->type) + return -EINVAL; + + DEB_EE("VIDIOC_S_FREQUENCY: freq:0x%08x\n", mxb->cur_freq.frequency); + + /* tune in desired frequency */ + tuner_call(mxb, tuner, s_frequency, f); + /* let the tuner subdev clamp the frequency to the tuner range */ + mxb->cur_freq = *f; + tuner_call(mxb, tuner, g_frequency, &mxb->cur_freq); + if (mxb->cur_audinput == 0) + mxb_update_audmode(mxb); + + if (mxb->cur_input) + return 0; + + /* hack: changing the frequency should invalidate the vbi-counter (=> alevt) */ + spin_lock(&dev->slock); + vv->vbi_fieldcount = 0; + spin_unlock(&dev->slock); + + return 0; +} + +static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a) +{ + if (a->index >= MXB_AUDIOS) + return -EINVAL; + *a = mxb_audios[a->index]; + return 0; +} + +static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct mxb *mxb = (struct mxb *)dev->ext_priv; + + DEB_EE("VIDIOC_G_AUDIO\n"); + *a = mxb_audios[mxb->cur_audinput]; + return 0; +} + +static int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *a) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct mxb *mxb = (struct mxb *)dev->ext_priv; + + DEB_D("VIDIOC_S_AUDIO %d\n", a->index); + if (a->index >= 32 || + !(mxb_inputs[mxb->cur_input].audioset & (1 << a->index))) + return -EINVAL; + + if (mxb->cur_audinput != a->index) { + mxb->cur_audinput = a->index; + tea6420_route(mxb, a->index); + if (mxb->cur_audinput == 0) + mxb_update_audmode(mxb); + } + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int vidioc_g_register(struct file *file, void *fh, struct v4l2_dbg_register *reg) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + + if (reg->reg > pci_resource_len(dev->pci, 0) - 4) + return -EINVAL; + reg->val = saa7146_read(dev, reg->reg); + reg->size = 4; + return 0; +} + +static int vidioc_s_register(struct file *file, void *fh, const struct v4l2_dbg_register *reg) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + + if (reg->reg > pci_resource_len(dev->pci, 0) - 4) + return -EINVAL; + saa7146_write(dev, reg->reg, reg->val); + return 0; +} +#endif + +static struct saa7146_ext_vv vv_data; + +/* this function only gets called when the probing was successful */ +static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) +{ + struct mxb *mxb; + int ret; + + DEB_EE("dev:%p\n", dev); + + ret = saa7146_vv_init(dev, &vv_data); + if (ret) { + ERR("Error in saa7146_vv_init()"); + return ret; + } + + if (mxb_probe(dev)) { + saa7146_vv_release(dev); + return -1; + } + mxb = (struct mxb *)dev->ext_priv; + + vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; + vv_data.vid_ops.vidioc_g_input = vidioc_g_input; + vv_data.vid_ops.vidioc_s_input = vidioc_s_input; + vv_data.vid_ops.vidioc_querystd = vidioc_querystd; + vv_data.vid_ops.vidioc_g_tuner = vidioc_g_tuner; + vv_data.vid_ops.vidioc_s_tuner = vidioc_s_tuner; + vv_data.vid_ops.vidioc_g_frequency = vidioc_g_frequency; + vv_data.vid_ops.vidioc_s_frequency = vidioc_s_frequency; + vv_data.vid_ops.vidioc_enumaudio = vidioc_enumaudio; + vv_data.vid_ops.vidioc_g_audio = vidioc_g_audio; + vv_data.vid_ops.vidioc_s_audio = vidioc_s_audio; +#ifdef CONFIG_VIDEO_ADV_DEBUG + vv_data.vid_ops.vidioc_g_register = vidioc_g_register; + vv_data.vid_ops.vidioc_s_register = vidioc_s_register; +#endif + if (saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_VIDEO)) { + ERR("cannot register capture v4l2 device. skipping.\n"); + saa7146_vv_release(dev); + return -1; + } + + /* initialization stuff (vbi) (only for revision > 0 and for extensions which want it)*/ + if (MXB_BOARD_CAN_DO_VBI(dev)) { + if (saa7146_register_device(&mxb->vbi_dev, dev, "mxb", VFL_TYPE_VBI)) { + ERR("cannot register vbi v4l2 device. skipping.\n"); + } + } + + pr_info("found Multimedia eXtension Board #%d\n", mxb_num); + + mxb_num++; + mxb_init_done(dev); + return 0; +} + +static int mxb_detach(struct saa7146_dev *dev) +{ + struct mxb *mxb = (struct mxb *)dev->ext_priv; + + DEB_EE("dev:%p\n", dev); + + /* mute audio on tea6420s */ + tea6420_route(mxb, 6); + + saa7146_unregister_device(&mxb->video_dev,dev); + if (MXB_BOARD_CAN_DO_VBI(dev)) + saa7146_unregister_device(&mxb->vbi_dev, dev); + saa7146_vv_release(dev); + + mxb_num--; + + i2c_del_adapter(&mxb->i2c_adapter); + kfree(mxb); + + return 0; +} + +static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standard) +{ + struct mxb *mxb = (struct mxb *)dev->ext_priv; + + if (V4L2_STD_PAL_I == standard->id) { + v4l2_std_id std = V4L2_STD_PAL_I; + + DEB_D("VIDIOC_S_STD: setting mxb for PAL_I\n"); + /* These two gpio calls set the GPIO pins that control the tda9820 */ + saa7146_write(dev, GPIO_CTRL, 0x00404050); + saa7111a_call(mxb, core, s_gpio, 0); + saa7111a_call(mxb, video, s_std, std); + if (mxb->cur_input == 0) + tuner_call(mxb, video, s_std, std); + } else { + v4l2_std_id std = V4L2_STD_PAL_BG; + + if (mxb->cur_input) + std = standard->id; + DEB_D("VIDIOC_S_STD: setting mxb for PAL/NTSC/SECAM\n"); + /* These two gpio calls set the GPIO pins that control the tda9820 */ + saa7146_write(dev, GPIO_CTRL, 0x00404050); + saa7111a_call(mxb, core, s_gpio, 1); + saa7111a_call(mxb, video, s_std, std); + if (mxb->cur_input == 0) + tuner_call(mxb, video, s_std, std); + } + return 0; +} + +static struct saa7146_standard standard[] = { + { + .name = "PAL-BG", .id = V4L2_STD_PAL_BG, + .v_offset = 0x17, .v_field = 288, + .h_offset = 0x14, .h_pixels = 680, + .v_max_out = 576, .h_max_out = 768, + }, { + .name = "PAL-I", .id = V4L2_STD_PAL_I, + .v_offset = 0x17, .v_field = 288, + .h_offset = 0x14, .h_pixels = 680, + .v_max_out = 576, .h_max_out = 768, + }, { + .name = "NTSC", .id = V4L2_STD_NTSC, + .v_offset = 0x16, .v_field = 240, + .h_offset = 0x06, .h_pixels = 708, + .v_max_out = 480, .h_max_out = 640, + }, { + .name = "SECAM", .id = V4L2_STD_SECAM, + .v_offset = 0x14, .v_field = 288, + .h_offset = 0x14, .h_pixels = 720, + .v_max_out = 576, .h_max_out = 768, + } +}; + +static struct saa7146_pci_extension_data mxb = { + .ext_priv = "Multimedia eXtension Board", + .ext = &extension, +}; + +static const struct pci_device_id pci_tbl[] = { + { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7146, + .subvendor = 0x0000, + .subdevice = 0x0000, + .driver_data = (unsigned long)&mxb, + }, { + .vendor = 0, + } +}; + +MODULE_DEVICE_TABLE(pci, pci_tbl); + +static struct saa7146_ext_vv vv_data = { + .inputs = MXB_INPUTS, + .capabilities = V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_AUDIO, + .stds = &standard[0], + .num_stds = ARRAY_SIZE(standard), + .std_callback = &std_callback, +}; + +static struct saa7146_extension extension = { + .name = "Multimedia eXtension Board", + .flags = SAA7146_USE_I2C_IRQ, + + .pci_tbl = &pci_tbl[0], + .module = THIS_MODULE, + + .attach = mxb_attach, + .detach = mxb_detach, + + .irq_mask = 0, + .irq_func = NULL, +}; + +static int __init mxb_init_module(void) +{ + if (saa7146_register_extension(&extension)) { + DEB_S("failed to register extension\n"); + return -ENODEV; + } + + return 0; +} + +static void __exit mxb_cleanup_module(void) +{ + saa7146_unregister_extension(&extension); +} + +module_init(mxb_init_module); +module_exit(mxb_cleanup_module); + +MODULE_DESCRIPTION("video4linux-2 driver for the Siemens-Nixdorf 'Multimedia eXtension board'"); +MODULE_AUTHOR("Michael Hunold <michael@mihu.de>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/deprecated/saa7146/ttpci/Kconfig b/drivers/staging/media/deprecated/saa7146/ttpci/Kconfig new file mode 100644 index 000000000000..8c85ed58e938 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/ttpci/Kconfig @@ -0,0 +1,95 @@ +# SPDX-License-Identifier: GPL-2.0-only +config DVB_BUDGET_CORE + tristate "SAA7146 DVB cards (aka Budget, Nova-PCI) (DEPRECATED)" + depends on DVB_CORE && PCI && I2C + select VIDEO_SAA7146 + select TTPCI_EEPROM + help + Support for simple SAA7146 based DVB cards + (so called Budget- or Nova-PCI cards) without onboard + MPEG2 decoder. + +config DVB_BUDGET + tristate "Budget cards (DEPRECATED)" + depends on DVB_BUDGET_CORE && I2C + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_VES1X93 if MEDIA_SUBDRV_AUTOSELECT + select DVB_VES1820 if MEDIA_SUBDRV_AUTOSELECT + select DVB_L64781 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA8083 if MEDIA_SUBDRV_AUTOSELECT + select DVB_S5H1420 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10086 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA826X if MEDIA_SUBDRV_AUTOSELECT + select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT + select DVB_ISL6423 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV6110x if MEDIA_SUBDRV_AUTOSELECT + help + Support for simple SAA7146 based DVB cards (so called Budget- + or Nova-PCI cards) without onboard MPEG2 decoder, and without + analog inputs or an onboard Common Interface connector. + + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + + Say Y if you own such a card and want to use it. + + To compile this driver as a module, choose M here: the + module will be called budget. + +config DVB_BUDGET_CI + tristate "Budget cards with onboard CI connector (DEPRECATED)" + depends on DVB_BUDGET_CORE && I2C + select DVB_STV0297 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB6000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA827X if MEDIA_SUBDRV_AUTOSELECT + depends on RC_CORE + help + Support for simple SAA7146 based DVB cards + (so called Budget- or Nova-PCI cards) without onboard + MPEG2 decoder, but with onboard Common Interface connector. + + Note: The Common Interface is not yet supported by this driver + due to lack of information from the vendor. + + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + + Say Y if you own such a card and want to use it. + + To compile this driver as a module, choose M here: the + module will be called budget-ci. + +config DVB_BUDGET_AV + tristate "Budget cards with analog video inputs (DEPRECATED)" + depends on DVB_BUDGET_CORE && I2C + select VIDEO_SAA7146_VV + depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10021 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT + select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA8261 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TUA6100 if MEDIA_SUBDRV_AUTOSELECT + help + Support for simple SAA7146 based DVB cards + (so called Budget- or Nova-PCI cards) without onboard + MPEG2 decoder, but with one or more analog video inputs. + + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + + Say Y if you own such a card and want to use it. + + To compile this driver as a module, choose M here: the + module will be called budget-av. diff --git a/drivers/staging/media/deprecated/saa7146/ttpci/Makefile b/drivers/staging/media/deprecated/saa7146/ttpci/Makefile new file mode 100644 index 000000000000..b0708f6e40cc --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/ttpci/Makefile @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the kernel SAA7146 FULL TS DVB device driver +# + +obj-$(CONFIG_DVB_BUDGET_CORE) += budget-core.o +obj-$(CONFIG_DVB_BUDGET) += budget.o +obj-$(CONFIG_DVB_BUDGET_AV) += budget-av.o +obj-$(CONFIG_DVB_BUDGET_CI) += budget-ci.o + +ccflags-y += -I $(srctree)/drivers/media/dvb-frontends/ +ccflags-y += -I $(srctree)/drivers/media/tuners +ccflags-y += -I $(srctree)/drivers/media/common diff --git a/drivers/staging/media/deprecated/saa7146/ttpci/TODO b/drivers/staging/media/deprecated/saa7146/ttpci/TODO new file mode 100644 index 000000000000..c9ae2ec79cea --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/ttpci/TODO @@ -0,0 +1,7 @@ +The saa7146-based drivers are one of the few drivers still not using +the vb2 framework, so these drivers are now deprecated with the intent of +removing them altogether by the beginning of 2023. + +In order to keep these drivers they have to be converted to vb2. +If someone is interested in doing this work, then contact the +linux-media mailinglist (https://linuxtv.org/lists.php). diff --git a/drivers/staging/media/deprecated/saa7146/ttpci/budget-av.c b/drivers/staging/media/deprecated/saa7146/ttpci/budget-av.c new file mode 100644 index 000000000000..0c61a2dec221 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/ttpci/budget-av.c @@ -0,0 +1,1622 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * budget-av.c: driver for the SAA7146 based Budget DVB cards + * with analog video in + * + * Compiled from various sources by Michael Hunold <michael@mihu.de> + * + * CI interface support (c) 2004 Olivier Gournet <ogournet@anevia.com> & + * Andrew de Quincey <adq_dvb@lidskialf.net> + * + * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de> + * + * Copyright (C) 1999-2002 Ralph Metzler + * & Marcus Metzler for convergence integrated media GmbH + * + * the project's page is at https://linuxtv.org + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "budget.h" +#include "stv0299.h" +#include "stb0899_drv.h" +#include "stb0899_reg.h" +#include "stb0899_cfg.h" +#include "tda8261.h" +#include "tda8261_cfg.h" +#include "tda1002x.h" +#include "tda1004x.h" +#include "tua6100.h" +#include "dvb-pll.h" +#include "../common/saa7146_vv.h" +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/spinlock.h> + +#include <media/dvb_ca_en50221.h> + +#define DEBICICAM 0x02420000 + +#define SLOTSTATUS_NONE 1 +#define SLOTSTATUS_PRESENT 2 +#define SLOTSTATUS_RESET 4 +#define SLOTSTATUS_READY 8 +#define SLOTSTATUS_OCCUPIED (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY) + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +struct budget_av { + struct budget budget; + struct video_device vd; + int cur_input; + int has_saa7113; + struct tasklet_struct ciintf_irq_tasklet; + int slot_status; + struct dvb_ca_en50221 ca; + u8 reinitialise_demod:1; +}; + +static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot); + + +/* GPIO Connections: + * 0 - Vcc/Reset (Reset is controlled by capacitor). Resets the frontend *AS WELL*! + * 1 - CI memory select 0=>IO memory, 1=>Attribute Memory + * 2 - CI Card Enable (Active Low) + * 3 - CI Card Detect + */ + +/**************************************************************************** + * INITIALIZATION + ****************************************************************************/ + +static u8 i2c_readreg(struct i2c_adapter *i2c, u8 id, u8 reg) +{ + u8 mm1[] = { 0x00 }; + u8 mm2[] = { 0x00 }; + struct i2c_msg msgs[2]; + + msgs[0].flags = 0; + msgs[1].flags = I2C_M_RD; + msgs[0].addr = msgs[1].addr = id / 2; + mm1[0] = reg; + msgs[0].len = 1; + msgs[1].len = 1; + msgs[0].buf = mm1; + msgs[1].buf = mm2; + + i2c_transfer(i2c, msgs, 2); + + return mm2[0]; +} + +static int i2c_readregs(struct i2c_adapter *i2c, u8 id, u8 reg, u8 * buf, u8 len) +{ + u8 mm1[] = { reg }; + struct i2c_msg msgs[2] = { + {.addr = id / 2,.flags = 0,.buf = mm1,.len = 1}, + {.addr = id / 2,.flags = I2C_M_RD,.buf = buf,.len = len} + }; + + if (i2c_transfer(i2c, msgs, 2) != 2) + return -EIO; + + return 0; +} + +static int i2c_writereg(struct i2c_adapter *i2c, u8 id, u8 reg, u8 val) +{ + u8 msg[2] = { reg, val }; + struct i2c_msg msgs; + + msgs.flags = 0; + msgs.addr = id / 2; + msgs.len = 2; + msgs.buf = msg; + return i2c_transfer(i2c, &msgs, 1); +} + +static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address) +{ + struct budget_av *budget_av = (struct budget_av *) ca->data; + int result; + + if (slot != 0) + return -EINVAL; + + saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTHI); + udelay(1); + + result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, address & 0xfff, 1, 0, 1); + if (result == -ETIMEDOUT) { + ciintf_slot_shutdown(ca, slot); + pr_info("cam ejected 1\n"); + } + return result; +} + +static int ciintf_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value) +{ + struct budget_av *budget_av = (struct budget_av *) ca->data; + int result; + + if (slot != 0) + return -EINVAL; + + saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTHI); + udelay(1); + + result = ttpci_budget_debiwrite(&budget_av->budget, DEBICICAM, address & 0xfff, 1, value, 0, 1); + if (result == -ETIMEDOUT) { + ciintf_slot_shutdown(ca, slot); + pr_info("cam ejected 2\n"); + } + return result; +} + +static int ciintf_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address) +{ + struct budget_av *budget_av = (struct budget_av *) ca->data; + int result; + + if (slot != 0) + return -EINVAL; + + saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO); + udelay(1); + + result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, address & 3, 1, 0, 0); + if (result == -ETIMEDOUT) { + ciintf_slot_shutdown(ca, slot); + pr_info("cam ejected 3\n"); + return -ETIMEDOUT; + } + return result; +} + +static int ciintf_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value) +{ + struct budget_av *budget_av = (struct budget_av *) ca->data; + int result; + + if (slot != 0) + return -EINVAL; + + saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO); + udelay(1); + + result = ttpci_budget_debiwrite(&budget_av->budget, DEBICICAM, address & 3, 1, value, 0, 0); + if (result == -ETIMEDOUT) { + ciintf_slot_shutdown(ca, slot); + pr_info("cam ejected 5\n"); + } + return result; +} + +static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot) +{ + struct budget_av *budget_av = (struct budget_av *) ca->data; + struct saa7146_dev *saa = budget_av->budget.dev; + + if (slot != 0) + return -EINVAL; + + dprintk(1, "ciintf_slot_reset\n"); + budget_av->slot_status = SLOTSTATUS_RESET; + + saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTHI); /* disable card */ + + saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTHI); /* Vcc off */ + msleep(2); + saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO); /* Vcc on */ + msleep(20); /* 20 ms Vcc settling time */ + + saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTLO); /* enable card */ + ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); + msleep(20); + + /* reinitialise the frontend if necessary */ + if (budget_av->reinitialise_demod) + dvb_frontend_reinitialise(budget_av->budget.dvb_frontend); + + return 0; +} + +static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) +{ + struct budget_av *budget_av = (struct budget_av *) ca->data; + struct saa7146_dev *saa = budget_av->budget.dev; + + if (slot != 0) + return -EINVAL; + + dprintk(1, "ciintf_slot_shutdown\n"); + + ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); + budget_av->slot_status = SLOTSTATUS_NONE; + + return 0; +} + +static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) +{ + struct budget_av *budget_av = (struct budget_av *) ca->data; + struct saa7146_dev *saa = budget_av->budget.dev; + + if (slot != 0) + return -EINVAL; + + dprintk(1, "ciintf_slot_ts_enable: %d\n", budget_av->slot_status); + + ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTA); + + return 0; +} + +static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) +{ + struct budget_av *budget_av = (struct budget_av *) ca->data; + struct saa7146_dev *saa = budget_av->budget.dev; + int result; + + if (slot != 0) + return -EINVAL; + + /* test the card detect line - needs to be done carefully + * since it never goes high for some CAMs on this interface (e.g. topuptv) */ + if (budget_av->slot_status == SLOTSTATUS_NONE) { + saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); + udelay(1); + if (saa7146_read(saa, PSR) & MASK_06) { + if (budget_av->slot_status == SLOTSTATUS_NONE) { + budget_av->slot_status = SLOTSTATUS_PRESENT; + pr_info("cam inserted A\n"); + } + } + saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO); + } + + /* We also try and read from IO memory to work round the above detection bug. If + * there is no CAM, we will get a timeout. Only done if there is no cam + * present, since this test actually breaks some cams :( + * + * if the CI interface is not open, we also do the above test since we + * don't care if the cam has problems - we'll be resetting it on open() anyway */ + if ((budget_av->slot_status == SLOTSTATUS_NONE) || (!open)) { + saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO); + result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, 0, 1, 0, 1); + if ((result >= 0) && (budget_av->slot_status == SLOTSTATUS_NONE)) { + budget_av->slot_status = SLOTSTATUS_PRESENT; + pr_info("cam inserted B\n"); + } else if (result < 0) { + if (budget_av->slot_status != SLOTSTATUS_NONE) { + ciintf_slot_shutdown(ca, slot); + pr_info("cam ejected 5\n"); + return 0; + } + } + } + + /* read from attribute memory in reset/ready state to know when the CAM is ready */ + if (budget_av->slot_status == SLOTSTATUS_RESET) { + result = ciintf_read_attribute_mem(ca, slot, 0); + if (result == 0x1d) { + budget_av->slot_status = SLOTSTATUS_READY; + } + } + + /* work out correct return code */ + if (budget_av->slot_status != SLOTSTATUS_NONE) { + if (budget_av->slot_status & SLOTSTATUS_READY) { + return DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY; + } + return DVB_CA_EN50221_POLL_CAM_PRESENT; + } + return 0; +} + +static int ciintf_init(struct budget_av *budget_av) +{ + struct saa7146_dev *saa = budget_av->budget.dev; + int result; + + memset(&budget_av->ca, 0, sizeof(struct dvb_ca_en50221)); + + saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO); + saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTLO); + saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTLO); + saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO); + + /* Enable DEBI pins */ + saa7146_write(saa, MC1, MASK_27 | MASK_11); + + /* register CI interface */ + budget_av->ca.owner = THIS_MODULE; + budget_av->ca.read_attribute_mem = ciintf_read_attribute_mem; + budget_av->ca.write_attribute_mem = ciintf_write_attribute_mem; + budget_av->ca.read_cam_control = ciintf_read_cam_control; + budget_av->ca.write_cam_control = ciintf_write_cam_control; + budget_av->ca.slot_reset = ciintf_slot_reset; + budget_av->ca.slot_shutdown = ciintf_slot_shutdown; + budget_av->ca.slot_ts_enable = ciintf_slot_ts_enable; + budget_av->ca.poll_slot_status = ciintf_poll_slot_status; + budget_av->ca.data = budget_av; + budget_av->budget.ci_present = 1; + budget_av->slot_status = SLOTSTATUS_NONE; + + if ((result = dvb_ca_en50221_init(&budget_av->budget.dvb_adapter, + &budget_av->ca, 0, 1)) != 0) { + pr_err("ci initialisation failed\n"); + goto error; + } + + pr_info("ci interface initialised\n"); + return 0; + +error: + saa7146_write(saa, MC1, MASK_27); + return result; +} + +static void ciintf_deinit(struct budget_av *budget_av) +{ + struct saa7146_dev *saa = budget_av->budget.dev; + + saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT); + saa7146_setgpio(saa, 1, SAA7146_GPIO_INPUT); + saa7146_setgpio(saa, 2, SAA7146_GPIO_INPUT); + saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); + + /* release the CA device */ + dvb_ca_en50221_release(&budget_av->ca); + + /* disable DEBI pins */ + saa7146_write(saa, MC1, MASK_27); +} + + +static const u8 saa7113_tab[] = { + 0x01, 0x08, + 0x02, 0xc0, + 0x03, 0x33, + 0x04, 0x00, + 0x05, 0x00, + 0x06, 0xeb, + 0x07, 0xe0, + 0x08, 0x28, + 0x09, 0x00, + 0x0a, 0x80, + 0x0b, 0x47, + 0x0c, 0x40, + 0x0d, 0x00, + 0x0e, 0x01, + 0x0f, 0x44, + + 0x10, 0x08, + 0x11, 0x0c, + 0x12, 0x7b, + 0x13, 0x00, + 0x15, 0x00, 0x16, 0x00, 0x17, 0x00, + + 0x57, 0xff, + 0x40, 0x82, 0x58, 0x00, 0x59, 0x54, 0x5a, 0x07, + 0x5b, 0x83, 0x5e, 0x00, + 0xff +}; + +static int saa7113_init(struct budget_av *budget_av) +{ + struct budget *budget = &budget_av->budget; + struct saa7146_dev *saa = budget->dev; + const u8 *data = saa7113_tab; + + saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTHI); + msleep(200); + + if (i2c_writereg(&budget->i2c_adap, 0x4a, 0x01, 0x08) != 1) { + dprintk(1, "saa7113 not found on KNC card\n"); + return -ENODEV; + } + + dprintk(1, "saa7113 detected and initializing\n"); + + while (*data != 0xff) { + i2c_writereg(&budget->i2c_adap, 0x4a, *data, *(data + 1)); + data += 2; + } + + dprintk(1, "saa7113 status=%02x\n", i2c_readreg(&budget->i2c_adap, 0x4a, 0x1f)); + + return 0; +} + +static int saa7113_setinput(struct budget_av *budget_av, int input) +{ + struct budget *budget = &budget_av->budget; + + if (1 != budget_av->has_saa7113) + return -ENODEV; + + if (input == 1) { + i2c_writereg(&budget->i2c_adap, 0x4a, 0x02, 0xc7); + i2c_writereg(&budget->i2c_adap, 0x4a, 0x09, 0x80); + } else if (input == 0) { + i2c_writereg(&budget->i2c_adap, 0x4a, 0x02, 0xc0); + i2c_writereg(&budget->i2c_adap, 0x4a, 0x09, 0x00); + } else + return -EINVAL; + + budget_av->cur_input = input; + return 0; +} + + +static int philips_su1278_ty_ci_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio) +{ + u8 aclk = 0; + u8 bclk = 0; + u8 m1; + + aclk = 0xb5; + if (srate < 2000000) + bclk = 0x86; + else if (srate < 5000000) + bclk = 0x89; + else if (srate < 15000000) + bclk = 0x8f; + else if (srate < 45000000) + bclk = 0x95; + + m1 = 0x14; + if (srate < 4000000) + m1 = 0x10; + + stv0299_writereg(fe, 0x13, aclk); + stv0299_writereg(fe, 0x14, bclk); + stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); + stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); + stv0299_writereg(fe, 0x21, (ratio) & 0xf0); + stv0299_writereg(fe, 0x0f, 0x80 | m1); + + return 0; +} + +static int philips_su1278_ty_ci_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u32 div; + u8 buf[4]; + struct budget *budget = (struct budget *) fe->dvb->priv; + struct i2c_msg msg = {.addr = 0x61,.flags = 0,.buf = buf,.len = sizeof(buf) }; + + if ((c->frequency < 950000) || (c->frequency > 2150000)) + return -EINVAL; + + div = (c->frequency + (125 - 1)) / 125; /* round correctly */ + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = 0x80 | ((div & 0x18000) >> 10) | 4; + buf[3] = 0x20; + + if (c->symbol_rate < 4000000) + buf[3] |= 1; + + if (c->frequency < 1250000) + buf[3] |= 0; + else if (c->frequency < 1550000) + buf[3] |= 0x40; + else if (c->frequency < 2050000) + buf[3] |= 0x80; + else if (c->frequency < 2150000) + buf[3] |= 0xC0; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) + return -EIO; + return 0; +} + +static u8 typhoon_cinergy1200s_inittab[] = { + 0x01, 0x15, + 0x02, 0x30, + 0x03, 0x00, + 0x04, 0x7d, /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */ + 0x05, 0x35, /* I2CT = 0, SCLT = 1, SDAT = 1 */ + 0x06, 0x40, /* DAC not used, set to high impendance mode */ + 0x07, 0x00, /* DAC LSB */ + 0x08, 0x40, /* DiSEqC off */ + 0x09, 0x00, /* FIFO */ + 0x0c, 0x51, /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */ + 0x0d, 0x82, /* DC offset compensation = ON, beta_agc1 = 2 */ + 0x0e, 0x23, /* alpha_tmg = 2, beta_tmg = 3 */ + 0x10, 0x3f, // AGC2 0x3d + 0x11, 0x84, + 0x12, 0xb9, + 0x15, 0xc9, // lock detector threshold + 0x16, 0x00, + 0x17, 0x00, + 0x18, 0x00, + 0x19, 0x00, + 0x1a, 0x00, + 0x1f, 0x50, + 0x20, 0x00, + 0x21, 0x00, + 0x22, 0x00, + 0x23, 0x00, + 0x28, 0x00, // out imp: normal out type: parallel FEC mode:0 + 0x29, 0x1e, // 1/2 threshold + 0x2a, 0x14, // 2/3 threshold + 0x2b, 0x0f, // 3/4 threshold + 0x2c, 0x09, // 5/6 threshold + 0x2d, 0x05, // 7/8 threshold + 0x2e, 0x01, + 0x31, 0x1f, // test all FECs + 0x32, 0x19, // viterbi and synchro search + 0x33, 0xfc, // rs control + 0x34, 0x93, // error control + 0x0f, 0x92, + 0xff, 0xff +}; + +static const struct stv0299_config typhoon_config = { + .demod_address = 0x68, + .inittab = typhoon_cinergy1200s_inittab, + .mclk = 88000000UL, + .invert = 0, + .skip_reinit = 0, + .lock_output = STV0299_LOCKOUTPUT_1, + .volt13_op0_op1 = STV0299_VOLT13_OP0, + .min_delay_ms = 100, + .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate, +}; + + +static const struct stv0299_config cinergy_1200s_config = { + .demod_address = 0x68, + .inittab = typhoon_cinergy1200s_inittab, + .mclk = 88000000UL, + .invert = 0, + .skip_reinit = 0, + .lock_output = STV0299_LOCKOUTPUT_0, + .volt13_op0_op1 = STV0299_VOLT13_OP0, + .min_delay_ms = 100, + .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate, +}; + +static const struct stv0299_config cinergy_1200s_1894_0010_config = { + .demod_address = 0x68, + .inittab = typhoon_cinergy1200s_inittab, + .mclk = 88000000UL, + .invert = 1, + .skip_reinit = 0, + .lock_output = STV0299_LOCKOUTPUT_1, + .volt13_op0_op1 = STV0299_VOLT13_OP0, + .min_delay_ms = 100, + .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate, +}; + +static int philips_cu1216_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct budget *budget = (struct budget *) fe->dvb->priv; + u8 buf[6]; + struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) }; + int i; + +#define CU1216_IF 36125000 +#define TUNER_MUL 62500 + + u32 div = (c->frequency + CU1216_IF + TUNER_MUL / 2) / TUNER_MUL; + + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = 0xce; + buf[3] = (c->frequency < 150000000 ? 0x01 : + c->frequency < 445000000 ? 0x02 : 0x04); + buf[4] = 0xde; + buf[5] = 0x20; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) + return -EIO; + + /* wait for the pll lock */ + msg.flags = I2C_M_RD; + msg.len = 1; + for (i = 0; i < 20; i++) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget->i2c_adap, &msg, 1) == 1 && (buf[0] & 0x40)) + break; + msleep(10); + } + + /* switch the charge pump to the lower current */ + msg.flags = 0; + msg.len = 2; + msg.buf = &buf[2]; + buf[2] &= ~0x40; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) + return -EIO; + + return 0; +} + +static struct tda1002x_config philips_cu1216_config = { + .demod_address = 0x0c, + .invert = 1, +}; + +static struct tda1002x_config philips_cu1216_config_altaddress = { + .demod_address = 0x0d, + .invert = 0, +}; + +static struct tda10023_config philips_cu1216_tda10023_config = { + .demod_address = 0x0c, + .invert = 1, +}; + +static int philips_tu1216_tuner_init(struct dvb_frontend *fe) +{ + struct budget *budget = (struct budget *) fe->dvb->priv; + static u8 tu1216_init[] = { 0x0b, 0xf5, 0x85, 0xab }; + struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tu1216_init,.len = sizeof(tu1216_init) }; + + // setup PLL configuration + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1) + return -EIO; + msleep(1); + + return 0; +} + +static int philips_tu1216_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct budget *budget = (struct budget *) fe->dvb->priv; + u8 tuner_buf[4]; + struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tuner_buf,.len = + sizeof(tuner_buf) }; + int tuner_frequency = 0; + u8 band, cp, filter; + + // determine charge pump + tuner_frequency = c->frequency + 36166000; + if (tuner_frequency < 87000000) + return -EINVAL; + else if (tuner_frequency < 130000000) + cp = 3; + else if (tuner_frequency < 160000000) + cp = 5; + else if (tuner_frequency < 200000000) + cp = 6; + else if (tuner_frequency < 290000000) + cp = 3; + else if (tuner_frequency < 420000000) + cp = 5; + else if (tuner_frequency < 480000000) + cp = 6; + else if (tuner_frequency < 620000000) + cp = 3; + else if (tuner_frequency < 830000000) + cp = 5; + else if (tuner_frequency < 895000000) + cp = 7; + else + return -EINVAL; + + // determine band + if (c->frequency < 49000000) + return -EINVAL; + else if (c->frequency < 161000000) + band = 1; + else if (c->frequency < 444000000) + band = 2; + else if (c->frequency < 861000000) + band = 4; + else + return -EINVAL; + + // setup PLL filter + switch (c->bandwidth_hz) { + case 6000000: + filter = 0; + break; + + case 7000000: + filter = 0; + break; + + case 8000000: + filter = 1; + break; + + default: + return -EINVAL; + } + + // calculate divisor + // ((36166000+((1000000/6)/2)) + Finput)/(1000000/6) + tuner_frequency = (((c->frequency / 1000) * 6) + 217496) / 1000; + + // setup tuner buffer + tuner_buf[0] = (tuner_frequency >> 8) & 0x7f; + tuner_buf[1] = tuner_frequency & 0xff; + tuner_buf[2] = 0xca; + tuner_buf[3] = (cp << 5) | (filter << 3) | band; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1) + return -EIO; + + msleep(1); + return 0; +} + +static int philips_tu1216_request_firmware(struct dvb_frontend *fe, + const struct firmware **fw, char *name) +{ + struct budget *budget = (struct budget *) fe->dvb->priv; + + return request_firmware(fw, name, &budget->dev->pci->dev); +} + +static struct tda1004x_config philips_tu1216_config = { + + .demod_address = 0x8, + .invert = 1, + .invert_oclk = 1, + .xtal_freq = TDA10046_XTAL_4M, + .agc_config = TDA10046_AGC_DEFAULT, + .if_freq = TDA10046_FREQ_3617, + .request_firmware = philips_tu1216_request_firmware, +}; + +static u8 philips_sd1878_inittab[] = { + 0x01, 0x15, + 0x02, 0x30, + 0x03, 0x00, + 0x04, 0x7d, + 0x05, 0x35, + 0x06, 0x40, + 0x07, 0x00, + 0x08, 0x43, + 0x09, 0x02, + 0x0C, 0x51, + 0x0D, 0x82, + 0x0E, 0x23, + 0x10, 0x3f, + 0x11, 0x84, + 0x12, 0xb9, + 0x15, 0xc9, + 0x16, 0x19, + 0x17, 0x8c, + 0x18, 0x59, + 0x19, 0xf8, + 0x1a, 0xfe, + 0x1c, 0x7f, + 0x1d, 0x00, + 0x1e, 0x00, + 0x1f, 0x50, + 0x20, 0x00, + 0x21, 0x00, + 0x22, 0x00, + 0x23, 0x00, + 0x28, 0x00, + 0x29, 0x28, + 0x2a, 0x14, + 0x2b, 0x0f, + 0x2c, 0x09, + 0x2d, 0x09, + 0x31, 0x1f, + 0x32, 0x19, + 0x33, 0xfc, + 0x34, 0x93, + 0xff, 0xff +}; + +static int philips_sd1878_ci_set_symbol_rate(struct dvb_frontend *fe, + u32 srate, u32 ratio) +{ + u8 aclk = 0; + u8 bclk = 0; + u8 m1; + + aclk = 0xb5; + if (srate < 2000000) + bclk = 0x86; + else if (srate < 5000000) + bclk = 0x89; + else if (srate < 15000000) + bclk = 0x8f; + else if (srate < 45000000) + bclk = 0x95; + + m1 = 0x14; + if (srate < 4000000) + m1 = 0x10; + + stv0299_writereg(fe, 0x0e, 0x23); + stv0299_writereg(fe, 0x0f, 0x94); + stv0299_writereg(fe, 0x10, 0x39); + stv0299_writereg(fe, 0x13, aclk); + stv0299_writereg(fe, 0x14, bclk); + stv0299_writereg(fe, 0x15, 0xc9); + stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); + stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); + stv0299_writereg(fe, 0x21, (ratio) & 0xf0); + stv0299_writereg(fe, 0x0f, 0x80 | m1); + + return 0; +} + +static const struct stv0299_config philips_sd1878_config = { + .demod_address = 0x68, + .inittab = philips_sd1878_inittab, + .mclk = 88000000UL, + .invert = 0, + .skip_reinit = 0, + .lock_output = STV0299_LOCKOUTPUT_1, + .volt13_op0_op1 = STV0299_VOLT13_OP0, + .min_delay_ms = 100, + .set_symbol_rate = philips_sd1878_ci_set_symbol_rate, +}; + +/* KNC1 DVB-S (STB0899) Inittab */ +static const struct stb0899_s1_reg knc1_stb0899_s1_init_1[] = { + + { STB0899_DEV_ID , 0x81 }, + { STB0899_DISCNTRL1 , 0x32 }, + { STB0899_DISCNTRL2 , 0x80 }, + { STB0899_DISRX_ST0 , 0x04 }, + { STB0899_DISRX_ST1 , 0x00 }, + { STB0899_DISPARITY , 0x00 }, + { STB0899_DISSTATUS , 0x20 }, + { STB0899_DISF22 , 0x8c }, + { STB0899_DISF22RX , 0x9a }, + { STB0899_SYSREG , 0x0b }, + { STB0899_ACRPRESC , 0x11 }, + { STB0899_ACRDIV1 , 0x0a }, + { STB0899_ACRDIV2 , 0x05 }, + { STB0899_DACR1 , 0x00 }, + { STB0899_DACR2 , 0x00 }, + { STB0899_OUTCFG , 0x00 }, + { STB0899_MODECFG , 0x00 }, + { STB0899_IRQSTATUS_3 , 0x30 }, + { STB0899_IRQSTATUS_2 , 0x00 }, + { STB0899_IRQSTATUS_1 , 0x00 }, + { STB0899_IRQSTATUS_0 , 0x00 }, + { STB0899_IRQMSK_3 , 0xf3 }, + { STB0899_IRQMSK_2 , 0xfc }, + { STB0899_IRQMSK_1 , 0xff }, + { STB0899_IRQMSK_0 , 0xff }, + { STB0899_IRQCFG , 0x00 }, + { STB0899_I2CCFG , 0x88 }, + { STB0899_I2CRPT , 0x58 }, /* Repeater=8, Stop=disabled */ + { STB0899_IOPVALUE5 , 0x00 }, + { STB0899_IOPVALUE4 , 0x20 }, + { STB0899_IOPVALUE3 , 0xc9 }, + { STB0899_IOPVALUE2 , 0x90 }, + { STB0899_IOPVALUE1 , 0x40 }, + { STB0899_IOPVALUE0 , 0x00 }, + { STB0899_GPIO00CFG , 0x82 }, + { STB0899_GPIO01CFG , 0x82 }, + { STB0899_GPIO02CFG , 0x82 }, + { STB0899_GPIO03CFG , 0x82 }, + { STB0899_GPIO04CFG , 0x82 }, + { STB0899_GPIO05CFG , 0x82 }, + { STB0899_GPIO06CFG , 0x82 }, + { STB0899_GPIO07CFG , 0x82 }, + { STB0899_GPIO08CFG , 0x82 }, + { STB0899_GPIO09CFG , 0x82 }, + { STB0899_GPIO10CFG , 0x82 }, + { STB0899_GPIO11CFG , 0x82 }, + { STB0899_GPIO12CFG , 0x82 }, + { STB0899_GPIO13CFG , 0x82 }, + { STB0899_GPIO14CFG , 0x82 }, + { STB0899_GPIO15CFG , 0x82 }, + { STB0899_GPIO16CFG , 0x82 }, + { STB0899_GPIO17CFG , 0x82 }, + { STB0899_GPIO18CFG , 0x82 }, + { STB0899_GPIO19CFG , 0x82 }, + { STB0899_GPIO20CFG , 0x82 }, + { STB0899_SDATCFG , 0xb8 }, + { STB0899_SCLTCFG , 0xba }, + { STB0899_AGCRFCFG , 0x08 }, /* 0x1c */ + { STB0899_GPIO22 , 0x82 }, /* AGCBB2CFG */ + { STB0899_GPIO21 , 0x91 }, /* AGCBB1CFG */ + { STB0899_DIRCLKCFG , 0x82 }, + { STB0899_CLKOUT27CFG , 0x7e }, + { STB0899_STDBYCFG , 0x82 }, + { STB0899_CS0CFG , 0x82 }, + { STB0899_CS1CFG , 0x82 }, + { STB0899_DISEQCOCFG , 0x20 }, + { STB0899_GPIO32CFG , 0x82 }, + { STB0899_GPIO33CFG , 0x82 }, + { STB0899_GPIO34CFG , 0x82 }, + { STB0899_GPIO35CFG , 0x82 }, + { STB0899_GPIO36CFG , 0x82 }, + { STB0899_GPIO37CFG , 0x82 }, + { STB0899_GPIO38CFG , 0x82 }, + { STB0899_GPIO39CFG , 0x82 }, + { STB0899_NCOARSE , 0x15 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */ + { STB0899_SYNTCTRL , 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */ + { STB0899_FILTCTRL , 0x00 }, + { STB0899_SYSCTRL , 0x00 }, + { STB0899_STOPCLK1 , 0x20 }, + { STB0899_STOPCLK2 , 0x00 }, + { STB0899_INTBUFSTATUS , 0x00 }, + { STB0899_INTBUFCTRL , 0x0a }, + { 0xffff , 0xff }, +}; + +static const struct stb0899_s1_reg knc1_stb0899_s1_init_3[] = { + { STB0899_DEMOD , 0x00 }, + { STB0899_RCOMPC , 0xc9 }, + { STB0899_AGC1CN , 0x41 }, + { STB0899_AGC1REF , 0x08 }, + { STB0899_RTC , 0x7a }, + { STB0899_TMGCFG , 0x4e }, + { STB0899_AGC2REF , 0x33 }, + { STB0899_TLSR , 0x84 }, + { STB0899_CFD , 0xee }, + { STB0899_ACLC , 0x87 }, + { STB0899_BCLC , 0x94 }, + { STB0899_EQON , 0x41 }, + { STB0899_LDT , 0xdd }, + { STB0899_LDT2 , 0xc9 }, + { STB0899_EQUALREF , 0xb4 }, + { STB0899_TMGRAMP , 0x10 }, + { STB0899_TMGTHD , 0x30 }, + { STB0899_IDCCOMP , 0xfb }, + { STB0899_QDCCOMP , 0x03 }, + { STB0899_POWERI , 0x3b }, + { STB0899_POWERQ , 0x3d }, + { STB0899_RCOMP , 0x81 }, + { STB0899_AGCIQIN , 0x80 }, + { STB0899_AGC2I1 , 0x04 }, + { STB0899_AGC2I2 , 0xf5 }, + { STB0899_TLIR , 0x25 }, + { STB0899_RTF , 0x80 }, + { STB0899_DSTATUS , 0x00 }, + { STB0899_LDI , 0xca }, + { STB0899_CFRM , 0xf1 }, + { STB0899_CFRL , 0xf3 }, + { STB0899_NIRM , 0x2a }, + { STB0899_NIRL , 0x05 }, + { STB0899_ISYMB , 0x17 }, + { STB0899_QSYMB , 0xfa }, + { STB0899_SFRH , 0x2f }, + { STB0899_SFRM , 0x68 }, + { STB0899_SFRL , 0x40 }, + { STB0899_SFRUPH , 0x2f }, + { STB0899_SFRUPM , 0x68 }, + { STB0899_SFRUPL , 0x40 }, + { STB0899_EQUAI1 , 0xfd }, + { STB0899_EQUAQ1 , 0x04 }, + { STB0899_EQUAI2 , 0x0f }, + { STB0899_EQUAQ2 , 0xff }, + { STB0899_EQUAI3 , 0xdf }, + { STB0899_EQUAQ3 , 0xfa }, + { STB0899_EQUAI4 , 0x37 }, + { STB0899_EQUAQ4 , 0x0d }, + { STB0899_EQUAI5 , 0xbd }, + { STB0899_EQUAQ5 , 0xf7 }, + { STB0899_DSTATUS2 , 0x00 }, + { STB0899_VSTATUS , 0x00 }, + { STB0899_VERROR , 0xff }, + { STB0899_IQSWAP , 0x2a }, + { STB0899_ECNT1M , 0x00 }, + { STB0899_ECNT1L , 0x00 }, + { STB0899_ECNT2M , 0x00 }, + { STB0899_ECNT2L , 0x00 }, + { STB0899_ECNT3M , 0x00 }, + { STB0899_ECNT3L , 0x00 }, + { STB0899_FECAUTO1 , 0x06 }, + { STB0899_FECM , 0x01 }, + { STB0899_VTH12 , 0xf0 }, + { STB0899_VTH23 , 0xa0 }, + { STB0899_VTH34 , 0x78 }, + { STB0899_VTH56 , 0x4e }, + { STB0899_VTH67 , 0x48 }, + { STB0899_VTH78 , 0x38 }, + { STB0899_PRVIT , 0xff }, + { STB0899_VITSYNC , 0x19 }, + { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */ + { STB0899_TSULC , 0x42 }, + { STB0899_RSLLC , 0x40 }, + { STB0899_TSLPL , 0x12 }, + { STB0899_TSCFGH , 0x0c }, + { STB0899_TSCFGM , 0x00 }, + { STB0899_TSCFGL , 0x0c }, + { STB0899_TSOUT , 0x4d }, /* 0x0d for CAM */ + { STB0899_RSSYNCDEL , 0x00 }, + { STB0899_TSINHDELH , 0x02 }, + { STB0899_TSINHDELM , 0x00 }, + { STB0899_TSINHDELL , 0x00 }, + { STB0899_TSLLSTKM , 0x00 }, + { STB0899_TSLLSTKL , 0x00 }, + { STB0899_TSULSTKM , 0x00 }, + { STB0899_TSULSTKL , 0xab }, + { STB0899_PCKLENUL , 0x00 }, + { STB0899_PCKLENLL , 0xcc }, + { STB0899_RSPCKLEN , 0xcc }, + { STB0899_TSSTATUS , 0x80 }, + { STB0899_ERRCTRL1 , 0xb6 }, + { STB0899_ERRCTRL2 , 0x96 }, + { STB0899_ERRCTRL3 , 0x89 }, + { STB0899_DMONMSK1 , 0x27 }, + { STB0899_DMONMSK0 , 0x03 }, + { STB0899_DEMAPVIT , 0x5c }, + { STB0899_PLPARM , 0x1f }, + { STB0899_PDELCTRL , 0x48 }, + { STB0899_PDELCTRL2 , 0x00 }, + { STB0899_BBHCTRL1 , 0x00 }, + { STB0899_BBHCTRL2 , 0x00 }, + { STB0899_HYSTTHRESH , 0x77 }, + { STB0899_MATCSTM , 0x00 }, + { STB0899_MATCSTL , 0x00 }, + { STB0899_UPLCSTM , 0x00 }, + { STB0899_UPLCSTL , 0x00 }, + { STB0899_DFLCSTM , 0x00 }, + { STB0899_DFLCSTL , 0x00 }, + { STB0899_SYNCCST , 0x00 }, + { STB0899_SYNCDCSTM , 0x00 }, + { STB0899_SYNCDCSTL , 0x00 }, + { STB0899_ISI_ENTRY , 0x00 }, + { STB0899_ISI_BIT_EN , 0x00 }, + { STB0899_MATSTRM , 0x00 }, + { STB0899_MATSTRL , 0x00 }, + { STB0899_UPLSTRM , 0x00 }, + { STB0899_UPLSTRL , 0x00 }, + { STB0899_DFLSTRM , 0x00 }, + { STB0899_DFLSTRL , 0x00 }, + { STB0899_SYNCSTR , 0x00 }, + { STB0899_SYNCDSTRM , 0x00 }, + { STB0899_SYNCDSTRL , 0x00 }, + { STB0899_CFGPDELSTATUS1 , 0x10 }, + { STB0899_CFGPDELSTATUS2 , 0x00 }, + { STB0899_BBFERRORM , 0x00 }, + { STB0899_BBFERRORL , 0x00 }, + { STB0899_UPKTERRORM , 0x00 }, + { STB0899_UPKTERRORL , 0x00 }, + { 0xffff , 0xff }, +}; + +/* STB0899 demodulator config for the KNC1 and clones */ +static struct stb0899_config knc1_dvbs2_config = { + .init_dev = knc1_stb0899_s1_init_1, + .init_s2_demod = stb0899_s2_init_2, + .init_s1_demod = knc1_stb0899_s1_init_3, + .init_s2_fec = stb0899_s2_init_4, + .init_tst = stb0899_s1_init_5, + + .postproc = NULL, + + .demod_address = 0x68, +// .ts_output_mode = STB0899_OUT_PARALLEL, /* types = SERIAL/PARALLEL */ + .block_sync_mode = STB0899_SYNC_FORCED, /* DSS, SYNC_FORCED/UNSYNCED */ +// .ts_pfbit_toggle = STB0899_MPEG_NORMAL, /* DirecTV, MPEG toggling seq */ + + .xtal_freq = 27000000, + .inversion = IQ_SWAP_OFF, + + .lo_clk = 76500000, + .hi_clk = 90000000, + + .esno_ave = STB0899_DVBS2_ESNO_AVE, + .esno_quant = STB0899_DVBS2_ESNO_QUANT, + .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE, + .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE, + .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD, + .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ, + .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK, + .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF, + .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT, + + .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS, + .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET, + .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS, + .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER, + + .tuner_get_frequency = tda8261_get_frequency, + .tuner_set_frequency = tda8261_set_frequency, + .tuner_set_bandwidth = NULL, + .tuner_get_bandwidth = tda8261_get_bandwidth, + .tuner_set_rfsiggain = NULL +}; + +/* + * SD1878/SHA tuner config + * 1F, Single I/P, Horizontal mount, High Sensitivity + */ +static const struct tda8261_config sd1878c_config = { +// .name = "SD1878/SHA", + .addr = 0x60, + .step_size = TDA8261_STEP_1000 /* kHz */ +}; + +static u8 read_pwm(struct budget_av *budget_av) +{ + u8 b = 0xff; + u8 pwm; + struct i2c_msg msg[] = { {.addr = 0x50,.flags = 0,.buf = &b,.len = 1}, + {.addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} + }; + + if ((i2c_transfer(&budget_av->budget.i2c_adap, msg, 2) != 2) + || (pwm == 0xff)) + pwm = 0x48; + + return pwm; +} + +#define SUBID_DVBS_KNC1 0x0010 +#define SUBID_DVBS_KNC1_PLUS 0x0011 +#define SUBID_DVBS_TYPHOON 0x4f56 +#define SUBID_DVBS_CINERGY1200 0x1154 +#define SUBID_DVBS_CYNERGY1200N 0x1155 +#define SUBID_DVBS_TV_STAR 0x0014 +#define SUBID_DVBS_TV_STAR_PLUS_X4 0x0015 +#define SUBID_DVBS_TV_STAR_CI 0x0016 +#define SUBID_DVBS2_KNC1 0x0018 +#define SUBID_DVBS2_KNC1_OEM 0x0019 +#define SUBID_DVBS_EASYWATCH_1 0x001a +#define SUBID_DVBS_EASYWATCH_2 0x001b +#define SUBID_DVBS2_EASYWATCH 0x001d +#define SUBID_DVBS_EASYWATCH 0x001e + +#define SUBID_DVBC_EASYWATCH 0x002a +#define SUBID_DVBC_EASYWATCH_MK3 0x002c +#define SUBID_DVBC_KNC1 0x0020 +#define SUBID_DVBC_KNC1_PLUS 0x0021 +#define SUBID_DVBC_KNC1_MK3 0x0022 +#define SUBID_DVBC_KNC1_TDA10024 0x0028 +#define SUBID_DVBC_KNC1_PLUS_MK3 0x0023 +#define SUBID_DVBC_CINERGY1200 0x1156 +#define SUBID_DVBC_CINERGY1200_MK3 0x1176 + +#define SUBID_DVBT_EASYWATCH 0x003a +#define SUBID_DVBT_KNC1_PLUS 0x0031 +#define SUBID_DVBT_KNC1 0x0030 +#define SUBID_DVBT_CINERGY1200 0x1157 + +static void frontend_init(struct budget_av *budget_av) +{ + struct saa7146_dev * saa = budget_av->budget.dev; + struct dvb_frontend * fe = NULL; + + /* Enable / PowerON Frontend */ + saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO); + + /* Wait for PowerON */ + msleep(100); + + /* additional setup necessary for the PLUS cards */ + switch (saa->pci->subsystem_device) { + case SUBID_DVBS_KNC1_PLUS: + case SUBID_DVBC_KNC1_PLUS: + case SUBID_DVBT_KNC1_PLUS: + case SUBID_DVBC_EASYWATCH: + case SUBID_DVBC_KNC1_PLUS_MK3: + case SUBID_DVBS2_KNC1: + case SUBID_DVBS2_KNC1_OEM: + case SUBID_DVBS2_EASYWATCH: + saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTHI); + break; + } + + switch (saa->pci->subsystem_device) { + + case SUBID_DVBS_KNC1: + /* + * maybe that setting is needed for other dvb-s cards as well, + * but so far it has been only confirmed for this type + */ + budget_av->reinitialise_demod = 1; + fallthrough; + case SUBID_DVBS_KNC1_PLUS: + case SUBID_DVBS_EASYWATCH_1: + if (saa->pci->subsystem_vendor == 0x1894) { + fe = dvb_attach(stv0299_attach, &cinergy_1200s_1894_0010_config, + &budget_av->budget.i2c_adap); + if (fe) { + dvb_attach(tua6100_attach, fe, 0x60, &budget_av->budget.i2c_adap); + } + } else { + fe = dvb_attach(stv0299_attach, &typhoon_config, + &budget_av->budget.i2c_adap); + if (fe) { + fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params; + } + } + break; + + case SUBID_DVBS_TV_STAR: + case SUBID_DVBS_TV_STAR_PLUS_X4: + case SUBID_DVBS_TV_STAR_CI: + case SUBID_DVBS_CYNERGY1200N: + case SUBID_DVBS_EASYWATCH: + case SUBID_DVBS_EASYWATCH_2: + fe = dvb_attach(stv0299_attach, &philips_sd1878_config, + &budget_av->budget.i2c_adap); + if (fe) { + dvb_attach(dvb_pll_attach, fe, 0x60, + &budget_av->budget.i2c_adap, + DVB_PLL_PHILIPS_SD1878_TDA8261); + } + break; + + case SUBID_DVBS_TYPHOON: + fe = dvb_attach(stv0299_attach, &typhoon_config, + &budget_av->budget.i2c_adap); + if (fe) { + fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params; + } + break; + case SUBID_DVBS2_KNC1: + case SUBID_DVBS2_KNC1_OEM: + case SUBID_DVBS2_EASYWATCH: + budget_av->reinitialise_demod = 1; + if ((fe = dvb_attach(stb0899_attach, &knc1_dvbs2_config, &budget_av->budget.i2c_adap))) + dvb_attach(tda8261_attach, fe, &sd1878c_config, &budget_av->budget.i2c_adap); + + break; + case SUBID_DVBS_CINERGY1200: + fe = dvb_attach(stv0299_attach, &cinergy_1200s_config, + &budget_av->budget.i2c_adap); + if (fe) { + fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params; + } + break; + + case SUBID_DVBC_KNC1: + case SUBID_DVBC_KNC1_PLUS: + case SUBID_DVBC_CINERGY1200: + case SUBID_DVBC_EASYWATCH: + budget_av->reinitialise_demod = 1; + budget_av->budget.dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240; + fe = dvb_attach(tda10021_attach, &philips_cu1216_config, + &budget_av->budget.i2c_adap, + read_pwm(budget_av)); + if (fe == NULL) + fe = dvb_attach(tda10021_attach, &philips_cu1216_config_altaddress, + &budget_av->budget.i2c_adap, + read_pwm(budget_av)); + if (fe) { + fe->ops.tuner_ops.set_params = philips_cu1216_tuner_set_params; + } + break; + + case SUBID_DVBC_EASYWATCH_MK3: + case SUBID_DVBC_CINERGY1200_MK3: + case SUBID_DVBC_KNC1_MK3: + case SUBID_DVBC_KNC1_TDA10024: + case SUBID_DVBC_KNC1_PLUS_MK3: + budget_av->reinitialise_demod = 1; + budget_av->budget.dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240; + fe = dvb_attach(tda10023_attach, + &philips_cu1216_tda10023_config, + &budget_av->budget.i2c_adap, + read_pwm(budget_av)); + if (fe) { + fe->ops.tuner_ops.set_params = philips_cu1216_tuner_set_params; + } + break; + + case SUBID_DVBT_EASYWATCH: + case SUBID_DVBT_KNC1: + case SUBID_DVBT_KNC1_PLUS: + case SUBID_DVBT_CINERGY1200: + budget_av->reinitialise_demod = 1; + fe = dvb_attach(tda10046_attach, &philips_tu1216_config, + &budget_av->budget.i2c_adap); + if (fe) { + fe->ops.tuner_ops.init = philips_tu1216_tuner_init; + fe->ops.tuner_ops.set_params = philips_tu1216_tuner_set_params; + } + break; + } + + if (fe == NULL) { + pr_err("A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", + saa->pci->vendor, + saa->pci->device, + saa->pci->subsystem_vendor, + saa->pci->subsystem_device); + return; + } + + budget_av->budget.dvb_frontend = fe; + + if (dvb_register_frontend(&budget_av->budget.dvb_adapter, + budget_av->budget.dvb_frontend)) { + pr_err("Frontend registration failed!\n"); + dvb_frontend_detach(budget_av->budget.dvb_frontend); + budget_av->budget.dvb_frontend = NULL; + } +} + + +static void budget_av_irq(struct saa7146_dev *dev, u32 * isr) +{ + struct budget_av *budget_av = (struct budget_av *) dev->ext_priv; + + dprintk(8, "dev: %p, budget_av: %p\n", dev, budget_av); + + if (*isr & MASK_10) + ttpci_budget_irq10_handler(dev, isr); +} + +static int budget_av_detach(struct saa7146_dev *dev) +{ + struct budget_av *budget_av = (struct budget_av *) dev->ext_priv; + int err; + + dprintk(2, "dev: %p\n", dev); + + if (1 == budget_av->has_saa7113) { + saa7146_setgpio(dev, 0, SAA7146_GPIO_OUTLO); + + msleep(200); + + saa7146_unregister_device(&budget_av->vd, dev); + + saa7146_vv_release(dev); + } + + if (budget_av->budget.ci_present) + ciintf_deinit(budget_av); + + if (budget_av->budget.dvb_frontend != NULL) { + dvb_unregister_frontend(budget_av->budget.dvb_frontend); + dvb_frontend_detach(budget_av->budget.dvb_frontend); + } + err = ttpci_budget_deinit(&budget_av->budget); + + kfree(budget_av); + + return err; +} + +#define KNC1_INPUTS 2 +static struct v4l2_input knc1_inputs[KNC1_INPUTS] = { + { 0, "Composite", V4L2_INPUT_TYPE_TUNER, 1, 0, + V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, + { 1, "S-Video", V4L2_INPUT_TYPE_CAMERA, 2, 0, + V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, +}; + +static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) +{ + dprintk(1, "VIDIOC_ENUMINPUT %d\n", i->index); + if (i->index >= KNC1_INPUTS) + return -EINVAL; + memcpy(i, &knc1_inputs[i->index], sizeof(struct v4l2_input)); + return 0; +} + +static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct budget_av *budget_av = (struct budget_av *)dev->ext_priv; + + *i = budget_av->cur_input; + + dprintk(1, "VIDIOC_G_INPUT %d\n", *i); + return 0; +} + +static int vidioc_s_input(struct file *file, void *fh, unsigned int input) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct budget_av *budget_av = (struct budget_av *)dev->ext_priv; + + dprintk(1, "VIDIOC_S_INPUT %d\n", input); + return saa7113_setinput(budget_av, input); +} + +static struct saa7146_ext_vv vv_data; + +static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) +{ + struct budget_av *budget_av; + u8 *mac; + int err; + + dprintk(2, "dev: %p\n", dev); + + if (!(budget_av = kzalloc(sizeof(struct budget_av), GFP_KERNEL))) + return -ENOMEM; + + budget_av->has_saa7113 = 0; + budget_av->budget.ci_present = 0; + + dev->ext_priv = budget_av; + + err = ttpci_budget_init(&budget_av->budget, dev, info, THIS_MODULE, + adapter_nr); + if (err) { + kfree(budget_av); + return err; + } + + /* knc1 initialization */ + saa7146_write(dev, DD1_STREAM_B, 0x04000000); + saa7146_write(dev, DD1_INIT, 0x07000600); + saa7146_write(dev, MC2, MASK_09 | MASK_25 | MASK_10 | MASK_26); + + if (saa7113_init(budget_av) == 0) { + budget_av->has_saa7113 = 1; + err = saa7146_vv_init(dev, &vv_data); + if (err != 0) { + /* fixme: proper cleanup here */ + ERR("cannot init vv subsystem\n"); + return err; + } + vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; + vv_data.vid_ops.vidioc_g_input = vidioc_g_input; + vv_data.vid_ops.vidioc_s_input = vidioc_s_input; + + if ((err = saa7146_register_device(&budget_av->vd, dev, "knc1", VFL_TYPE_VIDEO))) { + /* fixme: proper cleanup here */ + ERR("cannot register capture v4l2 device\n"); + saa7146_vv_release(dev); + return err; + } + + /* beware: this modifies dev->vv ... */ + saa7146_set_hps_source_and_sync(dev, SAA7146_HPS_SOURCE_PORT_A, + SAA7146_HPS_SYNC_PORT_A); + + saa7113_setinput(budget_av, 0); + } + + /* fixme: find some sane values here... */ + saa7146_write(dev, PCI_BT_V1, 0x1c00101f); + + mac = budget_av->budget.dvb_adapter.proposed_mac; + if (i2c_readregs(&budget_av->budget.i2c_adap, 0xa0, 0x30, mac, 6)) { + pr_err("KNC1-%d: Could not read MAC from KNC1 card\n", + budget_av->budget.dvb_adapter.num); + eth_zero_addr(mac); + } else { + pr_info("KNC1-%d: MAC addr = %pM\n", + budget_av->budget.dvb_adapter.num, mac); + } + + budget_av->budget.dvb_adapter.priv = budget_av; + frontend_init(budget_av); + ciintf_init(budget_av); + + ttpci_budget_init_hooks(&budget_av->budget); + + return 0; +} + +static struct saa7146_standard standard[] = { + {.name = "PAL",.id = V4L2_STD_PAL, + .v_offset = 0x17,.v_field = 288, + .h_offset = 0x14,.h_pixels = 680, + .v_max_out = 576,.h_max_out = 768 }, + + {.name = "NTSC",.id = V4L2_STD_NTSC, + .v_offset = 0x16,.v_field = 240, + .h_offset = 0x06,.h_pixels = 708, + .v_max_out = 480,.h_max_out = 640, }, +}; + +static struct saa7146_ext_vv vv_data = { + .inputs = 2, + .capabilities = 0, // perhaps later: V4L2_CAP_VBI_CAPTURE, but that need tweaking with the saa7113 + .flags = 0, + .stds = &standard[0], + .num_stds = ARRAY_SIZE(standard), +}; + +static struct saa7146_extension budget_extension; + +MAKE_BUDGET_INFO(knc1s, "KNC1 DVB-S", BUDGET_KNC1S); +MAKE_BUDGET_INFO(knc1s2,"KNC1 DVB-S2", BUDGET_KNC1S2); +MAKE_BUDGET_INFO(sates2,"Satelco EasyWatch DVB-S2", BUDGET_KNC1S2); +MAKE_BUDGET_INFO(knc1c, "KNC1 DVB-C", BUDGET_KNC1C); +MAKE_BUDGET_INFO(knc1t, "KNC1 DVB-T", BUDGET_KNC1T); +MAKE_BUDGET_INFO(kncxs, "KNC TV STAR DVB-S", BUDGET_TVSTAR); +MAKE_BUDGET_INFO(satewpls, "Satelco EasyWatch DVB-S light", BUDGET_TVSTAR); +MAKE_BUDGET_INFO(satewpls1, "Satelco EasyWatch DVB-S light", BUDGET_KNC1S); +MAKE_BUDGET_INFO(satewps, "Satelco EasyWatch DVB-S", BUDGET_KNC1S); +MAKE_BUDGET_INFO(satewplc, "Satelco EasyWatch DVB-C", BUDGET_KNC1CP); +MAKE_BUDGET_INFO(satewcmk3, "Satelco EasyWatch DVB-C MK3", BUDGET_KNC1C_MK3); +MAKE_BUDGET_INFO(satewt, "Satelco EasyWatch DVB-T", BUDGET_KNC1T); +MAKE_BUDGET_INFO(knc1sp, "KNC1 DVB-S Plus", BUDGET_KNC1SP); +MAKE_BUDGET_INFO(knc1spx4, "KNC1 DVB-S Plus X4", BUDGET_KNC1SP); +MAKE_BUDGET_INFO(knc1cp, "KNC1 DVB-C Plus", BUDGET_KNC1CP); +MAKE_BUDGET_INFO(knc1cmk3, "KNC1 DVB-C MK3", BUDGET_KNC1C_MK3); +MAKE_BUDGET_INFO(knc1ctda10024, "KNC1 DVB-C TDA10024", BUDGET_KNC1C_TDA10024); +MAKE_BUDGET_INFO(knc1cpmk3, "KNC1 DVB-C Plus MK3", BUDGET_KNC1CP_MK3); +MAKE_BUDGET_INFO(knc1tp, "KNC1 DVB-T Plus", BUDGET_KNC1TP); +MAKE_BUDGET_INFO(cin1200s, "TerraTec Cinergy 1200 DVB-S", BUDGET_CIN1200S); +MAKE_BUDGET_INFO(cin1200sn, "TerraTec Cinergy 1200 DVB-S", BUDGET_CIN1200S); +MAKE_BUDGET_INFO(cin1200c, "Terratec Cinergy 1200 DVB-C", BUDGET_CIN1200C); +MAKE_BUDGET_INFO(cin1200cmk3, "Terratec Cinergy 1200 DVB-C MK3", BUDGET_CIN1200C_MK3); +MAKE_BUDGET_INFO(cin1200t, "Terratec Cinergy 1200 DVB-T", BUDGET_CIN1200T); + +static const struct pci_device_id pci_tbl[] = { + MAKE_EXTENSION_PCI(knc1s, 0x1131, 0x4f56), + MAKE_EXTENSION_PCI(knc1s, 0x1131, 0x0010), + MAKE_EXTENSION_PCI(knc1s, 0x1894, 0x0010), + MAKE_EXTENSION_PCI(knc1sp, 0x1131, 0x0011), + MAKE_EXTENSION_PCI(knc1sp, 0x1894, 0x0011), + MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0014), + MAKE_EXTENSION_PCI(knc1spx4, 0x1894, 0x0015), + MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0016), + MAKE_EXTENSION_PCI(knc1s2, 0x1894, 0x0018), + MAKE_EXTENSION_PCI(knc1s2, 0x1894, 0x0019), + MAKE_EXTENSION_PCI(sates2, 0x1894, 0x001d), + MAKE_EXTENSION_PCI(satewpls, 0x1894, 0x001e), + MAKE_EXTENSION_PCI(satewpls1, 0x1894, 0x001a), + MAKE_EXTENSION_PCI(satewps, 0x1894, 0x001b), + MAKE_EXTENSION_PCI(satewplc, 0x1894, 0x002a), + MAKE_EXTENSION_PCI(satewcmk3, 0x1894, 0x002c), + MAKE_EXTENSION_PCI(satewt, 0x1894, 0x003a), + MAKE_EXTENSION_PCI(knc1c, 0x1894, 0x0020), + MAKE_EXTENSION_PCI(knc1cp, 0x1894, 0x0021), + MAKE_EXTENSION_PCI(knc1cmk3, 0x1894, 0x0022), + MAKE_EXTENSION_PCI(knc1ctda10024, 0x1894, 0x0028), + MAKE_EXTENSION_PCI(knc1cpmk3, 0x1894, 0x0023), + MAKE_EXTENSION_PCI(knc1t, 0x1894, 0x0030), + MAKE_EXTENSION_PCI(knc1tp, 0x1894, 0x0031), + MAKE_EXTENSION_PCI(cin1200s, 0x153b, 0x1154), + MAKE_EXTENSION_PCI(cin1200sn, 0x153b, 0x1155), + MAKE_EXTENSION_PCI(cin1200c, 0x153b, 0x1156), + MAKE_EXTENSION_PCI(cin1200cmk3, 0x153b, 0x1176), + MAKE_EXTENSION_PCI(cin1200t, 0x153b, 0x1157), + { + .vendor = 0, + } +}; + +MODULE_DEVICE_TABLE(pci, pci_tbl); + +static struct saa7146_extension budget_extension = { + .name = "budget_av", + .flags = SAA7146_USE_I2C_IRQ, + + .pci_tbl = pci_tbl, + + .module = THIS_MODULE, + .attach = budget_av_attach, + .detach = budget_av_detach, + + .irq_mask = MASK_10, + .irq_func = budget_av_irq, +}; + +static int __init budget_av_init(void) +{ + return saa7146_register_extension(&budget_extension); +} + +static void __exit budget_av_exit(void) +{ + saa7146_unregister_extension(&budget_extension); +} + +module_init(budget_av_init); +module_exit(budget_av_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, Michael Hunold, others"); +MODULE_DESCRIPTION("driver for the SAA7146 based so-called budget PCI DVB w/ analog input and CI-module (e.g. the KNC cards)"); diff --git a/drivers/staging/media/deprecated/saa7146/ttpci/budget-ci.c b/drivers/staging/media/deprecated/saa7146/ttpci/budget-ci.c new file mode 100644 index 000000000000..d59d18647371 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/ttpci/budget-ci.c @@ -0,0 +1,1574 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * budget-ci.c: driver for the SAA7146 based Budget DVB cards + * + * Compiled from various sources by Michael Hunold <michael@mihu.de> + * + * msp430 IR support contributed by Jack Thomasson <jkt@Helius.COM> + * partially based on the Siemens DVB driver by Ralph+Marcus Metzler + * + * CI interface support (c) 2004 Andrew de Quincey <adq_dvb@lidskialf.net> + * + * the project's page is at https://linuxtv.org + */ + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <media/rc-core.h> + +#include "budget.h" + +#include <media/dvb_ca_en50221.h> +#include "stv0299.h" +#include "stv0297.h" +#include "tda1004x.h" +#include "stb0899_drv.h" +#include "stb0899_reg.h" +#include "stb0899_cfg.h" +#include "stb6100.h" +#include "stb6100_cfg.h" +#include "lnbp21.h" +#include "bsbe1.h" +#include "bsru6.h" +#include "tda1002x.h" +#include "tda827x.h" +#include "bsbe1-d01a.h" + +#define MODULE_NAME "budget_ci" + +/* + * Regarding DEBIADDR_IR: + * Some CI modules hang if random addresses are read. + * Using address 0x4000 for the IR read means that we + * use the same address as for CI version, which should + * be a safe default. + */ +#define DEBIADDR_IR 0x4000 +#define DEBIADDR_CICONTROL 0x0000 +#define DEBIADDR_CIVERSION 0x4000 +#define DEBIADDR_IO 0x1000 +#define DEBIADDR_ATTR 0x3000 + +#define CICONTROL_RESET 0x01 +#define CICONTROL_ENABLETS 0x02 +#define CICONTROL_CAMDETECT 0x08 + +#define DEBICICTL 0x00420000 +#define DEBICICAM 0x02420000 + +#define SLOTSTATUS_NONE 1 +#define SLOTSTATUS_PRESENT 2 +#define SLOTSTATUS_RESET 4 +#define SLOTSTATUS_READY 8 +#define SLOTSTATUS_OCCUPIED (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY) + +/* RC5 device wildcard */ +#define IR_DEVICE_ANY 255 + +static int rc5_device = -1; +module_param(rc5_device, int, 0644); +MODULE_PARM_DESC(rc5_device, "only IR commands to given RC5 device (device = 0 - 31, any device = 255, default: autodetect)"); + +static int ir_debug; +module_param(ir_debug, int, 0644); +MODULE_PARM_DESC(ir_debug, "enable debugging information for IR decoding"); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +struct budget_ci_ir { + struct rc_dev *dev; + struct tasklet_struct msp430_irq_tasklet; + char name[72]; /* 40 + 32 for (struct saa7146_dev).name */ + char phys[32]; + int rc5_device; + u32 ir_key; + bool have_command; + bool full_rc5; /* Outputs a full RC5 code */ +}; + +struct budget_ci { + struct budget budget; + struct tasklet_struct ciintf_irq_tasklet; + int slot_status; + int ci_irq; + struct dvb_ca_en50221 ca; + struct budget_ci_ir ir; + u8 tuner_pll_address; /* used for philips_tdm1316l configs */ +}; + +static void msp430_ir_interrupt(struct tasklet_struct *t) +{ + struct budget_ci_ir *ir = from_tasklet(ir, t, msp430_irq_tasklet); + struct budget_ci *budget_ci = container_of(ir, typeof(*budget_ci), ir); + struct rc_dev *dev = budget_ci->ir.dev; + u32 command = ttpci_budget_debiread(&budget_ci->budget, DEBINOSWAP, DEBIADDR_IR, 2, 1, 0) >> 8; + + /* + * The msp430 chip can generate two different bytes, command and device + * + * type1: X1CCCCCC, C = command bits (0 - 63) + * type2: X0TDDDDD, D = device bits (0 - 31), T = RC5 toggle bit + * + * Each signal from the remote control can generate one or more command + * bytes and one or more device bytes. For the repeated bytes, the + * highest bit (X) is set. The first command byte is always generated + * before the first device byte. Other than that, no specific order + * seems to apply. To make life interesting, bytes can also be lost. + * + * Only when we have a command and device byte, a keypress is + * generated. + */ + + if (ir_debug) + printk("budget_ci: received byte 0x%02x\n", command); + + /* Remove repeat bit, we use every command */ + command = command & 0x7f; + + /* Is this a RC5 command byte? */ + if (command & 0x40) { + budget_ci->ir.have_command = true; + budget_ci->ir.ir_key = command & 0x3f; + return; + } + + /* It's a RC5 device byte */ + if (!budget_ci->ir.have_command) + return; + budget_ci->ir.have_command = false; + + if (budget_ci->ir.rc5_device != IR_DEVICE_ANY && + budget_ci->ir.rc5_device != (command & 0x1f)) + return; + + if (budget_ci->ir.full_rc5) { + rc_keydown(dev, RC_PROTO_RC5, + RC_SCANCODE_RC5(budget_ci->ir.rc5_device, budget_ci->ir.ir_key), + !!(command & 0x20)); + return; + } + + /* FIXME: We should generate complete scancodes for all devices */ + rc_keydown(dev, RC_PROTO_UNKNOWN, budget_ci->ir.ir_key, + !!(command & 0x20)); +} + +static int msp430_ir_init(struct budget_ci *budget_ci) +{ + struct saa7146_dev *saa = budget_ci->budget.dev; + struct rc_dev *dev; + int error; + + dev = rc_allocate_device(RC_DRIVER_SCANCODE); + if (!dev) { + printk(KERN_ERR "budget_ci: IR interface initialisation failed\n"); + return -ENOMEM; + } + + snprintf(budget_ci->ir.name, sizeof(budget_ci->ir.name), + "Budget-CI dvb ir receiver %s", saa->name); + snprintf(budget_ci->ir.phys, sizeof(budget_ci->ir.phys), + "pci-%s/ir0", pci_name(saa->pci)); + + dev->driver_name = MODULE_NAME; + dev->device_name = budget_ci->ir.name; + dev->input_phys = budget_ci->ir.phys; + dev->input_id.bustype = BUS_PCI; + dev->input_id.version = 1; + if (saa->pci->subsystem_vendor) { + dev->input_id.vendor = saa->pci->subsystem_vendor; + dev->input_id.product = saa->pci->subsystem_device; + } else { + dev->input_id.vendor = saa->pci->vendor; + dev->input_id.product = saa->pci->device; + } + dev->dev.parent = &saa->pci->dev; + + if (rc5_device < 0) + budget_ci->ir.rc5_device = IR_DEVICE_ANY; + else + budget_ci->ir.rc5_device = rc5_device; + + /* Select keymap and address */ + switch (budget_ci->budget.dev->pci->subsystem_device) { + case 0x100c: + case 0x100f: + case 0x1011: + case 0x1012: + /* The hauppauge keymap is a superset of these remotes */ + dev->map_name = RC_MAP_HAUPPAUGE; + budget_ci->ir.full_rc5 = true; + + if (rc5_device < 0) + budget_ci->ir.rc5_device = 0x1f; + break; + case 0x1010: + case 0x1017: + case 0x1019: + case 0x101a: + case 0x101b: + /* for the Technotrend 1500 bundled remote */ + dev->map_name = RC_MAP_TT_1500; + break; + default: + /* unknown remote */ + dev->map_name = RC_MAP_BUDGET_CI_OLD; + break; + } + if (!budget_ci->ir.full_rc5) + dev->scancode_mask = 0xff; + + error = rc_register_device(dev); + if (error) { + printk(KERN_ERR "budget_ci: could not init driver for IR device (code %d)\n", error); + rc_free_device(dev); + return error; + } + + budget_ci->ir.dev = dev; + + tasklet_setup(&budget_ci->ir.msp430_irq_tasklet, msp430_ir_interrupt); + + SAA7146_IER_ENABLE(saa, MASK_06); + saa7146_setgpio(saa, 3, SAA7146_GPIO_IRQHI); + + return 0; +} + +static void msp430_ir_deinit(struct budget_ci *budget_ci) +{ + struct saa7146_dev *saa = budget_ci->budget.dev; + + SAA7146_IER_DISABLE(saa, MASK_06); + saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); + tasklet_kill(&budget_ci->ir.msp430_irq_tasklet); + + rc_unregister_device(budget_ci->ir.dev); +} + +static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address) +{ + struct budget_ci *budget_ci = (struct budget_ci *) ca->data; + + if (slot != 0) + return -EINVAL; + + return ttpci_budget_debiread(&budget_ci->budget, DEBICICAM, + DEBIADDR_ATTR | (address & 0xfff), 1, 1, 0); +} + +static int ciintf_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value) +{ + struct budget_ci *budget_ci = (struct budget_ci *) ca->data; + + if (slot != 0) + return -EINVAL; + + return ttpci_budget_debiwrite(&budget_ci->budget, DEBICICAM, + DEBIADDR_ATTR | (address & 0xfff), 1, value, 1, 0); +} + +static int ciintf_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address) +{ + struct budget_ci *budget_ci = (struct budget_ci *) ca->data; + + if (slot != 0) + return -EINVAL; + + return ttpci_budget_debiread(&budget_ci->budget, DEBICICAM, + DEBIADDR_IO | (address & 3), 1, 1, 0); +} + +static int ciintf_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value) +{ + struct budget_ci *budget_ci = (struct budget_ci *) ca->data; + + if (slot != 0) + return -EINVAL; + + return ttpci_budget_debiwrite(&budget_ci->budget, DEBICICAM, + DEBIADDR_IO | (address & 3), 1, value, 1, 0); +} + +static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot) +{ + struct budget_ci *budget_ci = (struct budget_ci *) ca->data; + struct saa7146_dev *saa = budget_ci->budget.dev; + + if (slot != 0) + return -EINVAL; + + if (budget_ci->ci_irq) { + // trigger on RISING edge during reset so we know when READY is re-asserted + saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI); + } + budget_ci->slot_status = SLOTSTATUS_RESET; + ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 0, 1, 0); + msleep(1); + ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, + CICONTROL_RESET, 1, 0); + + saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTHI); + ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); + return 0; +} + +static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) +{ + struct budget_ci *budget_ci = (struct budget_ci *) ca->data; + struct saa7146_dev *saa = budget_ci->budget.dev; + + if (slot != 0) + return -EINVAL; + + saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTHI); + ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); + return 0; +} + +static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) +{ + struct budget_ci *budget_ci = (struct budget_ci *) ca->data; + struct saa7146_dev *saa = budget_ci->budget.dev; + int tmp; + + if (slot != 0) + return -EINVAL; + + saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTLO); + + tmp = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); + ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, + tmp | CICONTROL_ENABLETS, 1, 0); + + ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTA); + return 0; +} + +static void ciintf_interrupt(struct tasklet_struct *t) +{ + struct budget_ci *budget_ci = from_tasklet(budget_ci, t, + ciintf_irq_tasklet); + struct saa7146_dev *saa = budget_ci->budget.dev; + unsigned int flags; + + // ensure we don't get spurious IRQs during initialisation + if (!budget_ci->budget.ci_present) + return; + + // read the CAM status + flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); + if (flags & CICONTROL_CAMDETECT) { + + // GPIO should be set to trigger on falling edge if a CAM is present + saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO); + + if (budget_ci->slot_status & SLOTSTATUS_NONE) { + // CAM insertion IRQ + budget_ci->slot_status = SLOTSTATUS_PRESENT; + dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, + DVB_CA_EN50221_CAMCHANGE_INSERTED); + + } else if (budget_ci->slot_status & SLOTSTATUS_RESET) { + // CAM ready (reset completed) + budget_ci->slot_status = SLOTSTATUS_READY; + dvb_ca_en50221_camready_irq(&budget_ci->ca, 0); + + } else if (budget_ci->slot_status & SLOTSTATUS_READY) { + // FR/DA IRQ + dvb_ca_en50221_frda_irq(&budget_ci->ca, 0); + } + } else { + + // trigger on rising edge if a CAM is not present - when a CAM is inserted, we + // only want to get the IRQ when it sets READY. If we trigger on the falling edge, + // the CAM might not actually be ready yet. + saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI); + + // generate a CAM removal IRQ if we haven't already + if (budget_ci->slot_status & SLOTSTATUS_OCCUPIED) { + // CAM removal IRQ + budget_ci->slot_status = SLOTSTATUS_NONE; + dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, + DVB_CA_EN50221_CAMCHANGE_REMOVED); + } + } +} + +static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) +{ + struct budget_ci *budget_ci = (struct budget_ci *) ca->data; + unsigned int flags; + + // ensure we don't get spurious IRQs during initialisation + if (!budget_ci->budget.ci_present) + return -EINVAL; + + // read the CAM status + flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); + if (flags & CICONTROL_CAMDETECT) { + // mark it as present if it wasn't before + if (budget_ci->slot_status & SLOTSTATUS_NONE) { + budget_ci->slot_status = SLOTSTATUS_PRESENT; + } + + // during a RESET, we check if we can read from IO memory to see when CAM is ready + if (budget_ci->slot_status & SLOTSTATUS_RESET) { + if (ciintf_read_attribute_mem(ca, slot, 0) == 0x1d) { + budget_ci->slot_status = SLOTSTATUS_READY; + } + } + } else { + budget_ci->slot_status = SLOTSTATUS_NONE; + } + + if (budget_ci->slot_status != SLOTSTATUS_NONE) { + if (budget_ci->slot_status & SLOTSTATUS_READY) { + return DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY; + } + return DVB_CA_EN50221_POLL_CAM_PRESENT; + } + + return 0; +} + +static int ciintf_init(struct budget_ci *budget_ci) +{ + struct saa7146_dev *saa = budget_ci->budget.dev; + int flags; + int result; + int ci_version; + int ca_flags; + + memset(&budget_ci->ca, 0, sizeof(struct dvb_ca_en50221)); + + // enable DEBI pins + saa7146_write(saa, MC1, MASK_27 | MASK_11); + + // test if it is there + ci_version = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CIVERSION, 1, 1, 0); + if ((ci_version & 0xa0) != 0xa0) { + result = -ENODEV; + goto error; + } + + // determine whether a CAM is present or not + flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); + budget_ci->slot_status = SLOTSTATUS_NONE; + if (flags & CICONTROL_CAMDETECT) + budget_ci->slot_status = SLOTSTATUS_PRESENT; + + // version 0xa2 of the CI firmware doesn't generate interrupts + if (ci_version == 0xa2) { + ca_flags = 0; + budget_ci->ci_irq = 0; + } else { + ca_flags = DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE | + DVB_CA_EN50221_FLAG_IRQ_FR | + DVB_CA_EN50221_FLAG_IRQ_DA; + budget_ci->ci_irq = 1; + } + + // register CI interface + budget_ci->ca.owner = THIS_MODULE; + budget_ci->ca.read_attribute_mem = ciintf_read_attribute_mem; + budget_ci->ca.write_attribute_mem = ciintf_write_attribute_mem; + budget_ci->ca.read_cam_control = ciintf_read_cam_control; + budget_ci->ca.write_cam_control = ciintf_write_cam_control; + budget_ci->ca.slot_reset = ciintf_slot_reset; + budget_ci->ca.slot_shutdown = ciintf_slot_shutdown; + budget_ci->ca.slot_ts_enable = ciintf_slot_ts_enable; + budget_ci->ca.poll_slot_status = ciintf_poll_slot_status; + budget_ci->ca.data = budget_ci; + if ((result = dvb_ca_en50221_init(&budget_ci->budget.dvb_adapter, + &budget_ci->ca, + ca_flags, 1)) != 0) { + printk("budget_ci: CI interface detected, but initialisation failed.\n"); + goto error; + } + + // Setup CI slot IRQ + if (budget_ci->ci_irq) { + tasklet_setup(&budget_ci->ciintf_irq_tasklet, ciintf_interrupt); + if (budget_ci->slot_status != SLOTSTATUS_NONE) { + saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO); + } else { + saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI); + } + SAA7146_IER_ENABLE(saa, MASK_03); + } + + // enable interface + ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, + CICONTROL_RESET, 1, 0); + + // success! + printk("budget_ci: CI interface initialised\n"); + budget_ci->budget.ci_present = 1; + + // forge a fake CI IRQ so the CAM state is setup correctly + if (budget_ci->ci_irq) { + flags = DVB_CA_EN50221_CAMCHANGE_REMOVED; + if (budget_ci->slot_status != SLOTSTATUS_NONE) + flags = DVB_CA_EN50221_CAMCHANGE_INSERTED; + dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, flags); + } + + return 0; + +error: + saa7146_write(saa, MC1, MASK_27); + return result; +} + +static void ciintf_deinit(struct budget_ci *budget_ci) +{ + struct saa7146_dev *saa = budget_ci->budget.dev; + + // disable CI interrupts + if (budget_ci->ci_irq) { + SAA7146_IER_DISABLE(saa, MASK_03); + saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT); + tasklet_kill(&budget_ci->ciintf_irq_tasklet); + } + + // reset interface + ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 0, 1, 0); + msleep(1); + ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, + CICONTROL_RESET, 1, 0); + + // disable TS data stream to CI interface + saa7146_setgpio(saa, 1, SAA7146_GPIO_INPUT); + + // release the CA device + dvb_ca_en50221_release(&budget_ci->ca); + + // disable DEBI pins + saa7146_write(saa, MC1, MASK_27); +} + +static void budget_ci_irq(struct saa7146_dev *dev, u32 * isr) +{ + struct budget_ci *budget_ci = (struct budget_ci *) dev->ext_priv; + + dprintk(8, "dev: %p, budget_ci: %p\n", dev, budget_ci); + + if (*isr & MASK_06) + tasklet_schedule(&budget_ci->ir.msp430_irq_tasklet); + + if (*isr & MASK_10) + ttpci_budget_irq10_handler(dev, isr); + + if ((*isr & MASK_03) && (budget_ci->budget.ci_present) && (budget_ci->ci_irq)) + tasklet_schedule(&budget_ci->ciintf_irq_tasklet); +} + +static u8 philips_su1278_tt_inittab[] = { + 0x01, 0x0f, + 0x02, 0x30, + 0x03, 0x00, + 0x04, 0x5b, + 0x05, 0x85, + 0x06, 0x02, + 0x07, 0x00, + 0x08, 0x02, + 0x09, 0x00, + 0x0C, 0x01, + 0x0D, 0x81, + 0x0E, 0x44, + 0x0f, 0x14, + 0x10, 0x3c, + 0x11, 0x84, + 0x12, 0xda, + 0x13, 0x97, + 0x14, 0x95, + 0x15, 0xc9, + 0x16, 0x19, + 0x17, 0x8c, + 0x18, 0x59, + 0x19, 0xf8, + 0x1a, 0xfe, + 0x1c, 0x7f, + 0x1d, 0x00, + 0x1e, 0x00, + 0x1f, 0x50, + 0x20, 0x00, + 0x21, 0x00, + 0x22, 0x00, + 0x23, 0x00, + 0x28, 0x00, + 0x29, 0x28, + 0x2a, 0x14, + 0x2b, 0x0f, + 0x2c, 0x09, + 0x2d, 0x09, + 0x31, 0x1f, + 0x32, 0x19, + 0x33, 0xfc, + 0x34, 0x93, + 0xff, 0xff +}; + +static int philips_su1278_tt_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio) +{ + stv0299_writereg(fe, 0x0e, 0x44); + if (srate >= 10000000) { + stv0299_writereg(fe, 0x13, 0x97); + stv0299_writereg(fe, 0x14, 0x95); + stv0299_writereg(fe, 0x15, 0xc9); + stv0299_writereg(fe, 0x17, 0x8c); + stv0299_writereg(fe, 0x1a, 0xfe); + stv0299_writereg(fe, 0x1c, 0x7f); + stv0299_writereg(fe, 0x2d, 0x09); + } else { + stv0299_writereg(fe, 0x13, 0x99); + stv0299_writereg(fe, 0x14, 0x8d); + stv0299_writereg(fe, 0x15, 0xce); + stv0299_writereg(fe, 0x17, 0x43); + stv0299_writereg(fe, 0x1a, 0x1d); + stv0299_writereg(fe, 0x1c, 0x12); + stv0299_writereg(fe, 0x2d, 0x05); + } + stv0299_writereg(fe, 0x0e, 0x23); + stv0299_writereg(fe, 0x0f, 0x94); + stv0299_writereg(fe, 0x10, 0x39); + stv0299_writereg(fe, 0x15, 0xc9); + + stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); + stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); + stv0299_writereg(fe, 0x21, (ratio) & 0xf0); + + return 0; +} + +static int philips_su1278_tt_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; + u32 div; + u8 buf[4]; + struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) }; + + if ((p->frequency < 950000) || (p->frequency > 2150000)) + return -EINVAL; + + div = (p->frequency + (500 - 1)) / 500; /* round correctly */ + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = 0x80 | ((div & 0x18000) >> 10) | 2; + buf[3] = 0x20; + + if (p->symbol_rate < 4000000) + buf[3] |= 1; + + if (p->frequency < 1250000) + buf[3] |= 0; + else if (p->frequency < 1550000) + buf[3] |= 0x40; + else if (p->frequency < 2050000) + buf[3] |= 0x80; + else if (p->frequency < 2150000) + buf[3] |= 0xC0; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget_ci->budget.i2c_adap, &msg, 1) != 1) + return -EIO; + return 0; +} + +static const struct stv0299_config philips_su1278_tt_config = { + + .demod_address = 0x68, + .inittab = philips_su1278_tt_inittab, + .mclk = 64000000UL, + .invert = 0, + .skip_reinit = 1, + .lock_output = STV0299_LOCKOUTPUT_1, + .volt13_op0_op1 = STV0299_VOLT13_OP1, + .min_delay_ms = 50, + .set_symbol_rate = philips_su1278_tt_set_symbol_rate, +}; + + + +static int philips_tdm1316l_tuner_init(struct dvb_frontend *fe) +{ + struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; + static u8 td1316_init[] = { 0x0b, 0xf5, 0x85, 0xab }; + static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 }; + struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address,.flags = 0,.buf = td1316_init,.len = + sizeof(td1316_init) }; + + // setup PLL configuration + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) + return -EIO; + msleep(1); + + // disable the mc44BC374c (do not check for errors) + tuner_msg.addr = 0x65; + tuner_msg.buf = disable_mc44BC374c; + tuner_msg.len = sizeof(disable_mc44BC374c); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1); + } + + return 0; +} + +static int philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; + u8 tuner_buf[4]; + struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address,.flags = 0,.buf = tuner_buf,.len = sizeof(tuner_buf) }; + int tuner_frequency = 0; + u8 band, cp, filter; + + // determine charge pump + tuner_frequency = p->frequency + 36130000; + if (tuner_frequency < 87000000) + return -EINVAL; + else if (tuner_frequency < 130000000) + cp = 3; + else if (tuner_frequency < 160000000) + cp = 5; + else if (tuner_frequency < 200000000) + cp = 6; + else if (tuner_frequency < 290000000) + cp = 3; + else if (tuner_frequency < 420000000) + cp = 5; + else if (tuner_frequency < 480000000) + cp = 6; + else if (tuner_frequency < 620000000) + cp = 3; + else if (tuner_frequency < 830000000) + cp = 5; + else if (tuner_frequency < 895000000) + cp = 7; + else + return -EINVAL; + + // determine band + if (p->frequency < 49000000) + return -EINVAL; + else if (p->frequency < 159000000) + band = 1; + else if (p->frequency < 444000000) + band = 2; + else if (p->frequency < 861000000) + band = 4; + else + return -EINVAL; + + // setup PLL filter and TDA9889 + switch (p->bandwidth_hz) { + case 6000000: + tda1004x_writereg(fe, 0x0C, 0x14); + filter = 0; + break; + + case 7000000: + tda1004x_writereg(fe, 0x0C, 0x80); + filter = 0; + break; + + case 8000000: + tda1004x_writereg(fe, 0x0C, 0x14); + filter = 1; + break; + + default: + return -EINVAL; + } + + // calculate divisor + // ((36130000+((1000000/6)/2)) + Finput)/(1000000/6) + tuner_frequency = (((p->frequency / 1000) * 6) + 217280) / 1000; + + // setup tuner buffer + tuner_buf[0] = tuner_frequency >> 8; + tuner_buf[1] = tuner_frequency & 0xff; + tuner_buf[2] = 0xca; + tuner_buf[3] = (cp << 5) | (filter << 3) | band; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) + return -EIO; + + msleep(1); + return 0; +} + +static int philips_tdm1316l_request_firmware(struct dvb_frontend *fe, + const struct firmware **fw, char *name) +{ + struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; + + return request_firmware(fw, name, &budget_ci->budget.dev->pci->dev); +} + +static struct tda1004x_config philips_tdm1316l_config = { + + .demod_address = 0x8, + .invert = 0, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_4M, + .agc_config = TDA10046_AGC_DEFAULT, + .if_freq = TDA10046_FREQ_3617, + .request_firmware = philips_tdm1316l_request_firmware, +}; + +static struct tda1004x_config philips_tdm1316l_config_invert = { + + .demod_address = 0x8, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_4M, + .agc_config = TDA10046_AGC_DEFAULT, + .if_freq = TDA10046_FREQ_3617, + .request_firmware = philips_tdm1316l_request_firmware, +}; + +static int dvbc_philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; + u8 tuner_buf[5]; + struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address, + .flags = 0, + .buf = tuner_buf, + .len = sizeof(tuner_buf) }; + int tuner_frequency = 0; + u8 band, cp, filter; + + // determine charge pump + tuner_frequency = p->frequency + 36125000; + if (tuner_frequency < 87000000) + return -EINVAL; + else if (tuner_frequency < 130000000) { + cp = 3; + band = 1; + } else if (tuner_frequency < 160000000) { + cp = 5; + band = 1; + } else if (tuner_frequency < 200000000) { + cp = 6; + band = 1; + } else if (tuner_frequency < 290000000) { + cp = 3; + band = 2; + } else if (tuner_frequency < 420000000) { + cp = 5; + band = 2; + } else if (tuner_frequency < 480000000) { + cp = 6; + band = 2; + } else if (tuner_frequency < 620000000) { + cp = 3; + band = 4; + } else if (tuner_frequency < 830000000) { + cp = 5; + band = 4; + } else if (tuner_frequency < 895000000) { + cp = 7; + band = 4; + } else + return -EINVAL; + + // assume PLL filter should always be 8MHz for the moment. + filter = 1; + + // calculate divisor + tuner_frequency = (p->frequency + 36125000 + (62500/2)) / 62500; + + // setup tuner buffer + tuner_buf[0] = tuner_frequency >> 8; + tuner_buf[1] = tuner_frequency & 0xff; + tuner_buf[2] = 0xc8; + tuner_buf[3] = (cp << 5) | (filter << 3) | band; + tuner_buf[4] = 0x80; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) + return -EIO; + + msleep(50); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) + return -EIO; + + msleep(1); + + return 0; +} + +static u8 dvbc_philips_tdm1316l_inittab[] = { + 0x80, 0x01, + 0x80, 0x00, + 0x81, 0x01, + 0x81, 0x00, + 0x00, 0x09, + 0x01, 0x69, + 0x03, 0x00, + 0x04, 0x00, + 0x07, 0x00, + 0x08, 0x00, + 0x20, 0x00, + 0x21, 0x40, + 0x22, 0x00, + 0x23, 0x00, + 0x24, 0x40, + 0x25, 0x88, + 0x30, 0xff, + 0x31, 0x00, + 0x32, 0xff, + 0x33, 0x00, + 0x34, 0x50, + 0x35, 0x7f, + 0x36, 0x00, + 0x37, 0x20, + 0x38, 0x00, + 0x40, 0x1c, + 0x41, 0xff, + 0x42, 0x29, + 0x43, 0x20, + 0x44, 0xff, + 0x45, 0x00, + 0x46, 0x00, + 0x49, 0x04, + 0x4a, 0x00, + 0x4b, 0x7b, + 0x52, 0x30, + 0x55, 0xae, + 0x56, 0x47, + 0x57, 0xe1, + 0x58, 0x3a, + 0x5a, 0x1e, + 0x5b, 0x34, + 0x60, 0x00, + 0x63, 0x00, + 0x64, 0x00, + 0x65, 0x00, + 0x66, 0x00, + 0x67, 0x00, + 0x68, 0x00, + 0x69, 0x00, + 0x6a, 0x02, + 0x6b, 0x00, + 0x70, 0xff, + 0x71, 0x00, + 0x72, 0x00, + 0x73, 0x00, + 0x74, 0x0c, + 0x80, 0x00, + 0x81, 0x00, + 0x82, 0x00, + 0x83, 0x00, + 0x84, 0x04, + 0x85, 0x80, + 0x86, 0x24, + 0x87, 0x78, + 0x88, 0x10, + 0x89, 0x00, + 0x90, 0x01, + 0x91, 0x01, + 0xa0, 0x04, + 0xa1, 0x00, + 0xa2, 0x00, + 0xb0, 0x91, + 0xb1, 0x0b, + 0xc0, 0x53, + 0xc1, 0x70, + 0xc2, 0x12, + 0xd0, 0x00, + 0xd1, 0x00, + 0xd2, 0x00, + 0xd3, 0x00, + 0xd4, 0x00, + 0xd5, 0x00, + 0xde, 0x00, + 0xdf, 0x00, + 0x61, 0x38, + 0x62, 0x0a, + 0x53, 0x13, + 0x59, 0x08, + 0xff, 0xff, +}; + +static struct stv0297_config dvbc_philips_tdm1316l_config = { + .demod_address = 0x1c, + .inittab = dvbc_philips_tdm1316l_inittab, + .invert = 0, + .stop_during_read = 1, +}; + +static struct tda10023_config tda10023_config = { + .demod_address = 0xc, + .invert = 0, + .xtal = 16000000, + .pll_m = 11, + .pll_p = 3, + .pll_n = 1, + .deltaf = 0xa511, +}; + +static struct tda827x_config tda827x_config = { + .config = 0, +}; + +/* TT S2-3200 DVB-S (STB0899) Inittab */ +static const struct stb0899_s1_reg tt3200_stb0899_s1_init_1[] = { + + { STB0899_DEV_ID , 0x81 }, + { STB0899_DISCNTRL1 , 0x32 }, + { STB0899_DISCNTRL2 , 0x80 }, + { STB0899_DISRX_ST0 , 0x04 }, + { STB0899_DISRX_ST1 , 0x00 }, + { STB0899_DISPARITY , 0x00 }, + { STB0899_DISSTATUS , 0x20 }, + { STB0899_DISF22 , 0x8c }, + { STB0899_DISF22RX , 0x9a }, + { STB0899_SYSREG , 0x0b }, + { STB0899_ACRPRESC , 0x11 }, + { STB0899_ACRDIV1 , 0x0a }, + { STB0899_ACRDIV2 , 0x05 }, + { STB0899_DACR1 , 0x00 }, + { STB0899_DACR2 , 0x00 }, + { STB0899_OUTCFG , 0x00 }, + { STB0899_MODECFG , 0x00 }, + { STB0899_IRQSTATUS_3 , 0x30 }, + { STB0899_IRQSTATUS_2 , 0x00 }, + { STB0899_IRQSTATUS_1 , 0x00 }, + { STB0899_IRQSTATUS_0 , 0x00 }, + { STB0899_IRQMSK_3 , 0xf3 }, + { STB0899_IRQMSK_2 , 0xfc }, + { STB0899_IRQMSK_1 , 0xff }, + { STB0899_IRQMSK_0 , 0xff }, + { STB0899_IRQCFG , 0x00 }, + { STB0899_I2CCFG , 0x88 }, + { STB0899_I2CRPT , 0x48 }, /* 12k Pullup, Repeater=16, Stop=disabled */ + { STB0899_IOPVALUE5 , 0x00 }, + { STB0899_IOPVALUE4 , 0x20 }, + { STB0899_IOPVALUE3 , 0xc9 }, + { STB0899_IOPVALUE2 , 0x90 }, + { STB0899_IOPVALUE1 , 0x40 }, + { STB0899_IOPVALUE0 , 0x00 }, + { STB0899_GPIO00CFG , 0x82 }, + { STB0899_GPIO01CFG , 0x82 }, + { STB0899_GPIO02CFG , 0x82 }, + { STB0899_GPIO03CFG , 0x82 }, + { STB0899_GPIO04CFG , 0x82 }, + { STB0899_GPIO05CFG , 0x82 }, + { STB0899_GPIO06CFG , 0x82 }, + { STB0899_GPIO07CFG , 0x82 }, + { STB0899_GPIO08CFG , 0x82 }, + { STB0899_GPIO09CFG , 0x82 }, + { STB0899_GPIO10CFG , 0x82 }, + { STB0899_GPIO11CFG , 0x82 }, + { STB0899_GPIO12CFG , 0x82 }, + { STB0899_GPIO13CFG , 0x82 }, + { STB0899_GPIO14CFG , 0x82 }, + { STB0899_GPIO15CFG , 0x82 }, + { STB0899_GPIO16CFG , 0x82 }, + { STB0899_GPIO17CFG , 0x82 }, + { STB0899_GPIO18CFG , 0x82 }, + { STB0899_GPIO19CFG , 0x82 }, + { STB0899_GPIO20CFG , 0x82 }, + { STB0899_SDATCFG , 0xb8 }, + { STB0899_SCLTCFG , 0xba }, + { STB0899_AGCRFCFG , 0x1c }, /* 0x11 */ + { STB0899_GPIO22 , 0x82 }, /* AGCBB2CFG */ + { STB0899_GPIO21 , 0x91 }, /* AGCBB1CFG */ + { STB0899_DIRCLKCFG , 0x82 }, + { STB0899_CLKOUT27CFG , 0x7e }, + { STB0899_STDBYCFG , 0x82 }, + { STB0899_CS0CFG , 0x82 }, + { STB0899_CS1CFG , 0x82 }, + { STB0899_DISEQCOCFG , 0x20 }, + { STB0899_GPIO32CFG , 0x82 }, + { STB0899_GPIO33CFG , 0x82 }, + { STB0899_GPIO34CFG , 0x82 }, + { STB0899_GPIO35CFG , 0x82 }, + { STB0899_GPIO36CFG , 0x82 }, + { STB0899_GPIO37CFG , 0x82 }, + { STB0899_GPIO38CFG , 0x82 }, + { STB0899_GPIO39CFG , 0x82 }, + { STB0899_NCOARSE , 0x15 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */ + { STB0899_SYNTCTRL , 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */ + { STB0899_FILTCTRL , 0x00 }, + { STB0899_SYSCTRL , 0x00 }, + { STB0899_STOPCLK1 , 0x20 }, + { STB0899_STOPCLK2 , 0x00 }, + { STB0899_INTBUFSTATUS , 0x00 }, + { STB0899_INTBUFCTRL , 0x0a }, + { 0xffff , 0xff }, +}; + +static const struct stb0899_s1_reg tt3200_stb0899_s1_init_3[] = { + { STB0899_DEMOD , 0x00 }, + { STB0899_RCOMPC , 0xc9 }, + { STB0899_AGC1CN , 0x41 }, + { STB0899_AGC1REF , 0x10 }, + { STB0899_RTC , 0x7a }, + { STB0899_TMGCFG , 0x4e }, + { STB0899_AGC2REF , 0x34 }, + { STB0899_TLSR , 0x84 }, + { STB0899_CFD , 0xc7 }, + { STB0899_ACLC , 0x87 }, + { STB0899_BCLC , 0x94 }, + { STB0899_EQON , 0x41 }, + { STB0899_LDT , 0xdd }, + { STB0899_LDT2 , 0xc9 }, + { STB0899_EQUALREF , 0xb4 }, + { STB0899_TMGRAMP , 0x10 }, + { STB0899_TMGTHD , 0x30 }, + { STB0899_IDCCOMP , 0xfb }, + { STB0899_QDCCOMP , 0x03 }, + { STB0899_POWERI , 0x3b }, + { STB0899_POWERQ , 0x3d }, + { STB0899_RCOMP , 0x81 }, + { STB0899_AGCIQIN , 0x80 }, + { STB0899_AGC2I1 , 0x04 }, + { STB0899_AGC2I2 , 0xf5 }, + { STB0899_TLIR , 0x25 }, + { STB0899_RTF , 0x80 }, + { STB0899_DSTATUS , 0x00 }, + { STB0899_LDI , 0xca }, + { STB0899_CFRM , 0xf1 }, + { STB0899_CFRL , 0xf3 }, + { STB0899_NIRM , 0x2a }, + { STB0899_NIRL , 0x05 }, + { STB0899_ISYMB , 0x17 }, + { STB0899_QSYMB , 0xfa }, + { STB0899_SFRH , 0x2f }, + { STB0899_SFRM , 0x68 }, + { STB0899_SFRL , 0x40 }, + { STB0899_SFRUPH , 0x2f }, + { STB0899_SFRUPM , 0x68 }, + { STB0899_SFRUPL , 0x40 }, + { STB0899_EQUAI1 , 0xfd }, + { STB0899_EQUAQ1 , 0x04 }, + { STB0899_EQUAI2 , 0x0f }, + { STB0899_EQUAQ2 , 0xff }, + { STB0899_EQUAI3 , 0xdf }, + { STB0899_EQUAQ3 , 0xfa }, + { STB0899_EQUAI4 , 0x37 }, + { STB0899_EQUAQ4 , 0x0d }, + { STB0899_EQUAI5 , 0xbd }, + { STB0899_EQUAQ5 , 0xf7 }, + { STB0899_DSTATUS2 , 0x00 }, + { STB0899_VSTATUS , 0x00 }, + { STB0899_VERROR , 0xff }, + { STB0899_IQSWAP , 0x2a }, + { STB0899_ECNT1M , 0x00 }, + { STB0899_ECNT1L , 0x00 }, + { STB0899_ECNT2M , 0x00 }, + { STB0899_ECNT2L , 0x00 }, + { STB0899_ECNT3M , 0x00 }, + { STB0899_ECNT3L , 0x00 }, + { STB0899_FECAUTO1 , 0x06 }, + { STB0899_FECM , 0x01 }, + { STB0899_VTH12 , 0xf0 }, + { STB0899_VTH23 , 0xa0 }, + { STB0899_VTH34 , 0x78 }, + { STB0899_VTH56 , 0x4e }, + { STB0899_VTH67 , 0x48 }, + { STB0899_VTH78 , 0x38 }, + { STB0899_PRVIT , 0xff }, + { STB0899_VITSYNC , 0x19 }, + { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */ + { STB0899_TSULC , 0x42 }, + { STB0899_RSLLC , 0x40 }, + { STB0899_TSLPL , 0x12 }, + { STB0899_TSCFGH , 0x0c }, + { STB0899_TSCFGM , 0x00 }, + { STB0899_TSCFGL , 0x0c }, + { STB0899_TSOUT , 0x4d }, /* 0x0d for CAM */ + { STB0899_RSSYNCDEL , 0x00 }, + { STB0899_TSINHDELH , 0x02 }, + { STB0899_TSINHDELM , 0x00 }, + { STB0899_TSINHDELL , 0x00 }, + { STB0899_TSLLSTKM , 0x00 }, + { STB0899_TSLLSTKL , 0x00 }, + { STB0899_TSULSTKM , 0x00 }, + { STB0899_TSULSTKL , 0xab }, + { STB0899_PCKLENUL , 0x00 }, + { STB0899_PCKLENLL , 0xcc }, + { STB0899_RSPCKLEN , 0xcc }, + { STB0899_TSSTATUS , 0x80 }, + { STB0899_ERRCTRL1 , 0xb6 }, + { STB0899_ERRCTRL2 , 0x96 }, + { STB0899_ERRCTRL3 , 0x89 }, + { STB0899_DMONMSK1 , 0x27 }, + { STB0899_DMONMSK0 , 0x03 }, + { STB0899_DEMAPVIT , 0x5c }, + { STB0899_PLPARM , 0x1f }, + { STB0899_PDELCTRL , 0x48 }, + { STB0899_PDELCTRL2 , 0x00 }, + { STB0899_BBHCTRL1 , 0x00 }, + { STB0899_BBHCTRL2 , 0x00 }, + { STB0899_HYSTTHRESH , 0x77 }, + { STB0899_MATCSTM , 0x00 }, + { STB0899_MATCSTL , 0x00 }, + { STB0899_UPLCSTM , 0x00 }, + { STB0899_UPLCSTL , 0x00 }, + { STB0899_DFLCSTM , 0x00 }, + { STB0899_DFLCSTL , 0x00 }, + { STB0899_SYNCCST , 0x00 }, + { STB0899_SYNCDCSTM , 0x00 }, + { STB0899_SYNCDCSTL , 0x00 }, + { STB0899_ISI_ENTRY , 0x00 }, + { STB0899_ISI_BIT_EN , 0x00 }, + { STB0899_MATSTRM , 0x00 }, + { STB0899_MATSTRL , 0x00 }, + { STB0899_UPLSTRM , 0x00 }, + { STB0899_UPLSTRL , 0x00 }, + { STB0899_DFLSTRM , 0x00 }, + { STB0899_DFLSTRL , 0x00 }, + { STB0899_SYNCSTR , 0x00 }, + { STB0899_SYNCDSTRM , 0x00 }, + { STB0899_SYNCDSTRL , 0x00 }, + { STB0899_CFGPDELSTATUS1 , 0x10 }, + { STB0899_CFGPDELSTATUS2 , 0x00 }, + { STB0899_BBFERRORM , 0x00 }, + { STB0899_BBFERRORL , 0x00 }, + { STB0899_UPKTERRORM , 0x00 }, + { STB0899_UPKTERRORL , 0x00 }, + { 0xffff , 0xff }, +}; + +static struct stb0899_config tt3200_config = { + .init_dev = tt3200_stb0899_s1_init_1, + .init_s2_demod = stb0899_s2_init_2, + .init_s1_demod = tt3200_stb0899_s1_init_3, + .init_s2_fec = stb0899_s2_init_4, + .init_tst = stb0899_s1_init_5, + + .postproc = NULL, + + .demod_address = 0x68, + + .xtal_freq = 27000000, + .inversion = IQ_SWAP_ON, + + .lo_clk = 76500000, + .hi_clk = 99000000, + + .esno_ave = STB0899_DVBS2_ESNO_AVE, + .esno_quant = STB0899_DVBS2_ESNO_QUANT, + .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE, + .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE, + .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD, + .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ, + .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK, + .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF, + .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT, + + .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS, + .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET, + .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS, + .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER, + + .tuner_get_frequency = stb6100_get_frequency, + .tuner_set_frequency = stb6100_set_frequency, + .tuner_set_bandwidth = stb6100_set_bandwidth, + .tuner_get_bandwidth = stb6100_get_bandwidth, + .tuner_set_rfsiggain = NULL +}; + +static struct stb6100_config tt3200_stb6100_config = { + .tuner_address = 0x60, + .refclock = 27000000, +}; + +static void frontend_init(struct budget_ci *budget_ci) +{ + switch (budget_ci->budget.dev->pci->subsystem_device) { + case 0x100c: // Hauppauge/TT Nova-CI budget (stv0299/ALPS BSRU6(tsa5059)) + budget_ci->budget.dvb_frontend = + dvb_attach(stv0299_attach, &alps_bsru6_config, &budget_ci->budget.i2c_adap); + if (budget_ci->budget.dvb_frontend) { + budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; + budget_ci->budget.dvb_frontend->tuner_priv = &budget_ci->budget.i2c_adap; + break; + } + break; + + case 0x100f: // Hauppauge/TT Nova-CI budget (stv0299b/Philips su1278(tsa5059)) + budget_ci->budget.dvb_frontend = + dvb_attach(stv0299_attach, &philips_su1278_tt_config, &budget_ci->budget.i2c_adap); + if (budget_ci->budget.dvb_frontend) { + budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_su1278_tt_tuner_set_params; + break; + } + break; + + case 0x1010: // TT DVB-C CI budget (stv0297/Philips tdm1316l(tda6651tt)) + budget_ci->tuner_pll_address = 0x61; + budget_ci->budget.dvb_frontend = + dvb_attach(stv0297_attach, &dvbc_philips_tdm1316l_config, &budget_ci->budget.i2c_adap); + if (budget_ci->budget.dvb_frontend) { + budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = dvbc_philips_tdm1316l_tuner_set_params; + break; + } + break; + + case 0x1011: // Hauppauge/TT Nova-T budget (tda10045/Philips tdm1316l(tda6651tt) + TDA9889) + budget_ci->tuner_pll_address = 0x63; + budget_ci->budget.dvb_frontend = + dvb_attach(tda10045_attach, &philips_tdm1316l_config, &budget_ci->budget.i2c_adap); + if (budget_ci->budget.dvb_frontend) { + budget_ci->budget.dvb_frontend->ops.tuner_ops.init = philips_tdm1316l_tuner_init; + budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_tdm1316l_tuner_set_params; + break; + } + break; + + case 0x1012: // TT DVB-T CI budget (tda10046/Philips tdm1316l(tda6651tt)) + budget_ci->tuner_pll_address = 0x60; + budget_ci->budget.dvb_frontend = + dvb_attach(tda10046_attach, &philips_tdm1316l_config_invert, &budget_ci->budget.i2c_adap); + if (budget_ci->budget.dvb_frontend) { + budget_ci->budget.dvb_frontend->ops.tuner_ops.init = philips_tdm1316l_tuner_init; + budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_tdm1316l_tuner_set_params; + break; + } + break; + + case 0x1017: // TT S-1500 PCI + budget_ci->budget.dvb_frontend = dvb_attach(stv0299_attach, &alps_bsbe1_config, &budget_ci->budget.i2c_adap); + if (budget_ci->budget.dvb_frontend) { + budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params; + budget_ci->budget.dvb_frontend->tuner_priv = &budget_ci->budget.i2c_adap; + + budget_ci->budget.dvb_frontend->ops.dishnetwork_send_legacy_command = NULL; + if (dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, LNBP21_LLC, 0) == NULL) { + printk("%s: No LNBP21 found!\n", __func__); + dvb_frontend_detach(budget_ci->budget.dvb_frontend); + budget_ci->budget.dvb_frontend = NULL; + } + } + break; + + case 0x101a: /* TT Budget-C-1501 (philips tda10023/philips tda8274A) */ + budget_ci->budget.dvb_frontend = dvb_attach(tda10023_attach, &tda10023_config, &budget_ci->budget.i2c_adap, 0x48); + if (budget_ci->budget.dvb_frontend) { + if (dvb_attach(tda827x_attach, budget_ci->budget.dvb_frontend, 0x61, &budget_ci->budget.i2c_adap, &tda827x_config) == NULL) { + printk(KERN_ERR "%s: No tda827x found!\n", __func__); + dvb_frontend_detach(budget_ci->budget.dvb_frontend); + budget_ci->budget.dvb_frontend = NULL; + } + } + break; + + case 0x101b: /* TT S-1500B (BSBE1-D01A - STV0288/STB6000/LNBP21) */ + budget_ci->budget.dvb_frontend = dvb_attach(stv0288_attach, &stv0288_bsbe1_d01a_config, &budget_ci->budget.i2c_adap); + if (budget_ci->budget.dvb_frontend) { + if (dvb_attach(stb6000_attach, budget_ci->budget.dvb_frontend, 0x63, &budget_ci->budget.i2c_adap)) { + if (!dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, 0, 0)) { + printk(KERN_ERR "%s: No LNBP21 found!\n", __func__); + dvb_frontend_detach(budget_ci->budget.dvb_frontend); + budget_ci->budget.dvb_frontend = NULL; + } + } else { + printk(KERN_ERR "%s: No STB6000 found!\n", __func__); + dvb_frontend_detach(budget_ci->budget.dvb_frontend); + budget_ci->budget.dvb_frontend = NULL; + } + } + break; + + case 0x1019: // TT S2-3200 PCI + /* + * NOTE! on some STB0899 versions, the internal PLL takes a longer time + * to settle, aka LOCK. On the older revisions of the chip, we don't see + * this, as a result on the newer chips the entire clock tree, will not + * be stable after a freshly POWER 'ed up situation. + * In this case, we should RESET the STB0899 (Active LOW) and wait for + * PLL stabilization. + * + * On the TT S2 3200 and clones, the STB0899 demodulator's RESETB is + * connected to the SAA7146 GPIO, GPIO2, Pin 142 + */ + /* Reset Demodulator */ + saa7146_setgpio(budget_ci->budget.dev, 2, SAA7146_GPIO_OUTLO); + /* Wait for everything to die */ + msleep(50); + /* Pull it up out of Reset state */ + saa7146_setgpio(budget_ci->budget.dev, 2, SAA7146_GPIO_OUTHI); + /* Wait for PLL to stabilize */ + msleep(250); + /* + * PLL state should be stable now. Ideally, we should check + * for PLL LOCK status. But well, never mind! + */ + budget_ci->budget.dvb_frontend = dvb_attach(stb0899_attach, &tt3200_config, &budget_ci->budget.i2c_adap); + if (budget_ci->budget.dvb_frontend) { + if (dvb_attach(stb6100_attach, budget_ci->budget.dvb_frontend, &tt3200_stb6100_config, &budget_ci->budget.i2c_adap)) { + if (!dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, 0, 0)) { + printk("%s: No LNBP21 found!\n", __func__); + dvb_frontend_detach(budget_ci->budget.dvb_frontend); + budget_ci->budget.dvb_frontend = NULL; + } + } else { + dvb_frontend_detach(budget_ci->budget.dvb_frontend); + budget_ci->budget.dvb_frontend = NULL; + } + } + break; + + } + + if (budget_ci->budget.dvb_frontend == NULL) { + printk("budget-ci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", + budget_ci->budget.dev->pci->vendor, + budget_ci->budget.dev->pci->device, + budget_ci->budget.dev->pci->subsystem_vendor, + budget_ci->budget.dev->pci->subsystem_device); + } else { + if (dvb_register_frontend + (&budget_ci->budget.dvb_adapter, budget_ci->budget.dvb_frontend)) { + printk("budget-ci: Frontend registration failed!\n"); + dvb_frontend_detach(budget_ci->budget.dvb_frontend); + budget_ci->budget.dvb_frontend = NULL; + } + } +} + +static int budget_ci_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) +{ + struct budget_ci *budget_ci; + int err; + + budget_ci = kzalloc(sizeof(struct budget_ci), GFP_KERNEL); + if (!budget_ci) { + err = -ENOMEM; + goto out1; + } + + dprintk(2, "budget_ci: %p\n", budget_ci); + + dev->ext_priv = budget_ci; + + err = ttpci_budget_init(&budget_ci->budget, dev, info, THIS_MODULE, + adapter_nr); + if (err) + goto out2; + + err = msp430_ir_init(budget_ci); + if (err) + goto out3; + + ciintf_init(budget_ci); + + budget_ci->budget.dvb_adapter.priv = budget_ci; + frontend_init(budget_ci); + + ttpci_budget_init_hooks(&budget_ci->budget); + + return 0; + +out3: + ttpci_budget_deinit(&budget_ci->budget); +out2: + kfree(budget_ci); +out1: + return err; +} + +static int budget_ci_detach(struct saa7146_dev *dev) +{ + struct budget_ci *budget_ci = (struct budget_ci *) dev->ext_priv; + struct saa7146_dev *saa = budget_ci->budget.dev; + int err; + + if (budget_ci->budget.ci_present) + ciintf_deinit(budget_ci); + msp430_ir_deinit(budget_ci); + if (budget_ci->budget.dvb_frontend) { + dvb_unregister_frontend(budget_ci->budget.dvb_frontend); + dvb_frontend_detach(budget_ci->budget.dvb_frontend); + } + err = ttpci_budget_deinit(&budget_ci->budget); + + // disable frontend and CI interface + saa7146_setgpio(saa, 2, SAA7146_GPIO_INPUT); + + kfree(budget_ci); + + return err; +} + +static struct saa7146_extension budget_extension; + +MAKE_BUDGET_INFO(ttbs2, "TT-Budget/S-1500 PCI", BUDGET_TT); +MAKE_BUDGET_INFO(ttbci, "TT-Budget/WinTV-NOVA-CI PCI", BUDGET_TT_HW_DISEQC); +MAKE_BUDGET_INFO(ttbt2, "TT-Budget/WinTV-NOVA-T PCI", BUDGET_TT); +MAKE_BUDGET_INFO(ttbtci, "TT-Budget-T-CI PCI", BUDGET_TT); +MAKE_BUDGET_INFO(ttbcci, "TT-Budget-C-CI PCI", BUDGET_TT); +MAKE_BUDGET_INFO(ttc1501, "TT-Budget C-1501 PCI", BUDGET_TT); +MAKE_BUDGET_INFO(tt3200, "TT-Budget S2-3200 PCI", BUDGET_TT); +MAKE_BUDGET_INFO(ttbs1500b, "TT-Budget S-1500B PCI", BUDGET_TT); + +static const struct pci_device_id pci_tbl[] = { + MAKE_EXTENSION_PCI(ttbci, 0x13c2, 0x100c), + MAKE_EXTENSION_PCI(ttbci, 0x13c2, 0x100f), + MAKE_EXTENSION_PCI(ttbcci, 0x13c2, 0x1010), + MAKE_EXTENSION_PCI(ttbt2, 0x13c2, 0x1011), + MAKE_EXTENSION_PCI(ttbtci, 0x13c2, 0x1012), + MAKE_EXTENSION_PCI(ttbs2, 0x13c2, 0x1017), + MAKE_EXTENSION_PCI(ttc1501, 0x13c2, 0x101a), + MAKE_EXTENSION_PCI(tt3200, 0x13c2, 0x1019), + MAKE_EXTENSION_PCI(ttbs1500b, 0x13c2, 0x101b), + { + .vendor = 0, + } +}; + +MODULE_DEVICE_TABLE(pci, pci_tbl); + +static struct saa7146_extension budget_extension = { + .name = "budget_ci dvb", + .flags = SAA7146_USE_I2C_IRQ, + + .module = THIS_MODULE, + .pci_tbl = &pci_tbl[0], + .attach = budget_ci_attach, + .detach = budget_ci_detach, + + .irq_mask = MASK_03 | MASK_06 | MASK_10, + .irq_func = budget_ci_irq, +}; + +static int __init budget_ci_init(void) +{ + return saa7146_register_extension(&budget_extension); +} + +static void __exit budget_ci_exit(void) +{ + saa7146_unregister_extension(&budget_extension); +} + +module_init(budget_ci_init); +module_exit(budget_ci_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michael Hunold, Jack Thomasson, Andrew de Quincey, others"); +MODULE_DESCRIPTION("driver for the SAA7146 based so-called budget PCI DVB cards w/ CI-module produced by Siemens, Technotrend, Hauppauge"); diff --git a/drivers/staging/media/deprecated/saa7146/ttpci/budget-core.c b/drivers/staging/media/deprecated/saa7146/ttpci/budget-core.c new file mode 100644 index 000000000000..5d5796f24469 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/ttpci/budget-core.c @@ -0,0 +1,603 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * budget-core.c: driver for the SAA7146 based Budget DVB cards + * + * Compiled from various sources by Michael Hunold <michael@mihu.de> + * + * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de> + * + * Copyright (C) 1999-2002 Ralph Metzler + * & Marcus Metzler for convergence integrated media GmbH + * + * 26feb2004 Support for FS Activy Card (Grundig tuner) by + * Michael Dreher <michael@5dot1.de>, + * Oliver Endriss <o.endriss@gmx.de>, + * Andreas 'randy' Weinberger + * + * the project's page is at https://linuxtv.org + */ + + +#include "budget.h" +#include "ttpci-eeprom.h" + +#define TS_WIDTH (2 * TS_SIZE) +#define TS_WIDTH_ACTIVY TS_SIZE +#define TS_WIDTH_DVBC TS_SIZE +#define TS_HEIGHT_MASK 0xf00 +#define TS_HEIGHT_MASK_ACTIVY 0xc00 +#define TS_HEIGHT_MASK_DVBC 0xe00 +#define TS_MIN_BUFSIZE_K 188 +#define TS_MAX_BUFSIZE_K 1410 +#define TS_MAX_BUFSIZE_K_ACTIVY 564 +#define TS_MAX_BUFSIZE_K_DVBC 1316 +#define BUFFER_WARNING_WAIT (30*HZ) + +int budget_debug; +static int dma_buffer_size = TS_MIN_BUFSIZE_K; +module_param_named(debug, budget_debug, int, 0644); +module_param_named(bufsize, dma_buffer_size, int, 0444); +MODULE_PARM_DESC(debug, "Turn on/off budget debugging (default:off)."); +MODULE_PARM_DESC(bufsize, "DMA buffer size in KB, default: 188, min: 188, max: 1410 (Activy: 564)"); + +/**************************************************************************** + * TT budget / WinTV Nova + ****************************************************************************/ + +static int stop_ts_capture(struct budget *budget) +{ + dprintk(2, "budget: %p\n", budget); + + saa7146_write(budget->dev, MC1, MASK_20); // DMA3 off + SAA7146_IER_DISABLE(budget->dev, MASK_10); + return 0; +} + +static int start_ts_capture(struct budget *budget) +{ + struct saa7146_dev *dev = budget->dev; + + dprintk(2, "budget: %p\n", budget); + + if (!budget->feeding || !budget->fe_synced) + return 0; + + saa7146_write(dev, MC1, MASK_20); // DMA3 off + + memset(budget->grabbing, 0x00, budget->buffer_size); + + saa7146_write(dev, PCI_BT_V1, 0x001c0000 | (saa7146_read(dev, PCI_BT_V1) & ~0x001f0000)); + + budget->ttbp = 0; + + /* + * Signal path on the Activy: + * + * tuner -> SAA7146 port A -> SAA7146 BRS -> SAA7146 DMA3 -> memory + * + * Since the tuner feeds 204 bytes packets into the SAA7146, + * DMA3 is configured to strip the trailing 16 FEC bytes: + * Pitch: 188, NumBytes3: 188, NumLines3: 1024 + */ + + switch(budget->card->type) { + case BUDGET_FS_ACTIVY: + saa7146_write(dev, DD1_INIT, 0x04000000); + saa7146_write(dev, MC2, (MASK_09 | MASK_25)); + saa7146_write(dev, BRS_CTRL, 0x00000000); + break; + case BUDGET_PATCH: + saa7146_write(dev, DD1_INIT, 0x00000200); + saa7146_write(dev, MC2, (MASK_10 | MASK_26)); + saa7146_write(dev, BRS_CTRL, 0x60000000); + break; + case BUDGET_CIN1200C_MK3: + case BUDGET_KNC1C_MK3: + case BUDGET_KNC1C_TDA10024: + case BUDGET_KNC1CP_MK3: + if (budget->video_port == BUDGET_VIDEO_PORTA) { + saa7146_write(dev, DD1_INIT, 0x06000200); + saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + saa7146_write(dev, BRS_CTRL, 0x00000000); + } else { + saa7146_write(dev, DD1_INIT, 0x00000600); + saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + saa7146_write(dev, BRS_CTRL, 0x60000000); + } + break; + default: + if (budget->video_port == BUDGET_VIDEO_PORTA) { + saa7146_write(dev, DD1_INIT, 0x06000200); + saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + saa7146_write(dev, BRS_CTRL, 0x00000000); + } else { + saa7146_write(dev, DD1_INIT, 0x02000600); + saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + saa7146_write(dev, BRS_CTRL, 0x60000000); + } + } + + saa7146_write(dev, MC2, (MASK_08 | MASK_24)); + mdelay(10); + + saa7146_write(dev, BASE_ODD3, 0); + if (budget->buffer_size > budget->buffer_height * budget->buffer_width) { + // using odd/even buffers + saa7146_write(dev, BASE_EVEN3, budget->buffer_height * budget->buffer_width); + } else { + // using a single buffer + saa7146_write(dev, BASE_EVEN3, 0); + } + saa7146_write(dev, PROT_ADDR3, budget->buffer_size); + saa7146_write(dev, BASE_PAGE3, budget->pt.dma | ME1 | 0x90); + + saa7146_write(dev, PITCH3, budget->buffer_width); + saa7146_write(dev, NUM_LINE_BYTE3, + (budget->buffer_height << 16) | budget->buffer_width); + + saa7146_write(dev, MC2, (MASK_04 | MASK_20)); + + SAA7146_ISR_CLEAR(budget->dev, MASK_10); /* VPE */ + SAA7146_IER_ENABLE(budget->dev, MASK_10); /* VPE */ + saa7146_write(dev, MC1, (MASK_04 | MASK_20)); /* DMA3 on */ + + return 0; +} + +static int budget_read_fe_status(struct dvb_frontend *fe, + enum fe_status *status) +{ + struct budget *budget = (struct budget *) fe->dvb->priv; + int synced; + int ret; + + if (budget->read_fe_status) + ret = budget->read_fe_status(fe, status); + else + ret = -EINVAL; + + if (!ret) { + synced = (*status & FE_HAS_LOCK); + if (synced != budget->fe_synced) { + budget->fe_synced = synced; + spin_lock(&budget->feedlock); + if (synced) + start_ts_capture(budget); + else + stop_ts_capture(budget); + spin_unlock(&budget->feedlock); + } + } + return ret; +} + +static void vpeirq(struct tasklet_struct *t) +{ + struct budget *budget = from_tasklet(budget, t, vpe_tasklet); + u8 *mem = (u8 *) (budget->grabbing); + u32 olddma = budget->ttbp; + u32 newdma = saa7146_read(budget->dev, PCI_VDP3); + u32 count; + + /* Ensure streamed PCI data is synced to CPU */ + dma_sync_sg_for_cpu(&budget->dev->pci->dev, budget->pt.slist, + budget->pt.nents, DMA_FROM_DEVICE); + + /* nearest lower position divisible by 188 */ + newdma -= newdma % 188; + + if (newdma >= budget->buffer_size) + return; + + budget->ttbp = newdma; + + if (budget->feeding == 0 || newdma == olddma) + return; + + if (newdma > olddma) { /* no wraparound, dump olddma..newdma */ + count = newdma - olddma; + dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, count / 188); + } else { /* wraparound, dump olddma..buflen and 0..newdma */ + count = budget->buffer_size - olddma; + dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, count / 188); + count += newdma; + dvb_dmx_swfilter_packets(&budget->demux, mem, newdma / 188); + } + + if (count > budget->buffer_warning_threshold) + budget->buffer_warnings++; + + if (budget->buffer_warnings && time_after(jiffies, budget->buffer_warning_time)) { + printk("%s %s: used %d times >80%% of buffer (%u bytes now)\n", + budget->dev->name, __func__, budget->buffer_warnings, count); + budget->buffer_warning_time = jiffies + BUFFER_WARNING_WAIT; + budget->buffer_warnings = 0; + } +} + + +static int ttpci_budget_debiread_nolock(struct budget *budget, u32 config, + int addr, int count, int nobusyloop) +{ + struct saa7146_dev *saa = budget->dev; + int result; + + result = saa7146_wait_for_debi_done(saa, nobusyloop); + if (result < 0) + return result; + + saa7146_write(saa, DEBI_COMMAND, (count << 17) | 0x10000 | (addr & 0xffff)); + saa7146_write(saa, DEBI_CONFIG, config); + saa7146_write(saa, DEBI_PAGE, 0); + saa7146_write(saa, MC2, (2 << 16) | 2); + + result = saa7146_wait_for_debi_done(saa, nobusyloop); + if (result < 0) + return result; + + result = saa7146_read(saa, DEBI_AD); + result &= (0xffffffffUL >> ((4 - count) * 8)); + return result; +} + +int ttpci_budget_debiread(struct budget *budget, u32 config, int addr, int count, + int uselocks, int nobusyloop) +{ + if (count > 4 || count <= 0) + return 0; + + if (uselocks) { + unsigned long flags; + int result; + + spin_lock_irqsave(&budget->debilock, flags); + result = ttpci_budget_debiread_nolock(budget, config, addr, + count, nobusyloop); + spin_unlock_irqrestore(&budget->debilock, flags); + return result; + } + return ttpci_budget_debiread_nolock(budget, config, addr, + count, nobusyloop); +} + +static int ttpci_budget_debiwrite_nolock(struct budget *budget, u32 config, + int addr, int count, u32 value, int nobusyloop) +{ + struct saa7146_dev *saa = budget->dev; + int result; + + result = saa7146_wait_for_debi_done(saa, nobusyloop); + if (result < 0) + return result; + + saa7146_write(saa, DEBI_COMMAND, (count << 17) | 0x00000 | (addr & 0xffff)); + saa7146_write(saa, DEBI_CONFIG, config); + saa7146_write(saa, DEBI_PAGE, 0); + saa7146_write(saa, DEBI_AD, value); + saa7146_write(saa, MC2, (2 << 16) | 2); + + result = saa7146_wait_for_debi_done(saa, nobusyloop); + return result < 0 ? result : 0; +} + +int ttpci_budget_debiwrite(struct budget *budget, u32 config, int addr, + int count, u32 value, int uselocks, int nobusyloop) +{ + if (count > 4 || count <= 0) + return 0; + + if (uselocks) { + unsigned long flags; + int result; + + spin_lock_irqsave(&budget->debilock, flags); + result = ttpci_budget_debiwrite_nolock(budget, config, addr, + count, value, nobusyloop); + spin_unlock_irqrestore(&budget->debilock, flags); + return result; + } + return ttpci_budget_debiwrite_nolock(budget, config, addr, + count, value, nobusyloop); +} + + +/**************************************************************************** + * DVB API SECTION + ****************************************************************************/ + +static int budget_start_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct budget *budget = (struct budget *) demux->priv; + int status = 0; + + dprintk(2, "budget: %p\n", budget); + + if (!demux->dmx.frontend) + return -EINVAL; + + spin_lock(&budget->feedlock); + feed->pusi_seen = false; /* have a clean section start */ + if (budget->feeding++ == 0) + status = start_ts_capture(budget); + spin_unlock(&budget->feedlock); + return status; +} + +static int budget_stop_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct budget *budget = (struct budget *) demux->priv; + int status = 0; + + dprintk(2, "budget: %p\n", budget); + + spin_lock(&budget->feedlock); + if (--budget->feeding == 0) + status = stop_ts_capture(budget); + spin_unlock(&budget->feedlock); + return status; +} + +static int budget_register(struct budget *budget) +{ + struct dvb_demux *dvbdemux = &budget->demux; + int ret; + + dprintk(2, "budget: %p\n", budget); + + dvbdemux->priv = (void *) budget; + + dvbdemux->filternum = 256; + dvbdemux->feednum = 256; + dvbdemux->start_feed = budget_start_feed; + dvbdemux->stop_feed = budget_stop_feed; + dvbdemux->write_to_decoder = NULL; + + dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING); + + dvb_dmx_init(&budget->demux); + + budget->dmxdev.filternum = 256; + budget->dmxdev.demux = &dvbdemux->dmx; + budget->dmxdev.capabilities = 0; + + dvb_dmxdev_init(&budget->dmxdev, &budget->dvb_adapter); + + budget->hw_frontend.source = DMX_FRONTEND_0; + + ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &budget->hw_frontend); + + if (ret < 0) + goto err_release_dmx; + + budget->mem_frontend.source = DMX_MEMORY_FE; + ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &budget->mem_frontend); + if (ret < 0) + goto err_release_dmx; + + ret = dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, &budget->hw_frontend); + if (ret < 0) + goto err_release_dmx; + + dvb_net_init(&budget->dvb_adapter, &budget->dvb_net, &dvbdemux->dmx); + + return 0; + +err_release_dmx: + dvb_dmxdev_release(&budget->dmxdev); + dvb_dmx_release(&budget->demux); + return ret; +} + +static void budget_unregister(struct budget *budget) +{ + struct dvb_demux *dvbdemux = &budget->demux; + + dprintk(2, "budget: %p\n", budget); + + dvb_net_release(&budget->dvb_net); + + dvbdemux->dmx.close(&dvbdemux->dmx); + dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &budget->hw_frontend); + dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &budget->mem_frontend); + + dvb_dmxdev_release(&budget->dmxdev); + dvb_dmx_release(&budget->demux); +} + +int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, + struct saa7146_pci_extension_data *info, + struct module *owner, short *adapter_nums) +{ + int ret = 0; + struct budget_info *bi = info->ext_priv; + int max_bufsize; + int height_mask; + + memset(budget, 0, sizeof(struct budget)); + + dprintk(2, "dev: %p, budget: %p\n", dev, budget); + + budget->card = bi; + budget->dev = (struct saa7146_dev *) dev; + + switch(budget->card->type) { + case BUDGET_FS_ACTIVY: + budget->buffer_width = TS_WIDTH_ACTIVY; + max_bufsize = TS_MAX_BUFSIZE_K_ACTIVY; + height_mask = TS_HEIGHT_MASK_ACTIVY; + break; + + case BUDGET_KNC1C: + case BUDGET_KNC1CP: + case BUDGET_CIN1200C: + case BUDGET_KNC1C_MK3: + case BUDGET_KNC1C_TDA10024: + case BUDGET_KNC1CP_MK3: + case BUDGET_CIN1200C_MK3: + budget->buffer_width = TS_WIDTH_DVBC; + max_bufsize = TS_MAX_BUFSIZE_K_DVBC; + height_mask = TS_HEIGHT_MASK_DVBC; + break; + + default: + budget->buffer_width = TS_WIDTH; + max_bufsize = TS_MAX_BUFSIZE_K; + height_mask = TS_HEIGHT_MASK; + } + + if (dma_buffer_size < TS_MIN_BUFSIZE_K) + dma_buffer_size = TS_MIN_BUFSIZE_K; + else if (dma_buffer_size > max_bufsize) + dma_buffer_size = max_bufsize; + + budget->buffer_height = dma_buffer_size * 1024 / budget->buffer_width; + if (budget->buffer_height > 0xfff) { + budget->buffer_height /= 2; + budget->buffer_height &= height_mask; + budget->buffer_size = 2 * budget->buffer_height * budget->buffer_width; + } else { + budget->buffer_height &= height_mask; + budget->buffer_size = budget->buffer_height * budget->buffer_width; + } + budget->buffer_warning_threshold = budget->buffer_size * 80/100; + budget->buffer_warnings = 0; + budget->buffer_warning_time = jiffies; + + dprintk(2, "%s: buffer type = %s, width = %d, height = %d\n", + budget->dev->name, + budget->buffer_size > budget->buffer_width * budget->buffer_height ? "odd/even" : "single", + budget->buffer_width, budget->buffer_height); + printk("%s: dma buffer size %u\n", budget->dev->name, budget->buffer_size); + + ret = dvb_register_adapter(&budget->dvb_adapter, budget->card->name, + owner, &budget->dev->pci->dev, adapter_nums); + if (ret < 0) + return ret; + + /* set dd1 stream a & b */ + saa7146_write(dev, DD1_STREAM_B, 0x00000000); + saa7146_write(dev, MC2, (MASK_09 | MASK_25)); + saa7146_write(dev, MC2, (MASK_10 | MASK_26)); + saa7146_write(dev, DD1_INIT, 0x02000000); + saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + + if (bi->type != BUDGET_FS_ACTIVY) + budget->video_port = BUDGET_VIDEO_PORTB; + else + budget->video_port = BUDGET_VIDEO_PORTA; + spin_lock_init(&budget->feedlock); + spin_lock_init(&budget->debilock); + + /* the Siemens DVB needs this if you want to have the i2c chips + get recognized before the main driver is loaded */ + if (bi->type != BUDGET_FS_ACTIVY) + saa7146_write(dev, GPIO_CTRL, 0x500000); /* GPIO 3 = 1 */ + + strscpy(budget->i2c_adap.name, budget->card->name, + sizeof(budget->i2c_adap.name)); + + saa7146_i2c_adapter_prepare(dev, &budget->i2c_adap, SAA7146_I2C_BUS_BIT_RATE_120); + strscpy(budget->i2c_adap.name, budget->card->name, + sizeof(budget->i2c_adap.name)); + + if (i2c_add_adapter(&budget->i2c_adap) < 0) { + ret = -ENOMEM; + goto err_dvb_unregister; + } + + ttpci_eeprom_parse_mac(&budget->i2c_adap, budget->dvb_adapter.proposed_mac); + + budget->grabbing = saa7146_vmalloc_build_pgtable(dev->pci, budget->buffer_size, &budget->pt); + if (NULL == budget->grabbing) { + ret = -ENOMEM; + goto err_del_i2c; + } + + saa7146_write(dev, PCI_BT_V1, 0x001c0000); + /* upload all */ + saa7146_write(dev, GPIO_CTRL, 0x000000); + + tasklet_setup(&budget->vpe_tasklet, vpeirq); + + /* frontend power on */ + if (bi->type != BUDGET_FS_ACTIVY) + saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI); + + if ((ret = budget_register(budget)) == 0) + return 0; /* Everything OK */ + + /* An error occurred, cleanup resources */ + saa7146_vfree_destroy_pgtable(dev->pci, budget->grabbing, &budget->pt); + +err_del_i2c: + i2c_del_adapter(&budget->i2c_adap); + +err_dvb_unregister: + dvb_unregister_adapter(&budget->dvb_adapter); + + return ret; +} + +void ttpci_budget_init_hooks(struct budget *budget) +{ + if (budget->dvb_frontend && !budget->read_fe_status) { + budget->read_fe_status = budget->dvb_frontend->ops.read_status; + budget->dvb_frontend->ops.read_status = budget_read_fe_status; + } +} + +int ttpci_budget_deinit(struct budget *budget) +{ + struct saa7146_dev *dev = budget->dev; + + dprintk(2, "budget: %p\n", budget); + + budget_unregister(budget); + + tasklet_kill(&budget->vpe_tasklet); + + saa7146_vfree_destroy_pgtable(dev->pci, budget->grabbing, &budget->pt); + + i2c_del_adapter(&budget->i2c_adap); + + dvb_unregister_adapter(&budget->dvb_adapter); + + return 0; +} + +void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 * isr) +{ + struct budget *budget = (struct budget *) dev->ext_priv; + + dprintk(8, "dev: %p, budget: %p\n", dev, budget); + + if (*isr & MASK_10) + tasklet_schedule(&budget->vpe_tasklet); +} + +void ttpci_budget_set_video_port(struct saa7146_dev *dev, int video_port) +{ + struct budget *budget = (struct budget *) dev->ext_priv; + + spin_lock(&budget->feedlock); + budget->video_port = video_port; + if (budget->feeding) { + stop_ts_capture(budget); + start_ts_capture(budget); + } + spin_unlock(&budget->feedlock); +} + +EXPORT_SYMBOL_GPL(ttpci_budget_debiread); +EXPORT_SYMBOL_GPL(ttpci_budget_debiwrite); +EXPORT_SYMBOL_GPL(ttpci_budget_init); +EXPORT_SYMBOL_GPL(ttpci_budget_init_hooks); +EXPORT_SYMBOL_GPL(ttpci_budget_deinit); +EXPORT_SYMBOL_GPL(ttpci_budget_irq10_handler); +EXPORT_SYMBOL_GPL(ttpci_budget_set_video_port); +EXPORT_SYMBOL_GPL(budget_debug); + +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/deprecated/saa7146/ttpci/budget.c b/drivers/staging/media/deprecated/saa7146/ttpci/budget.c new file mode 100644 index 000000000000..a88711a3ac7f --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/ttpci/budget.c @@ -0,0 +1,883 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * budget.c: driver for the SAA7146 based Budget DVB cards + * + * Compiled from various sources by Michael Hunold <michael@mihu.de> + * + * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de> + * + * Copyright (C) 1999-2002 Ralph Metzler + * & Marcus Metzler for convergence integrated media GmbH + * + * 26feb2004 Support for FS Activy Card (Grundig tuner) by + * Michael Dreher <michael@5dot1.de>, + * Oliver Endriss <o.endriss@gmx.de> and + * Andreas 'randy' Weinberger + * + * the project's page is at https://linuxtv.org + */ + +#include "budget.h" +#include "stv0299.h" +#include "ves1x93.h" +#include "ves1820.h" +#include "l64781.h" +#include "tda8083.h" +#include "s5h1420.h" +#include "tda10086.h" +#include "tda826x.h" +#include "lnbp21.h" +#include "bsru6.h" +#include "bsbe1.h" +#include "tdhd1.h" +#include "stv6110x.h" +#include "stv090x.h" +#include "isl6423.h" +#include "lnbh24.h" + + +static int diseqc_method; +module_param(diseqc_method, int, 0444); +MODULE_PARM_DESC(diseqc_method, "Select DiSEqC method for subsystem id 13c2:1003, 0: default, 1: more reliable (for newer revisions only)"); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static void Set22K (struct budget *budget, int state) +{ + struct saa7146_dev *dev=budget->dev; + dprintk(2, "budget: %p\n", budget); + saa7146_setgpio(dev, 3, (state ? SAA7146_GPIO_OUTHI : SAA7146_GPIO_OUTLO)); +} + +/* Diseqc functions only for TT Budget card */ +/* taken from the Skyvision DVB driver by + Ralph Metzler <rjkm@metzlerbros.de> */ + +static void DiseqcSendBit (struct budget *budget, int data) +{ + struct saa7146_dev *dev=budget->dev; + dprintk(2, "budget: %p\n", budget); + + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); + udelay(data ? 500 : 1000); + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); + udelay(data ? 1000 : 500); +} + +static void DiseqcSendByte (struct budget *budget, int data) +{ + int i, par=1, d; + + dprintk(2, "budget: %p\n", budget); + + for (i=7; i>=0; i--) { + d = (data>>i)&1; + par ^= d; + DiseqcSendBit(budget, d); + } + + DiseqcSendBit(budget, par); +} + +static int SendDiSEqCMsg (struct budget *budget, int len, u8 *msg, unsigned long burst) +{ + struct saa7146_dev *dev=budget->dev; + int i; + + dprintk(2, "budget: %p\n", budget); + + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); + mdelay(16); + + for (i=0; i<len; i++) + DiseqcSendByte(budget, msg[i]); + + mdelay(16); + + if (burst!=-1) { + if (burst) + DiseqcSendByte(budget, 0xff); + else { + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); + mdelay(12); + udelay(500); + saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); + } + msleep(20); + } + + return 0; +} + +/* + * Routines for the Fujitsu Siemens Activy budget card + * 22 kHz tone and DiSEqC are handled by the frontend. + * Voltage must be set here. + * GPIO 1: LNBP EN, GPIO 2: LNBP VSEL + */ +static int SetVoltage_Activy(struct budget *budget, + enum fe_sec_voltage voltage) +{ + struct saa7146_dev *dev=budget->dev; + + dprintk(2, "budget: %p\n", budget); + + switch (voltage) { + case SEC_VOLTAGE_13: + saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); + saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTLO); + break; + case SEC_VOLTAGE_18: + saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); + saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI); + break; + case SEC_VOLTAGE_OFF: + saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTLO); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int siemens_budget_set_voltage(struct dvb_frontend *fe, + enum fe_sec_voltage voltage) +{ + struct budget* budget = (struct budget*) fe->dvb->priv; + + return SetVoltage_Activy (budget, voltage); +} + +static int budget_set_tone(struct dvb_frontend *fe, + enum fe_sec_tone_mode tone) +{ + struct budget* budget = (struct budget*) fe->dvb->priv; + + switch (tone) { + case SEC_TONE_ON: + Set22K (budget, 1); + break; + + case SEC_TONE_OFF: + Set22K (budget, 0); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int budget_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd) +{ + struct budget* budget = (struct budget*) fe->dvb->priv; + + SendDiSEqCMsg (budget, cmd->msg_len, cmd->msg, 0); + + return 0; +} + +static int budget_diseqc_send_burst(struct dvb_frontend *fe, + enum fe_sec_mini_cmd minicmd) +{ + struct budget* budget = (struct budget*) fe->dvb->priv; + + SendDiSEqCMsg (budget, 0, NULL, minicmd); + + return 0; +} + +static int alps_bsrv2_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct budget* budget = (struct budget*) fe->dvb->priv; + u8 pwr = 0; + u8 buf[4]; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; + u32 div = (c->frequency + 479500) / 125; + + if (c->frequency > 2000000) + pwr = 3; + else if (c->frequency > 1800000) + pwr = 2; + else if (c->frequency > 1600000) + pwr = 1; + else if (c->frequency > 1200000) + pwr = 0; + else if (c->frequency >= 1100000) + pwr = 1; + else pwr = 2; + + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = ((div & 0x18000) >> 10) | 0x95; + buf[3] = (pwr << 6) | 0x30; + + // NOTE: since we're using a prescaler of 2, we set the + // divisor frequency to 62.5kHz and divide by 125 above + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; + return 0; +} + +static struct ves1x93_config alps_bsrv2_config = +{ + .demod_address = 0x08, + .xin = 90100000UL, + .invert_pwm = 0, +}; + +static int alps_tdbe2_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct budget* budget = (struct budget*) fe->dvb->priv; + u32 div; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x62, .flags = 0, .buf = data, .len = sizeof(data) }; + + div = (c->frequency + 35937500 + 31250) / 62500; + + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0x85 | ((div >> 10) & 0x60); + data[3] = (c->frequency < 174000000 ? 0x88 : c->frequency < 470000000 ? 0x84 : 0x81); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; + return 0; +} + +static struct ves1820_config alps_tdbe2_config = { + .demod_address = 0x09, + .xin = 57840000UL, + .invert = 1, + .selagc = VES1820_SELAGC_SIGNAMPERR, +}; + +static int grundig_29504_401_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct budget *budget = fe->dvb->priv; + u8 *tuner_addr = fe->tuner_priv; + u32 div; + u8 cfg, cpump, band_select; + u8 data[4]; + struct i2c_msg msg = { .flags = 0, .buf = data, .len = sizeof(data) }; + + if (tuner_addr) + msg.addr = *tuner_addr; + else + msg.addr = 0x61; + + div = (36125000 + c->frequency) / 166666; + + cfg = 0x88; + + if (c->frequency < 175000000) + cpump = 2; + else if (c->frequency < 390000000) + cpump = 1; + else if (c->frequency < 470000000) + cpump = 2; + else if (c->frequency < 750000000) + cpump = 1; + else + cpump = 3; + + if (c->frequency < 175000000) + band_select = 0x0e; + else if (c->frequency < 470000000) + band_select = 0x05; + else + band_select = 0x03; + + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = ((div >> 10) & 0x60) | cfg; + data[3] = (cpump << 6) | band_select; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; + return 0; +} + +static struct l64781_config grundig_29504_401_config = { + .demod_address = 0x55, +}; + +static struct l64781_config grundig_29504_401_config_activy = { + .demod_address = 0x54, +}; + +static u8 tuner_address_grundig_29504_401_activy = 0x60; + +static int grundig_29504_451_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct budget* budget = (struct budget*) fe->dvb->priv; + u32 div; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; + + div = c->frequency / 125; + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0x8e; + data[3] = 0x00; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; + return 0; +} + +static struct tda8083_config grundig_29504_451_config = { + .demod_address = 0x68, +}; + +static int s5h1420_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct budget* budget = (struct budget*) fe->dvb->priv; + u32 div; + u8 data[4]; + struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; + + div = c->frequency / 1000; + data[0] = (div >> 8) & 0x7f; + data[1] = div & 0xff; + data[2] = 0xc2; + + if (div < 1450) + data[3] = 0x00; + else if (div < 1850) + data[3] = 0x40; + else if (div < 2000) + data[3] = 0x80; + else + data[3] = 0xc0; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; + + return 0; +} + +static struct s5h1420_config s5h1420_config = { + .demod_address = 0x53, + .invert = 1, + .cdclk_polarity = 1, +}; + +static struct tda10086_config tda10086_config = { + .demod_address = 0x0e, + .invert = 0, + .diseqc_tone = 1, + .xtal_freq = TDA10086_XTAL_16M, +}; + +static const struct stv0299_config alps_bsru6_config_activy = { + .demod_address = 0x68, + .inittab = alps_bsru6_inittab, + .mclk = 88000000UL, + .invert = 1, + .op0_off = 1, + .min_delay_ms = 100, + .set_symbol_rate = alps_bsru6_set_symbol_rate, +}; + +static const struct stv0299_config alps_bsbe1_config_activy = { + .demod_address = 0x68, + .inittab = alps_bsbe1_inittab, + .mclk = 88000000UL, + .invert = 1, + .op0_off = 1, + .min_delay_ms = 100, + .set_symbol_rate = alps_bsbe1_set_symbol_rate, +}; + +static int alps_tdhd1_204_request_firmware(struct dvb_frontend *fe, const struct firmware **fw, char *name) +{ + struct budget *budget = (struct budget *)fe->dvb->priv; + + return request_firmware(fw, name, &budget->dev->pci->dev); +} + + +static int i2c_readreg(struct i2c_adapter *i2c, u8 adr, u8 reg) +{ + u8 val; + struct i2c_msg msg[] = { + { .addr = adr, .flags = 0, .buf = ®, .len = 1 }, + { .addr = adr, .flags = I2C_M_RD, .buf = &val, .len = 1 } + }; + + return (i2c_transfer(i2c, msg, 2) != 2) ? -EIO : val; +} + +static u8 read_pwm(struct budget* budget) +{ + u8 b = 0xff; + u8 pwm; + struct i2c_msg msg[] = { { .addr = 0x50,.flags = 0,.buf = &b,.len = 1 }, + { .addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} }; + + if ((i2c_transfer(&budget->i2c_adap, msg, 2) != 2) || (pwm == 0xff)) + pwm = 0x48; + + return pwm; +} + +static struct stv090x_config tt1600_stv090x_config = { + .device = STV0903, + .demod_mode = STV090x_SINGLE, + .clk_mode = STV090x_CLK_EXT, + + .xtal = 13500000, + .address = 0x68, + + .ts1_mode = STV090x_TSMODE_DVBCI, + .ts2_mode = STV090x_TSMODE_SERIAL_CONTINUOUS, + + .repeater_level = STV090x_RPTLEVEL_16, + + .tuner_init = NULL, + .tuner_sleep = NULL, + .tuner_set_mode = NULL, + .tuner_set_frequency = NULL, + .tuner_get_frequency = NULL, + .tuner_set_bandwidth = NULL, + .tuner_get_bandwidth = NULL, + .tuner_set_bbgain = NULL, + .tuner_get_bbgain = NULL, + .tuner_set_refclk = NULL, + .tuner_get_status = NULL, +}; + +static struct stv6110x_config tt1600_stv6110x_config = { + .addr = 0x60, + .refclk = 27000000, + .clk_div = 2, +}; + +static struct isl6423_config tt1600_isl6423_config = { + .current_max = SEC_CURRENT_515m, + .curlim = SEC_CURRENT_LIM_ON, + .mod_extern = 1, + .addr = 0x08, +}; + +static void frontend_init(struct budget *budget) +{ + (void)alps_bsbe1_config; /* avoid warning */ + + switch(budget->dev->pci->subsystem_device) { + case 0x1003: // Hauppauge/TT Nova budget (stv0299/ALPS BSRU6(tsa5059) OR ves1893/ALPS BSRV2(sp5659)) + case 0x1013: + // try the ALPS BSRV2 first of all + budget->dvb_frontend = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &budget->i2c_adap); + if (budget->dvb_frontend) { + budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params; + budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd; + budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst; + budget->dvb_frontend->ops.set_tone = budget_set_tone; + break; + } + + // try the ALPS BSRU6 now + budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap); + if (budget->dvb_frontend) { + budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; + budget->dvb_frontend->tuner_priv = &budget->i2c_adap; + if (budget->dev->pci->subsystem_device == 0x1003 && diseqc_method == 0) { + budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd; + budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst; + budget->dvb_frontend->ops.set_tone = budget_set_tone; + } + break; + } + break; + + case 0x1004: // Hauppauge/TT DVB-C budget (ves1820/ALPS TDBE2(sp5659)) + + budget->dvb_frontend = dvb_attach(ves1820_attach, &alps_tdbe2_config, &budget->i2c_adap, read_pwm(budget)); + if (budget->dvb_frontend) { + budget->dvb_frontend->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params; + break; + } + break; + + case 0x1005: // Hauppauge/TT Nova-T budget (L64781/Grundig 29504-401(tsa5060)) + + budget->dvb_frontend = dvb_attach(l64781_attach, &grundig_29504_401_config, &budget->i2c_adap); + if (budget->dvb_frontend) { + budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params; + budget->dvb_frontend->tuner_priv = NULL; + break; + } + break; + + case 0x4f52: /* Cards based on Philips Semi Sylt PCI ref. design */ + budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap); + if (budget->dvb_frontend) { + printk(KERN_INFO "budget: tuner ALPS BSRU6 in Philips Semi. Sylt detected\n"); + budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; + budget->dvb_frontend->tuner_priv = &budget->i2c_adap; + break; + } + break; + + case 0x4f60: /* Fujitsu Siemens Activy Budget-S PCI rev AL (stv0299/tsa5059) */ + { + int subtype = i2c_readreg(&budget->i2c_adap, 0x50, 0x67); + + if (subtype < 0) + break; + /* fixme: find a better way to identify the card */ + if (subtype < 0x36) { + /* assume ALPS BSRU6 */ + budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config_activy, &budget->i2c_adap); + if (budget->dvb_frontend) { + printk(KERN_INFO "budget: tuner ALPS BSRU6 detected\n"); + budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; + budget->dvb_frontend->tuner_priv = &budget->i2c_adap; + budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage; + budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL; + break; + } + } else { + /* assume ALPS BSBE1 */ + /* reset tuner */ + saa7146_setgpio(budget->dev, 3, SAA7146_GPIO_OUTLO); + msleep(50); + saa7146_setgpio(budget->dev, 3, SAA7146_GPIO_OUTHI); + msleep(250); + budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsbe1_config_activy, &budget->i2c_adap); + if (budget->dvb_frontend) { + printk(KERN_INFO "budget: tuner ALPS BSBE1 detected\n"); + budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params; + budget->dvb_frontend->tuner_priv = &budget->i2c_adap; + budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage; + budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL; + break; + } + } + break; + } + + case 0x4f61: // Fujitsu Siemens Activy Budget-S PCI rev GR (tda8083/Grundig 29504-451(tsa5522)) + budget->dvb_frontend = dvb_attach(tda8083_attach, &grundig_29504_451_config, &budget->i2c_adap); + if (budget->dvb_frontend) { + budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params; + budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage; + budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL; + } + break; + + case 0x5f60: /* Fujitsu Siemens Activy Budget-T PCI rev AL (tda10046/ALPS TDHD1-204A) */ + budget->dvb_frontend = dvb_attach(tda10046_attach, &alps_tdhd1_204a_config, &budget->i2c_adap); + if (budget->dvb_frontend) { + budget->dvb_frontend->ops.tuner_ops.set_params = alps_tdhd1_204a_tuner_set_params; + budget->dvb_frontend->tuner_priv = &budget->i2c_adap; + } + break; + + case 0x5f61: /* Fujitsu Siemens Activy Budget-T PCI rev GR (L64781/Grundig 29504-401(tsa5060)) */ + budget->dvb_frontend = dvb_attach(l64781_attach, &grundig_29504_401_config_activy, &budget->i2c_adap); + if (budget->dvb_frontend) { + budget->dvb_frontend->tuner_priv = &tuner_address_grundig_29504_401_activy; + budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params; + } + break; + + case 0x1016: // Hauppauge/TT Nova-S SE (samsung s5h1420/????(tda8260)) + { + struct dvb_frontend *fe; + + fe = dvb_attach(s5h1420_attach, &s5h1420_config, &budget->i2c_adap); + if (fe) { + fe->ops.tuner_ops.set_params = s5h1420_tuner_set_params; + budget->dvb_frontend = fe; + if (dvb_attach(lnbp21_attach, fe, &budget->i2c_adap, + 0, 0) == NULL) { + printk("%s: No LNBP21 found!\n", __func__); + goto error_out; + } + break; + } + } + fallthrough; + case 0x1018: // TT Budget-S-1401 (philips tda10086/philips tda8262) + { + struct dvb_frontend *fe; + + // gpio2 is connected to CLB - reset it + leave it high + saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO); + msleep(1); + saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI); + msleep(1); + + fe = dvb_attach(tda10086_attach, &tda10086_config, &budget->i2c_adap); + if (fe) { + budget->dvb_frontend = fe; + if (dvb_attach(tda826x_attach, fe, 0x60, + &budget->i2c_adap, 0) == NULL) + printk("%s: No tda826x found!\n", __func__); + if (dvb_attach(lnbp21_attach, fe, + &budget->i2c_adap, 0, 0) == NULL) { + printk("%s: No LNBP21 found!\n", __func__); + goto error_out; + } + break; + } + } + fallthrough; + + case 0x101c: { /* TT S2-1600 */ + const struct stv6110x_devctl *ctl; + saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO); + msleep(50); + saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI); + msleep(250); + + budget->dvb_frontend = dvb_attach(stv090x_attach, + &tt1600_stv090x_config, + &budget->i2c_adap, + STV090x_DEMODULATOR_0); + + if (budget->dvb_frontend) { + + ctl = dvb_attach(stv6110x_attach, + budget->dvb_frontend, + &tt1600_stv6110x_config, + &budget->i2c_adap); + + if (ctl) { + tt1600_stv090x_config.tuner_init = ctl->tuner_init; + tt1600_stv090x_config.tuner_sleep = ctl->tuner_sleep; + tt1600_stv090x_config.tuner_set_mode = ctl->tuner_set_mode; + tt1600_stv090x_config.tuner_set_frequency = ctl->tuner_set_frequency; + tt1600_stv090x_config.tuner_get_frequency = ctl->tuner_get_frequency; + tt1600_stv090x_config.tuner_set_bandwidth = ctl->tuner_set_bandwidth; + tt1600_stv090x_config.tuner_get_bandwidth = ctl->tuner_get_bandwidth; + tt1600_stv090x_config.tuner_set_bbgain = ctl->tuner_set_bbgain; + tt1600_stv090x_config.tuner_get_bbgain = ctl->tuner_get_bbgain; + tt1600_stv090x_config.tuner_set_refclk = ctl->tuner_set_refclk; + tt1600_stv090x_config.tuner_get_status = ctl->tuner_get_status; + + /* call the init function once to initialize + tuner's clock output divider and demod's + master clock */ + if (budget->dvb_frontend->ops.init) + budget->dvb_frontend->ops.init(budget->dvb_frontend); + + if (dvb_attach(isl6423_attach, + budget->dvb_frontend, + &budget->i2c_adap, + &tt1600_isl6423_config) == NULL) { + printk(KERN_ERR "%s: No Intersil ISL6423 found!\n", __func__); + goto error_out; + } + } else { + printk(KERN_ERR "%s: No STV6110(A) Silicon Tuner found!\n", __func__); + goto error_out; + } + } + } + break; + + case 0x1020: { /* Omicom S2 */ + const struct stv6110x_devctl *ctl; + saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO); + msleep(50); + saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI); + msleep(250); + + budget->dvb_frontend = dvb_attach(stv090x_attach, + &tt1600_stv090x_config, + &budget->i2c_adap, + STV090x_DEMODULATOR_0); + + if (budget->dvb_frontend) { + printk(KERN_INFO "budget: Omicom S2 detected\n"); + + ctl = dvb_attach(stv6110x_attach, + budget->dvb_frontend, + &tt1600_stv6110x_config, + &budget->i2c_adap); + + if (ctl) { + tt1600_stv090x_config.tuner_init = ctl->tuner_init; + tt1600_stv090x_config.tuner_sleep = ctl->tuner_sleep; + tt1600_stv090x_config.tuner_set_mode = ctl->tuner_set_mode; + tt1600_stv090x_config.tuner_set_frequency = ctl->tuner_set_frequency; + tt1600_stv090x_config.tuner_get_frequency = ctl->tuner_get_frequency; + tt1600_stv090x_config.tuner_set_bandwidth = ctl->tuner_set_bandwidth; + tt1600_stv090x_config.tuner_get_bandwidth = ctl->tuner_get_bandwidth; + tt1600_stv090x_config.tuner_set_bbgain = ctl->tuner_set_bbgain; + tt1600_stv090x_config.tuner_get_bbgain = ctl->tuner_get_bbgain; + tt1600_stv090x_config.tuner_set_refclk = ctl->tuner_set_refclk; + tt1600_stv090x_config.tuner_get_status = ctl->tuner_get_status; + + /* call the init function once to initialize + tuner's clock output divider and demod's + master clock */ + if (budget->dvb_frontend->ops.init) + budget->dvb_frontend->ops.init(budget->dvb_frontend); + + if (dvb_attach(lnbh24_attach, + budget->dvb_frontend, + &budget->i2c_adap, + LNBH24_PCL | LNBH24_TTX, + LNBH24_TEN, 0x14>>1) == NULL) { + printk(KERN_ERR + "No LNBH24 found!\n"); + goto error_out; + } + } else { + printk(KERN_ERR "%s: No STV6110(A) Silicon Tuner found!\n", __func__); + goto error_out; + } + } + } + break; + } + + if (budget->dvb_frontend == NULL) { + printk("budget: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", + budget->dev->pci->vendor, + budget->dev->pci->device, + budget->dev->pci->subsystem_vendor, + budget->dev->pci->subsystem_device); + } else { + if (dvb_register_frontend(&budget->dvb_adapter, budget->dvb_frontend)) + goto error_out; + } + return; + +error_out: + printk("budget: Frontend registration failed!\n"); + dvb_frontend_detach(budget->dvb_frontend); + budget->dvb_frontend = NULL; + return; +} + +static int budget_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_data *info) +{ + struct budget *budget = NULL; + int err; + + budget = kmalloc(sizeof(struct budget), GFP_KERNEL); + if( NULL == budget ) { + return -ENOMEM; + } + + dprintk(2, "dev:%p, info:%p, budget:%p\n", dev, info, budget); + + dev->ext_priv = budget; + + err = ttpci_budget_init(budget, dev, info, THIS_MODULE, adapter_nr); + if (err) { + printk("==> failed\n"); + kfree (budget); + return err; + } + + budget->dvb_adapter.priv = budget; + frontend_init(budget); + + ttpci_budget_init_hooks(budget); + + return 0; +} + +static int budget_detach (struct saa7146_dev* dev) +{ + struct budget *budget = (struct budget*) dev->ext_priv; + int err; + + if (budget->dvb_frontend) { + dvb_unregister_frontend(budget->dvb_frontend); + dvb_frontend_detach(budget->dvb_frontend); + } + + err = ttpci_budget_deinit (budget); + + kfree (budget); + dev->ext_priv = NULL; + + return err; +} + +static struct saa7146_extension budget_extension; + +MAKE_BUDGET_INFO(ttbs, "TT-Budget/WinTV-NOVA-S PCI", BUDGET_TT); +MAKE_BUDGET_INFO(ttbc, "TT-Budget/WinTV-NOVA-C PCI", BUDGET_TT); +MAKE_BUDGET_INFO(ttbt, "TT-Budget/WinTV-NOVA-T PCI", BUDGET_TT); +MAKE_BUDGET_INFO(satel, "SATELCO Multimedia PCI", BUDGET_TT_HW_DISEQC); +MAKE_BUDGET_INFO(ttbs1401, "TT-Budget-S-1401 PCI", BUDGET_TT); +MAKE_BUDGET_INFO(tt1600, "TT-Budget S2-1600 PCI", BUDGET_TT); +MAKE_BUDGET_INFO(fsacs0, "Fujitsu Siemens Activy Budget-S PCI (rev GR/grundig frontend)", BUDGET_FS_ACTIVY); +MAKE_BUDGET_INFO(fsacs1, "Fujitsu Siemens Activy Budget-S PCI (rev AL/alps frontend)", BUDGET_FS_ACTIVY); +MAKE_BUDGET_INFO(fsact, "Fujitsu Siemens Activy Budget-T PCI (rev GR/Grundig frontend)", BUDGET_FS_ACTIVY); +MAKE_BUDGET_INFO(fsact1, "Fujitsu Siemens Activy Budget-T PCI (rev AL/ALPS TDHD1-204A)", BUDGET_FS_ACTIVY); +MAKE_BUDGET_INFO(omicom, "Omicom S2 PCI", BUDGET_TT); +MAKE_BUDGET_INFO(sylt, "Philips Semi Sylt PCI", BUDGET_TT_HW_DISEQC); + +static const struct pci_device_id pci_tbl[] = { + MAKE_EXTENSION_PCI(ttbs, 0x13c2, 0x1003), + MAKE_EXTENSION_PCI(ttbc, 0x13c2, 0x1004), + MAKE_EXTENSION_PCI(ttbt, 0x13c2, 0x1005), + MAKE_EXTENSION_PCI(satel, 0x13c2, 0x1013), + MAKE_EXTENSION_PCI(ttbs, 0x13c2, 0x1016), + MAKE_EXTENSION_PCI(ttbs1401, 0x13c2, 0x1018), + MAKE_EXTENSION_PCI(tt1600, 0x13c2, 0x101c), + MAKE_EXTENSION_PCI(fsacs1,0x1131, 0x4f60), + MAKE_EXTENSION_PCI(fsacs0,0x1131, 0x4f61), + MAKE_EXTENSION_PCI(fsact1, 0x1131, 0x5f60), + MAKE_EXTENSION_PCI(fsact, 0x1131, 0x5f61), + MAKE_EXTENSION_PCI(omicom, 0x14c4, 0x1020), + MAKE_EXTENSION_PCI(sylt, 0x1131, 0x4f52), + { + .vendor = 0, + } +}; + +MODULE_DEVICE_TABLE(pci, pci_tbl); + +static struct saa7146_extension budget_extension = { + .name = "budget dvb", + .flags = SAA7146_USE_I2C_IRQ, + + .module = THIS_MODULE, + .pci_tbl = pci_tbl, + .attach = budget_attach, + .detach = budget_detach, + + .irq_mask = MASK_10, + .irq_func = ttpci_budget_irq10_handler, +}; + +static int __init budget_init(void) +{ + return saa7146_register_extension(&budget_extension); +} + +static void __exit budget_exit(void) +{ + saa7146_unregister_extension(&budget_extension); +} + +module_init(budget_init); +module_exit(budget_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, Michael Hunold, others"); +MODULE_DESCRIPTION("driver for the SAA7146 based so-called budget PCI DVB cards by Siemens, Technotrend, Hauppauge"); diff --git a/drivers/staging/media/deprecated/saa7146/ttpci/budget.h b/drivers/staging/media/deprecated/saa7146/ttpci/budget.h new file mode 100644 index 000000000000..82cc0df492b3 --- /dev/null +++ b/drivers/staging/media/deprecated/saa7146/ttpci/budget.h @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __BUDGET_DVB__ +#define __BUDGET_DVB__ + +#include <media/dvb_frontend.h> +#include <media/dvbdev.h> +#include <media/demux.h> +#include <media/dvb_demux.h> +#include <media/dmxdev.h> +#include <media/dvb_net.h> + +#include <linux/module.h> +#include <linux/mutex.h> + +#include "../common/saa7146.h" + +extern int budget_debug; + +#ifdef dprintk +#undef dprintk +#endif + +#define dprintk(level, fmt, arg...) do { \ + if (level & budget_debug) \ + printk(KERN_DEBUG KBUILD_MODNAME ": %s(): " fmt, \ + __func__, ##arg); \ +} while (0) + +#define TS_SIZE 188 + +struct budget_info { + char *name; + int type; +}; + +/* place to store all the necessary device information */ +struct budget { + + /* devices */ + struct dvb_device dvb_dev; + struct dvb_net dvb_net; + + struct saa7146_dev *dev; + + struct i2c_adapter i2c_adap; + struct budget_info *card; + + unsigned char *grabbing; + struct saa7146_pgtable pt; + + struct tasklet_struct fidb_tasklet; + struct tasklet_struct vpe_tasklet; + + struct dmxdev dmxdev; + struct dvb_demux demux; + + struct dmx_frontend hw_frontend; + struct dmx_frontend mem_frontend; + + int ci_present; + int video_port; + + u32 buffer_width; + u32 buffer_height; + u32 buffer_size; + u32 buffer_warning_threshold; + u32 buffer_warnings; + unsigned long buffer_warning_time; + + u32 ttbp; + int feeding; + + spinlock_t feedlock; + + spinlock_t debilock; + + struct dvb_adapter dvb_adapter; + struct dvb_frontend *dvb_frontend; + int (*read_fe_status)(struct dvb_frontend *fe, enum fe_status *status); + int fe_synced; + + void *priv; +}; + +#define MAKE_BUDGET_INFO(x_var,x_name,x_type) \ +static struct budget_info x_var ## _info = { \ + .name=x_name, \ + .type=x_type }; \ +static struct saa7146_pci_extension_data x_var = { \ + .ext_priv = &x_var ## _info, \ + .ext = &budget_extension }; + +#define BUDGET_TT 0 +#define BUDGET_TT_HW_DISEQC 1 +#define BUDGET_PATCH 3 +#define BUDGET_FS_ACTIVY 4 +#define BUDGET_CIN1200S 5 +#define BUDGET_CIN1200C 6 +#define BUDGET_CIN1200T 7 +#define BUDGET_KNC1S 8 +#define BUDGET_KNC1C 9 +#define BUDGET_KNC1T 10 +#define BUDGET_KNC1SP 11 +#define BUDGET_KNC1CP 12 +#define BUDGET_KNC1TP 13 +#define BUDGET_TVSTAR 14 +#define BUDGET_CIN1200C_MK3 15 +#define BUDGET_KNC1C_MK3 16 +#define BUDGET_KNC1CP_MK3 17 +#define BUDGET_KNC1S2 18 +#define BUDGET_KNC1C_TDA10024 19 + +#define BUDGET_VIDEO_PORTA 0 +#define BUDGET_VIDEO_PORTB 1 + +extern int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, + struct saa7146_pci_extension_data *info, + struct module *owner, short *adapter_nums); +extern void ttpci_budget_init_hooks(struct budget *budget); +extern int ttpci_budget_deinit(struct budget *budget); +extern void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 * isr); +extern void ttpci_budget_set_video_port(struct saa7146_dev *dev, int video_port); +extern int ttpci_budget_debiread(struct budget *budget, u32 config, int addr, int count, + int uselocks, int nobusyloop); +extern int ttpci_budget_debiwrite(struct budget *budget, u32 config, int addr, int count, u32 value, + int uselocks, int nobusyloop); + +#endif diff --git a/drivers/staging/media/stkwebcam/Kconfig b/drivers/staging/media/deprecated/stkwebcam/Kconfig index 4450403dff41..4450403dff41 100644 --- a/drivers/staging/media/stkwebcam/Kconfig +++ b/drivers/staging/media/deprecated/stkwebcam/Kconfig diff --git a/drivers/staging/media/stkwebcam/Makefile b/drivers/staging/media/deprecated/stkwebcam/Makefile index 17ad7b6f43d0..17ad7b6f43d0 100644 --- a/drivers/staging/media/stkwebcam/Makefile +++ b/drivers/staging/media/deprecated/stkwebcam/Makefile diff --git a/drivers/staging/media/stkwebcam/TODO b/drivers/staging/media/deprecated/stkwebcam/TODO index 735304a72729..735304a72729 100644 --- a/drivers/staging/media/stkwebcam/TODO +++ b/drivers/staging/media/deprecated/stkwebcam/TODO diff --git a/drivers/staging/media/stkwebcam/stk-sensor.c b/drivers/staging/media/deprecated/stkwebcam/stk-sensor.c index 94aa6a27f934..94aa6a27f934 100644 --- a/drivers/staging/media/stkwebcam/stk-sensor.c +++ b/drivers/staging/media/deprecated/stkwebcam/stk-sensor.c diff --git a/drivers/staging/media/stkwebcam/stk-webcam.c b/drivers/staging/media/deprecated/stkwebcam/stk-webcam.c index 787edb3d47c2..787edb3d47c2 100644 --- a/drivers/staging/media/stkwebcam/stk-webcam.c +++ b/drivers/staging/media/deprecated/stkwebcam/stk-webcam.c diff --git a/drivers/staging/media/stkwebcam/stk-webcam.h b/drivers/staging/media/deprecated/stkwebcam/stk-webcam.h index 136decffe9ce..136decffe9ce 100644 --- a/drivers/staging/media/stkwebcam/stk-webcam.h +++ b/drivers/staging/media/deprecated/stkwebcam/stk-webcam.h diff --git a/drivers/staging/media/deprecated/tm6000/Kconfig b/drivers/staging/media/deprecated/tm6000/Kconfig new file mode 100644 index 000000000000..73d72e49eb28 --- /dev/null +++ b/drivers/staging/media/deprecated/tm6000/Kconfig @@ -0,0 +1,37 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_TM6000 + tristate "TV Master TM5600/6000/6010 driver (DEPRECATED)" + depends on VIDEO_DEV && I2C && INPUT && RC_CORE && USB + select VIDEO_TUNER + select MEDIA_TUNER_XC2028 + select MEDIA_TUNER_XC5000 + select VIDEOBUF_VMALLOC + help + Support for TM5600/TM6000/TM6010 USB Device + + Since these cards have no MPEG decoder onboard, they transmit + only compressed MPEG data over the usb bus, so you need + an external software decoder to watch TV on your computer. + + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + + Say Y if you own such a device and want to use it. + +config VIDEO_TM6000_ALSA + tristate "TV Master TM5600/6000/6010 audio support" + depends on VIDEO_TM6000 && SND + select SND_PCM + help + This is a video4linux driver for direct (DMA) audio for + TM5600/TM6000/TM6010 USB Devices. + + To compile this driver as a module, choose M here: the + module will be called tm6000-alsa. + +config VIDEO_TM6000_DVB + tristate "DVB Support for tm6000 based TV cards" + depends on VIDEO_TM6000 && DVB_CORE && USB + select DVB_ZL10353 + help + This adds support for DVB cards based on the tm5600/tm6000 chip. diff --git a/drivers/staging/media/deprecated/tm6000/Makefile b/drivers/staging/media/deprecated/tm6000/Makefile new file mode 100644 index 000000000000..75247a02a485 --- /dev/null +++ b/drivers/staging/media/deprecated/tm6000/Makefile @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0 +tm6000-y := tm6000-cards.o \ + tm6000-core.o \ + tm6000-i2c.o \ + tm6000-video.o \ + tm6000-stds.o \ + tm6000-input.o + +obj-$(CONFIG_VIDEO_TM6000) += tm6000.o +obj-$(CONFIG_VIDEO_TM6000_ALSA) += tm6000-alsa.o +obj-$(CONFIG_VIDEO_TM6000_DVB) += tm6000-dvb.o + +ccflags-y += -I $(srctree)/drivers/media/tuners +ccflags-y += -I $(srctree)/drivers/media/dvb-frontends diff --git a/drivers/staging/media/deprecated/tm6000/TODO b/drivers/staging/media/deprecated/tm6000/TODO new file mode 100644 index 000000000000..ecb30a429689 --- /dev/null +++ b/drivers/staging/media/deprecated/tm6000/TODO @@ -0,0 +1,7 @@ +This is one of the few drivers still not using the vb2 +framework, so this driver is now deprecated with the intent of +removing it altogether by the beginning of 2023. + +In order to keep this driver it has to be converted to vb2. +If someone is interested in doing this work, then contact the +linux-media mailinglist (https://linuxtv.org/lists.php). diff --git a/drivers/staging/media/deprecated/tm6000/tm6000-alsa.c b/drivers/staging/media/deprecated/tm6000/tm6000-alsa.c new file mode 100644 index 000000000000..a19a46770c2b --- /dev/null +++ b/drivers/staging/media/deprecated/tm6000/tm6000-alsa.c @@ -0,0 +1,440 @@ +// SPDX-License-Identifier: GPL-2.0 +// Support for audio capture for tm5600/6000/6010 +// Copyright (c) 2007-2008 Mauro Carvalho Chehab <mchehab@kernel.org> +// +// Based on cx88-alsa.c + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/usb.h> +#include <linux/slab.h> + +#include <linux/delay.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/control.h> +#include <sound/initval.h> + + +#include "tm6000.h" +#include "tm6000-regs.h" + +#undef dprintk + +#define dprintk(level, fmt, arg...) do { \ + if (debug >= level) \ + printk(KERN_INFO "%s/1: " fmt, chip->core->name , ## arg); \ + } while (0) + +/**************************************************************************** + Module global static vars + ****************************************************************************/ + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ + +static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; + +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable tm6000x soundcard. default enabled."); + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for tm6000x capture interface(s)."); + + +/**************************************************************************** + Module macros + ****************************************************************************/ + +MODULE_DESCRIPTION("ALSA driver module for tm5600/tm6000/tm6010 based TV cards"); +MODULE_AUTHOR("Mauro Carvalho Chehab"); +MODULE_LICENSE("GPL v2"); +static unsigned int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debug messages"); + +/**************************************************************************** + Module specific functions + ****************************************************************************/ + +/* + * BOARD Specific: Sets audio DMA + */ + +static int _tm6000_start_audio_dma(struct snd_tm6000_card *chip) +{ + struct tm6000_core *core = chip->core; + + dprintk(1, "Starting audio DMA\n"); + + /* Enables audio */ + tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_IF, 0x40, 0x40); + + tm6000_set_audio_bitrate(core, 48000); + + return 0; +} + +/* + * BOARD Specific: Resets audio DMA + */ +static int _tm6000_stop_audio_dma(struct snd_tm6000_card *chip) +{ + struct tm6000_core *core = chip->core; + + dprintk(1, "Stopping audio DMA\n"); + + /* Disables audio */ + tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_IF, 0x00, 0x40); + + return 0; +} + +/**************************************************************************** + ALSA PCM Interface + ****************************************************************************/ + +/* + * Digital hardware definition + */ +#define DEFAULT_FIFO_SIZE 4096 + +static const struct snd_pcm_hardware snd_tm6000_digital_hw = { + .info = SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .period_bytes_min = 64, + .period_bytes_max = 12544, + .periods_min = 2, + .periods_max = 98, + .buffer_bytes_max = 62720 * 8, +}; + +/* + * audio pcm capture open callback + */ +static int snd_tm6000_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + int err; + + err = snd_pcm_hw_constraint_pow2(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIODS); + if (err < 0) + goto _error; + + chip->substream = substream; + + runtime->hw = snd_tm6000_digital_hw; + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + + return 0; +_error: + dprintk(1, "Error opening PCM!\n"); + return err; +} + +/* + * audio close callback + */ +static int snd_tm6000_close(struct snd_pcm_substream *substream) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + struct tm6000_core *core = chip->core; + + if (atomic_read(&core->stream_started) > 0) { + atomic_set(&core->stream_started, 0); + schedule_work(&core->wq_trigger); + } + + return 0; +} + +static int tm6000_fillbuf(struct tm6000_core *core, char *buf, int size) +{ + struct snd_tm6000_card *chip = core->adev; + struct snd_pcm_substream *substream = chip->substream; + struct snd_pcm_runtime *runtime; + int period_elapsed = 0; + unsigned int stride, buf_pos; + int length; + + if (atomic_read(&core->stream_started) == 0) + return 0; + + if (!size || !substream) { + dprintk(1, "substream was NULL\n"); + return -EINVAL; + } + + runtime = substream->runtime; + if (!runtime || !runtime->dma_area) { + dprintk(1, "runtime was NULL\n"); + return -EINVAL; + } + + buf_pos = chip->buf_pos; + stride = runtime->frame_bits >> 3; + + if (stride == 0) { + dprintk(1, "stride is zero\n"); + return -EINVAL; + } + + length = size / stride; + if (length == 0) { + dprintk(1, "%s: length was zero\n", __func__); + return -EINVAL; + } + + dprintk(1, "Copying %d bytes at %p[%d] - buf size=%d x %d\n", size, + runtime->dma_area, buf_pos, + (unsigned int)runtime->buffer_size, stride); + + if (buf_pos + length >= runtime->buffer_size) { + unsigned int cnt = runtime->buffer_size - buf_pos; + memcpy(runtime->dma_area + buf_pos * stride, buf, cnt * stride); + memcpy(runtime->dma_area, buf + cnt * stride, + length * stride - cnt * stride); + } else + memcpy(runtime->dma_area + buf_pos * stride, buf, + length * stride); + + snd_pcm_stream_lock(substream); + + chip->buf_pos += length; + if (chip->buf_pos >= runtime->buffer_size) + chip->buf_pos -= runtime->buffer_size; + + chip->period_pos += length; + if (chip->period_pos >= runtime->period_size) { + chip->period_pos -= runtime->period_size; + period_elapsed = 1; + } + + snd_pcm_stream_unlock(substream); + + if (period_elapsed) + snd_pcm_period_elapsed(substream); + + return 0; +} + +/* + * prepare callback + */ +static int snd_tm6000_prepare(struct snd_pcm_substream *substream) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + + chip->buf_pos = 0; + chip->period_pos = 0; + + return 0; +} + + +/* + * trigger callback + */ +static void audio_trigger(struct work_struct *work) +{ + struct tm6000_core *core = container_of(work, struct tm6000_core, + wq_trigger); + struct snd_tm6000_card *chip = core->adev; + + if (atomic_read(&core->stream_started)) { + dprintk(1, "starting capture"); + _tm6000_start_audio_dma(chip); + } else { + dprintk(1, "stopping capture"); + _tm6000_stop_audio_dma(chip); + } +} + +static int snd_tm6000_card_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + struct tm6000_core *core = chip->core; + int err = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_START: + atomic_set(&core->stream_started, 1); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + atomic_set(&core->stream_started, 0); + break; + default: + err = -EINVAL; + break; + } + schedule_work(&core->wq_trigger); + + return err; +} +/* + * pointer callback + */ +static snd_pcm_uframes_t snd_tm6000_pointer(struct snd_pcm_substream *substream) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + + return chip->buf_pos; +} + +/* + * operators + */ +static const struct snd_pcm_ops snd_tm6000_pcm_ops = { + .open = snd_tm6000_pcm_open, + .close = snd_tm6000_close, + .prepare = snd_tm6000_prepare, + .trigger = snd_tm6000_card_trigger, + .pointer = snd_tm6000_pointer, +}; + +/* + * create a PCM device + */ + +/* FIXME: Control interface - How to control volume/mute? */ + +/**************************************************************************** + Basic Flow for Sound Devices + ****************************************************************************/ + +/* + * Alsa Constructor - Component probe + */ +static int tm6000_audio_init(struct tm6000_core *dev) +{ + struct snd_card *card; + struct snd_tm6000_card *chip; + int rc; + static int devnr; + char component[14]; + struct snd_pcm *pcm; + + if (!dev) + return 0; + + if (devnr >= SNDRV_CARDS) + return -ENODEV; + + if (!enable[devnr]) + return -ENOENT; + + rc = snd_card_new(&dev->udev->dev, index[devnr], "tm6000", + THIS_MODULE, 0, &card); + if (rc < 0) { + snd_printk(KERN_ERR "cannot create card instance %d\n", devnr); + return rc; + } + strscpy(card->driver, "tm6000-alsa", sizeof(card->driver)); + strscpy(card->shortname, "TM5600/60x0", sizeof(card->shortname)); + sprintf(card->longname, "TM5600/60x0 Audio at bus %d device %d", + dev->udev->bus->busnum, dev->udev->devnum); + + sprintf(component, "USB%04x:%04x", + le16_to_cpu(dev->udev->descriptor.idVendor), + le16_to_cpu(dev->udev->descriptor.idProduct)); + snd_component_add(card, component); + + chip = kzalloc(sizeof(struct snd_tm6000_card), GFP_KERNEL); + if (!chip) { + rc = -ENOMEM; + goto error; + } + + chip->core = dev; + chip->card = card; + dev->adev = chip; + spin_lock_init(&chip->reg_lock); + + rc = snd_pcm_new(card, "TM6000 Audio", 0, 0, 1, &pcm); + if (rc < 0) + goto error_chip; + + pcm->info_flags = 0; + pcm->private_data = chip; + strscpy(pcm->name, "Trident TM5600/60x0", sizeof(pcm->name)); + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_tm6000_pcm_ops); + snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0); + + INIT_WORK(&dev->wq_trigger, audio_trigger); + rc = snd_card_register(card); + if (rc < 0) + goto error_chip; + + dprintk(1, "Registered audio driver for %s\n", card->longname); + + return 0; + +error_chip: + kfree(chip); + dev->adev = NULL; +error: + snd_card_free(card); + return rc; +} + +static int tm6000_audio_fini(struct tm6000_core *dev) +{ + struct snd_tm6000_card *chip; + + if (!dev) + return 0; + chip = dev->adev; + + if (!chip) + return 0; + + if (!chip->card) + return 0; + + snd_card_free(chip->card); + chip->card = NULL; + kfree(chip); + dev->adev = NULL; + + return 0; +} + +static struct tm6000_ops audio_ops = { + .type = TM6000_AUDIO, + .name = "TM6000 Audio Extension", + .init = tm6000_audio_init, + .fini = tm6000_audio_fini, + .fillbuf = tm6000_fillbuf, +}; + +static int __init tm6000_alsa_register(void) +{ + return tm6000_register_extension(&audio_ops); +} + +static void __exit tm6000_alsa_unregister(void) +{ + tm6000_unregister_extension(&audio_ops); +} + +module_init(tm6000_alsa_register); +module_exit(tm6000_alsa_unregister); diff --git a/drivers/staging/media/deprecated/tm6000/tm6000-cards.c b/drivers/staging/media/deprecated/tm6000/tm6000-cards.c new file mode 100644 index 000000000000..98f4a63adc2a --- /dev/null +++ b/drivers/staging/media/deprecated/tm6000/tm6000-cards.c @@ -0,0 +1,1397 @@ +// SPDX-License-Identifier: GPL-2.0 +// tm6000-cards.c - driver for TM5600/TM6000/TM6010 USB video capture devices +// +// Copyright (c) 2006-2007 Mauro Carvalho Chehab <mchehab@kernel.org> + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/usb.h> +#include <linux/slab.h> +#include <media/v4l2-common.h> +#include <media/tuner.h> +#include <media/i2c/tvaudio.h> +#include <media/rc-map.h> + +#include "tm6000.h" +#include "tm6000-regs.h" +#include "xc2028.h" +#include "xc5000.h" + +#define TM6000_BOARD_UNKNOWN 0 +#define TM5600_BOARD_GENERIC 1 +#define TM6000_BOARD_GENERIC 2 +#define TM6010_BOARD_GENERIC 3 +#define TM5600_BOARD_10MOONS_UT821 4 +#define TM5600_BOARD_10MOONS_UT330 5 +#define TM6000_BOARD_ADSTECH_DUAL_TV 6 +#define TM6000_BOARD_FREECOM_AND_SIMILAR 7 +#define TM6000_BOARD_ADSTECH_MINI_DUAL_TV 8 +#define TM6010_BOARD_HAUPPAUGE_900H 9 +#define TM6010_BOARD_BEHOLD_WANDER 10 +#define TM6010_BOARD_BEHOLD_VOYAGER 11 +#define TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE 12 +#define TM6010_BOARD_TWINHAN_TU501 13 +#define TM6010_BOARD_BEHOLD_WANDER_LITE 14 +#define TM6010_BOARD_BEHOLD_VOYAGER_LITE 15 +#define TM5600_BOARD_TERRATEC_GRABSTER 16 + +#define is_generic(model) ((model == TM6000_BOARD_UNKNOWN) || \ + (model == TM5600_BOARD_GENERIC) || \ + (model == TM6000_BOARD_GENERIC) || \ + (model == TM6010_BOARD_GENERIC)) + +#define TM6000_MAXBOARDS 16 +static unsigned int card[] = {[0 ... (TM6000_MAXBOARDS - 1)] = UNSET }; + +module_param_array(card, int, NULL, 0444); + +static unsigned long tm6000_devused; + + +struct tm6000_board { + char *name; + char eename[16]; /* EEPROM name */ + unsigned eename_size; /* size of EEPROM name */ + unsigned eename_pos; /* Position where it appears at ROM */ + + struct tm6000_capabilities caps; + + enum tm6000_devtype type; /* variant of the chipset */ + int tuner_type; /* type of the tuner */ + int tuner_addr; /* tuner address */ + int demod_addr; /* demodulator address */ + + struct tm6000_gpio gpio; + + struct tm6000_input vinput[3]; + struct tm6000_input rinput; + + char *ir_codes; +}; + +static struct tm6000_board tm6000_boards[] = { + [TM6000_BOARD_UNKNOWN] = { + .name = "Unknown tm6000 video grabber", + .caps = { + .has_tuner = 1, + .has_eeprom = 1, + }, + .gpio = { + .tuner_reset = TM6000_GPIO_1, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM5600_BOARD_GENERIC] = { + .name = "Generic tm5600 board", + .type = TM5600, + .tuner_type = TUNER_XC2028, + .tuner_addr = 0xc2 >> 1, + .caps = { + .has_tuner = 1, + .has_eeprom = 1, + }, + .gpio = { + .tuner_reset = TM6000_GPIO_1, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6000_BOARD_GENERIC] = { + .name = "Generic tm6000 board", + .tuner_type = TUNER_XC2028, + .tuner_addr = 0xc2 >> 1, + .caps = { + .has_tuner = 1, + .has_eeprom = 1, + }, + .gpio = { + .tuner_reset = TM6000_GPIO_1, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6010_BOARD_GENERIC] = { + .name = "Generic tm6010 board", + .type = TM6010, + .tuner_type = TUNER_XC2028, + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + .has_remote = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_2, + .tuner_on = TM6010_GPIO_3, + .demod_reset = TM6010_GPIO_1, + .demod_on = TM6010_GPIO_4, + .power_led = TM6010_GPIO_7, + .dvb_led = TM6010_GPIO_5, + .ir = TM6010_GPIO_0, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_SIF1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM5600_BOARD_10MOONS_UT821] = { + .name = "10Moons UT 821", + .tuner_type = TUNER_XC2028, + .eename = { '1', '0', 'M', 'O', 'O', 'N', 'S', '5', '6', '0', '0', 0xff, 0x45, 0x5b}, + .eename_size = 14, + .eename_pos = 0x14, + .type = TM5600, + .tuner_addr = 0xc2 >> 1, + .caps = { + .has_tuner = 1, + .has_eeprom = 1, + }, + .gpio = { + .tuner_reset = TM6000_GPIO_1, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM5600_BOARD_10MOONS_UT330] = { + .name = "10Moons UT 330", + .tuner_type = TUNER_PHILIPS_FQ1216AME_MK4, + .tuner_addr = 0xc8 >> 1, + .caps = { + .has_tuner = 1, + .has_dvb = 0, + .has_zl10353 = 0, + .has_eeprom = 1, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6000_BOARD_ADSTECH_DUAL_TV] = { + .name = "ADSTECH Dual TV USB", + .tuner_type = TUNER_XC2028, + .tuner_addr = 0xc8 >> 1, + .caps = { + .has_tuner = 1, + .has_tda9874 = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6000_BOARD_FREECOM_AND_SIMILAR] = { + .name = "Freecom Hybrid Stick / Moka DVB-T Receiver Dual", + .tuner_type = TUNER_XC2028, /* has a XC3028 */ + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 0, + .has_remote = 1, + }, + .gpio = { + .tuner_reset = TM6000_GPIO_4, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6000_BOARD_ADSTECH_MINI_DUAL_TV] = { + .name = "ADSTECH Mini Dual TV USB", + .tuner_type = TUNER_XC2028, /* has a XC3028 */ + .tuner_addr = 0xc8 >> 1, + .demod_addr = 0x1e >> 1, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 0, + }, + .gpio = { + .tuner_reset = TM6000_GPIO_4, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6010_BOARD_HAUPPAUGE_900H] = { + .name = "Hauppauge WinTV HVR-900H / WinTV USB2-Stick", + .eename = { 'H', 0, 'V', 0, 'R', 0, '9', 0, '0', 0, '0', 0, 'H', 0 }, + .eename_size = 14, + .eename_pos = 0x42, + .tuner_type = TUNER_XC2028, /* has a XC3028 */ + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .type = TM6010, + .ir_codes = RC_MAP_HAUPPAUGE, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + .has_remote = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_2, + .tuner_on = TM6010_GPIO_3, + .demod_reset = TM6010_GPIO_1, + .demod_on = TM6010_GPIO_4, + .power_led = TM6010_GPIO_7, + .dvb_led = TM6010_GPIO_5, + .ir = TM6010_GPIO_0, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_SIF1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6010_BOARD_BEHOLD_WANDER] = { + .name = "Beholder Wander DVB-T/TV/FM USB2.0", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + .has_remote = 1, + .has_radio = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_0, + .demod_reset = TM6010_GPIO_1, + .power_led = TM6010_GPIO_6, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_SIF1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + .rinput = { + .type = TM6000_INPUT_RADIO, + .amux = TM6000_AMUX_ADC1, + }, + }, + [TM6010_BOARD_BEHOLD_VOYAGER] = { + .name = "Beholder Voyager TV/FM USB2.0", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0xc2 >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 0, + .has_zl10353 = 0, + .has_eeprom = 1, + .has_remote = 1, + .has_radio = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_0, + .power_led = TM6010_GPIO_6, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_SIF1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + .rinput = { + .type = TM6000_INPUT_RADIO, + .amux = TM6000_AMUX_ADC1, + }, + }, + [TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE] = { + .name = "Terratec Cinergy Hybrid XE / Cinergy Hybrid-Stick", + .tuner_type = TUNER_XC2028, /* has a XC3028 */ + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + .has_remote = 1, + .has_radio = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_2, + .tuner_on = TM6010_GPIO_3, + .demod_reset = TM6010_GPIO_1, + .demod_on = TM6010_GPIO_4, + .power_led = TM6010_GPIO_7, + .dvb_led = TM6010_GPIO_5, + .ir = TM6010_GPIO_0, + }, + .ir_codes = RC_MAP_NEC_TERRATEC_CINERGY_XS, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_SIF1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + .rinput = { + .type = TM6000_INPUT_RADIO, + .amux = TM6000_AMUX_SIF1, + }, + }, + [TM5600_BOARD_TERRATEC_GRABSTER] = { + .name = "Terratec Grabster AV 150/250 MX", + .type = TM5600, + .tuner_type = TUNER_ABSENT, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_ADC1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6010_BOARD_TWINHAN_TU501] = { + .name = "Twinhan TU501(704D1)", + .tuner_type = TUNER_XC2028, /* has a XC3028 */ + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + .has_remote = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_2, + .tuner_on = TM6010_GPIO_3, + .demod_reset = TM6010_GPIO_1, + .demod_on = TM6010_GPIO_4, + .power_led = TM6010_GPIO_7, + .dvb_led = TM6010_GPIO_5, + .ir = TM6010_GPIO_0, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_SIF1, + }, { + .type = TM6000_INPUT_COMPOSITE1, + .vmux = TM6000_VMUX_VIDEO_A, + .amux = TM6000_AMUX_ADC2, + }, { + .type = TM6000_INPUT_SVIDEO, + .vmux = TM6000_VMUX_VIDEO_AB, + .amux = TM6000_AMUX_ADC2, + }, + }, + }, + [TM6010_BOARD_BEHOLD_WANDER_LITE] = { + .name = "Beholder Wander Lite DVB-T/TV/FM USB2.0", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + .has_remote = 0, + .has_radio = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_0, + .demod_reset = TM6010_GPIO_1, + .power_led = TM6010_GPIO_6, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_SIF1, + }, + }, + .rinput = { + .type = TM6000_INPUT_RADIO, + .amux = TM6000_AMUX_ADC1, + }, + }, + [TM6010_BOARD_BEHOLD_VOYAGER_LITE] = { + .name = "Beholder Voyager Lite TV/FM USB2.0", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0xc2 >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 0, + .has_zl10353 = 0, + .has_eeprom = 1, + .has_remote = 0, + .has_radio = 1, + }, + .gpio = { + .tuner_reset = TM6010_GPIO_0, + .power_led = TM6010_GPIO_6, + }, + .vinput = { { + .type = TM6000_INPUT_TV, + .vmux = TM6000_VMUX_VIDEO_B, + .amux = TM6000_AMUX_SIF1, + }, + }, + .rinput = { + .type = TM6000_INPUT_RADIO, + .amux = TM6000_AMUX_ADC1, + }, + }, +}; + +/* table of devices that work with this driver */ +static const struct usb_device_id tm6000_id_table[] = { + { USB_DEVICE(0x6000, 0x0001), .driver_info = TM5600_BOARD_GENERIC }, + { USB_DEVICE(0x6000, 0x0002), .driver_info = TM6010_BOARD_GENERIC }, + { USB_DEVICE(0x06e1, 0xf332), .driver_info = TM6000_BOARD_ADSTECH_DUAL_TV }, + { USB_DEVICE(0x14aa, 0x0620), .driver_info = TM6000_BOARD_FREECOM_AND_SIMILAR }, + { USB_DEVICE(0x06e1, 0xb339), .driver_info = TM6000_BOARD_ADSTECH_MINI_DUAL_TV }, + { USB_DEVICE(0x2040, 0x6600), .driver_info = TM6010_BOARD_HAUPPAUGE_900H }, + { USB_DEVICE(0x2040, 0x6601), .driver_info = TM6010_BOARD_HAUPPAUGE_900H }, + { USB_DEVICE(0x2040, 0x6610), .driver_info = TM6010_BOARD_HAUPPAUGE_900H }, + { USB_DEVICE(0x2040, 0x6611), .driver_info = TM6010_BOARD_HAUPPAUGE_900H }, + { USB_DEVICE(0x6000, 0xdec0), .driver_info = TM6010_BOARD_BEHOLD_WANDER }, + { USB_DEVICE(0x6000, 0xdec1), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER }, + { USB_DEVICE(0x0ccd, 0x0086), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE }, + { USB_DEVICE(0x0ccd, 0x00A5), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE }, + { USB_DEVICE(0x0ccd, 0x0079), .driver_info = TM5600_BOARD_TERRATEC_GRABSTER }, + { USB_DEVICE(0x13d3, 0x3240), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, + { USB_DEVICE(0x13d3, 0x3241), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, + { USB_DEVICE(0x13d3, 0x3243), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, + { USB_DEVICE(0x13d3, 0x3264), .driver_info = TM6010_BOARD_TWINHAN_TU501 }, + { USB_DEVICE(0x6000, 0xdec2), .driver_info = TM6010_BOARD_BEHOLD_WANDER_LITE }, + { USB_DEVICE(0x6000, 0xdec3), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER_LITE }, + { } +}; +MODULE_DEVICE_TABLE(usb, tm6000_id_table); + +/* Control power led for show some activity */ +void tm6000_flash_led(struct tm6000_core *dev, u8 state) +{ + /* Power LED unconfigured */ + if (!dev->gpio.power_led) + return; + + /* ON Power LED */ + if (state) { + switch (dev->model) { + case TM6010_BOARD_HAUPPAUGE_900H: + case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: + case TM6010_BOARD_TWINHAN_TU501: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.power_led, 0x00); + break; + case TM6010_BOARD_BEHOLD_WANDER: + case TM6010_BOARD_BEHOLD_VOYAGER: + case TM6010_BOARD_BEHOLD_WANDER_LITE: + case TM6010_BOARD_BEHOLD_VOYAGER_LITE: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.power_led, 0x01); + break; + } + } + /* OFF Power LED */ + else { + switch (dev->model) { + case TM6010_BOARD_HAUPPAUGE_900H: + case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: + case TM6010_BOARD_TWINHAN_TU501: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.power_led, 0x01); + break; + case TM6010_BOARD_BEHOLD_WANDER: + case TM6010_BOARD_BEHOLD_VOYAGER: + case TM6010_BOARD_BEHOLD_WANDER_LITE: + case TM6010_BOARD_BEHOLD_VOYAGER_LITE: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.power_led, 0x00); + break; + } + } +} + +/* Tuner callback to provide the proper gpio changes needed for xc5000 */ +int tm6000_xc5000_callback(void *ptr, int component, int command, int arg) +{ + int rc = 0; + struct tm6000_core *dev = ptr; + + if (dev->tuner_type != TUNER_XC5000) + return 0; + + switch (command) { + case XC5000_TUNER_RESET: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + msleep(15); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x00); + msleep(15); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + break; + } + return rc; +} +EXPORT_SYMBOL_GPL(tm6000_xc5000_callback); + +/* Tuner callback to provide the proper gpio changes needed for xc2028 */ + +int tm6000_tuner_callback(void *ptr, int component, int command, int arg) +{ + int rc = 0; + struct tm6000_core *dev = ptr; + + if (dev->tuner_type != TUNER_XC2028) + return 0; + + switch (command) { + case XC2028_RESET_CLK: + tm6000_ir_wait(dev, 0); + + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, + 0x02, arg); + msleep(10); + rc = tm6000_i2c_reset(dev, 10); + break; + case XC2028_TUNER_RESET: + /* Reset codes during load firmware */ + switch (arg) { + case 0: + /* newer tuner can faster reset */ + switch (dev->model) { + case TM5600_BOARD_10MOONS_UT821: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + 0x300, 0x01); + msleep(10); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x00); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + 0x300, 0x00); + msleep(10); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + 0x300, 0x01); + break; + case TM6010_BOARD_HAUPPAUGE_900H: + case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: + case TM6010_BOARD_TWINHAN_TU501: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + msleep(60); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x00); + msleep(75); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + msleep(60); + break; + default: + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x00); + msleep(130); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + msleep(130); + break; + } + + tm6000_ir_wait(dev, 1); + break; + case 1: + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, + 0x02, 0x01); + msleep(10); + break; + case 2: + rc = tm6000_i2c_reset(dev, 100); + break; + } + break; + case XC2028_I2C_FLUSH: + tm6000_set_reg(dev, REQ_50_SET_START, 0, 0); + tm6000_set_reg(dev, REQ_51_SET_STOP, 0, 0); + break; + } + return rc; +} +EXPORT_SYMBOL_GPL(tm6000_tuner_callback); + +int tm6000_cards_setup(struct tm6000_core *dev) +{ + /* + * Board-specific initialization sequence. Handles all GPIO + * initialization sequences that are board-specific. + * Up to now, all found devices use GPIO1 and GPIO4 at the same way. + * Probably, they're all based on some reference device. Due to that, + * there's a common routine at the end to handle those GPIO's. Devices + * that use different pinups or init sequences can just return at + * the board-specific session. + */ + switch (dev->model) { + case TM6010_BOARD_HAUPPAUGE_900H: + case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: + case TM6010_BOARD_TWINHAN_TU501: + case TM6010_BOARD_GENERIC: + /* Turn xceive 3028 on */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.tuner_on, 0x01); + msleep(15); + /* Turn zarlink zl10353 on */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x00); + msleep(15); + /* Reset zarlink zl10353 */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x00); + msleep(50); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x01); + msleep(15); + /* Turn zarlink zl10353 off */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x01); + msleep(15); + /* ir ? */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.ir, 0x01); + msleep(15); + /* Power led on (blue) */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x00); + msleep(15); + /* DVB led off (orange) */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.dvb_led, 0x01); + msleep(15); + /* Turn zarlink zl10353 on */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_on, 0x00); + msleep(15); + break; + case TM6010_BOARD_BEHOLD_WANDER: + case TM6010_BOARD_BEHOLD_WANDER_LITE: + /* Power led on (blue) */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01); + msleep(15); + /* Reset zarlink zl10353 */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x00); + msleep(50); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.demod_reset, 0x01); + msleep(15); + break; + case TM6010_BOARD_BEHOLD_VOYAGER: + case TM6010_BOARD_BEHOLD_VOYAGER_LITE: + /* Power led on (blue) */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, dev->gpio.power_led, 0x01); + msleep(15); + break; + default: + break; + } + + /* + * Default initialization. Most of the devices seem to use GPIO1 + * and GPIO4.on the same way, so, this handles the common sequence + * used by most devices. + * If a device uses a different sequence or different GPIO pins for + * reset, just add the code at the board-specific part + */ + + if (dev->gpio.tuner_reset) { + int rc; + int i; + + for (i = 0; i < 2; i++) { + rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x00); + if (rc < 0) { + printk(KERN_ERR "Error %i doing tuner reset\n", rc); + return rc; + } + + msleep(10); /* Just to be conservative */ + rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.tuner_reset, 0x01); + if (rc < 0) { + printk(KERN_ERR "Error %i doing tuner reset\n", rc); + return rc; + } + } + } else { + printk(KERN_ERR "Tuner reset is not configured\n"); + return -1; + } + + msleep(50); + + return 0; +}; + +static void tm6000_config_tuner(struct tm6000_core *dev) +{ + struct tuner_setup tun_setup; + + /* Load tuner module */ + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "tuner", dev->tuner_addr, NULL); + + memset(&tun_setup, 0, sizeof(tun_setup)); + tun_setup.type = dev->tuner_type; + tun_setup.addr = dev->tuner_addr; + + tun_setup.mode_mask = 0; + if (dev->caps.has_tuner) + tun_setup.mode_mask |= (T_ANALOG_TV | T_RADIO); + + switch (dev->tuner_type) { + case TUNER_XC2028: + tun_setup.tuner_callback = tm6000_tuner_callback; + break; + case TUNER_XC5000: + tun_setup.tuner_callback = tm6000_xc5000_callback; + break; + } + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup); + + switch (dev->tuner_type) { + case TUNER_XC2028: { + struct v4l2_priv_tun_config xc2028_cfg; + struct xc2028_ctrl ctl; + + memset(&xc2028_cfg, 0, sizeof(xc2028_cfg)); + memset(&ctl, 0, sizeof(ctl)); + + ctl.demod = XC3028_FE_ZARLINK456; + + xc2028_cfg.tuner = TUNER_XC2028; + xc2028_cfg.priv = &ctl; + + switch (dev->model) { + case TM6010_BOARD_HAUPPAUGE_900H: + case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: + case TM6010_BOARD_TWINHAN_TU501: + ctl.max_len = 80; + ctl.fname = "xc3028L-v36.fw"; + break; + default: + if (dev->dev_type == TM6010) + ctl.fname = "xc3028-v27.fw"; + else + ctl.fname = "xc3028-v24.fw"; + } + + printk(KERN_INFO "Setting firmware parameters for xc2028\n"); + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, + &xc2028_cfg); + + } + break; + case TUNER_XC5000: + { + struct v4l2_priv_tun_config xc5000_cfg; + struct xc5000_config ctl = { + .i2c_address = dev->tuner_addr, + .if_khz = 4570, + .radio_input = XC5000_RADIO_FM1_MONO, + }; + + xc5000_cfg.tuner = TUNER_XC5000; + xc5000_cfg.priv = &ctl; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, + &xc5000_cfg); + } + break; + default: + printk(KERN_INFO "Unknown tuner type. Tuner is not configured.\n"); + break; + } +} + +static int fill_board_specific_data(struct tm6000_core *dev) +{ + int rc; + + dev->dev_type = tm6000_boards[dev->model].type; + dev->tuner_type = tm6000_boards[dev->model].tuner_type; + dev->tuner_addr = tm6000_boards[dev->model].tuner_addr; + + dev->gpio = tm6000_boards[dev->model].gpio; + + dev->ir_codes = tm6000_boards[dev->model].ir_codes; + + dev->demod_addr = tm6000_boards[dev->model].demod_addr; + + dev->caps = tm6000_boards[dev->model].caps; + + dev->vinput[0] = tm6000_boards[dev->model].vinput[0]; + dev->vinput[1] = tm6000_boards[dev->model].vinput[1]; + dev->vinput[2] = tm6000_boards[dev->model].vinput[2]; + dev->rinput = tm6000_boards[dev->model].rinput; + + /* setup per-model quirks */ + switch (dev->model) { + case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: + case TM6010_BOARD_HAUPPAUGE_900H: + dev->quirks |= TM6000_QUIRK_NO_USB_DELAY; + break; + + default: + break; + } + + /* initialize hardware */ + rc = tm6000_init(dev); + if (rc < 0) + return rc; + + return v4l2_device_register(&dev->udev->dev, &dev->v4l2_dev); +} + + +static void use_alternative_detection_method(struct tm6000_core *dev) +{ + int i, model = -1; + + if (!dev->eedata_size) + return; + + for (i = 0; i < ARRAY_SIZE(tm6000_boards); i++) { + if (!tm6000_boards[i].eename_size) + continue; + if (dev->eedata_size < tm6000_boards[i].eename_pos + + tm6000_boards[i].eename_size) + continue; + + if (!memcmp(&dev->eedata[tm6000_boards[i].eename_pos], + tm6000_boards[i].eename, + tm6000_boards[i].eename_size)) { + model = i; + break; + } + } + if (model < 0) { + printk(KERN_INFO "Device has eeprom but is currently unknown\n"); + return; + } + + dev->model = model; + + printk(KERN_INFO "Device identified via eeprom as %s (type = %d)\n", + tm6000_boards[model].name, model); +} + +#if defined(CONFIG_MODULES) && defined(MODULE) +static void request_module_async(struct work_struct *work) +{ + struct tm6000_core *dev = container_of(work, struct tm6000_core, + request_module_wk); + + request_module("tm6000-alsa"); + + if (dev->caps.has_dvb) + request_module("tm6000-dvb"); +} + +static void request_modules(struct tm6000_core *dev) +{ + INIT_WORK(&dev->request_module_wk, request_module_async); + schedule_work(&dev->request_module_wk); +} + +static void flush_request_modules(struct tm6000_core *dev) +{ + flush_work(&dev->request_module_wk); +} +#else +#define request_modules(dev) +#define flush_request_modules(dev) +#endif /* CONFIG_MODULES */ + +static int tm6000_init_dev(struct tm6000_core *dev) +{ + struct v4l2_frequency f; + int rc = 0; + + mutex_init(&dev->lock); + mutex_lock(&dev->lock); + + if (!is_generic(dev->model)) { + rc = fill_board_specific_data(dev); + if (rc < 0) + goto err; + + /* register i2c bus */ + rc = tm6000_i2c_register(dev); + if (rc < 0) + goto err; + } else { + /* register i2c bus */ + rc = tm6000_i2c_register(dev); + if (rc < 0) + goto err; + + use_alternative_detection_method(dev); + + rc = fill_board_specific_data(dev); + if (rc < 0) + goto err; + } + + /* Default values for STD and resolutions */ + dev->width = 720; + dev->height = 480; + dev->norm = V4L2_STD_NTSC_M; + + /* Configure tuner */ + tm6000_config_tuner(dev); + + /* Set video standard */ + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, dev->norm); + + /* Set tuner frequency - also loads firmware on xc2028/xc3028 */ + f.tuner = 0; + f.type = V4L2_TUNER_ANALOG_TV; + f.frequency = 3092; /* 193.25 MHz */ + dev->freq = f.frequency; + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); + + if (dev->caps.has_tda9874) + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "tvaudio", I2C_ADDR_TDA9874, NULL); + + /* register and initialize V4L2 */ + rc = tm6000_v4l2_register(dev); + if (rc < 0) + goto err; + + tm6000_add_into_devlist(dev); + tm6000_init_extension(dev); + + tm6000_ir_init(dev); + + request_modules(dev); + + mutex_unlock(&dev->lock); + return 0; + +err: + mutex_unlock(&dev->lock); + return rc; +} + +/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */ +#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) + +static void get_max_endpoint(struct usb_device *udev, + struct usb_host_interface *alt, + char *msgtype, + struct usb_host_endpoint *curr_e, + struct tm6000_endpoint *tm_ep) +{ + u16 tmp = le16_to_cpu(curr_e->desc.wMaxPacketSize); + unsigned int size = tmp & 0x7ff; + + if (udev->speed == USB_SPEED_HIGH) + size = size * hb_mult(tmp); + + if (size > tm_ep->maxsize) { + tm_ep->endp = curr_e; + tm_ep->maxsize = size; + tm_ep->bInterfaceNumber = alt->desc.bInterfaceNumber; + tm_ep->bAlternateSetting = alt->desc.bAlternateSetting; + + printk(KERN_INFO "tm6000: %s endpoint: 0x%02x (max size=%u bytes)\n", + msgtype, curr_e->desc.bEndpointAddress, + size); + } +} + +/* + * tm6000_usb_probe() + * checks for supported devices + */ +static int tm6000_usb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *usbdev; + struct tm6000_core *dev; + int i, rc; + int nr = 0; + char *speed; + + usbdev = usb_get_dev(interface_to_usbdev(interface)); + + /* Selects the proper interface */ + rc = usb_set_interface(usbdev, 0, 1); + if (rc < 0) + goto report_failure; + + /* Check to see next free device and mark as used */ + nr = find_first_zero_bit(&tm6000_devused, TM6000_MAXBOARDS); + if (nr >= TM6000_MAXBOARDS) { + printk(KERN_ERR "tm6000: Supports only %i tm60xx boards.\n", TM6000_MAXBOARDS); + rc = -ENOMEM; + goto put_device; + } + + /* Create and initialize dev struct */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + rc = -ENOMEM; + goto put_device; + } + spin_lock_init(&dev->slock); + mutex_init(&dev->usb_lock); + + /* Increment usage count */ + set_bit(nr, &tm6000_devused); + snprintf(dev->name, 29, "tm6000 #%d", nr); + + dev->model = id->driver_info; + if (card[nr] < ARRAY_SIZE(tm6000_boards)) + dev->model = card[nr]; + + dev->udev = usbdev; + dev->devno = nr; + + switch (usbdev->speed) { + case USB_SPEED_LOW: + speed = "1.5"; + break; + case USB_SPEED_UNKNOWN: + case USB_SPEED_FULL: + speed = "12"; + break; + case USB_SPEED_HIGH: + speed = "480"; + break; + default: + speed = "unknown"; + } + + /* Get endpoints */ + for (i = 0; i < interface->num_altsetting; i++) { + int ep; + + for (ep = 0; ep < interface->altsetting[i].desc.bNumEndpoints; ep++) { + struct usb_host_endpoint *e; + int dir_out; + + e = &interface->altsetting[i].endpoint[ep]; + + dir_out = ((e->desc.bEndpointAddress & + USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT); + + printk(KERN_INFO "tm6000: alt %d, interface %i, class %i\n", + i, + interface->altsetting[i].desc.bInterfaceNumber, + interface->altsetting[i].desc.bInterfaceClass); + + switch (e->desc.bmAttributes) { + case USB_ENDPOINT_XFER_BULK: + if (!dir_out) { + get_max_endpoint(usbdev, + &interface->altsetting[i], + "Bulk IN", e, + &dev->bulk_in); + } else { + get_max_endpoint(usbdev, + &interface->altsetting[i], + "Bulk OUT", e, + &dev->bulk_out); + } + break; + case USB_ENDPOINT_XFER_ISOC: + if (!dir_out) { + get_max_endpoint(usbdev, + &interface->altsetting[i], + "ISOC IN", e, + &dev->isoc_in); + } else { + get_max_endpoint(usbdev, + &interface->altsetting[i], + "ISOC OUT", e, + &dev->isoc_out); + } + break; + case USB_ENDPOINT_XFER_INT: + if (!dir_out) { + get_max_endpoint(usbdev, + &interface->altsetting[i], + "INT IN", e, + &dev->int_in); + } else { + get_max_endpoint(usbdev, + &interface->altsetting[i], + "INT OUT", e, + &dev->int_out); + } + break; + } + } + } + + + printk(KERN_INFO "tm6000: New video device @ %s Mbps (%04x:%04x, ifnum %d)\n", + speed, + le16_to_cpu(dev->udev->descriptor.idVendor), + le16_to_cpu(dev->udev->descriptor.idProduct), + interface->altsetting->desc.bInterfaceNumber); + +/* check if the the device has the iso in endpoint at the correct place */ + if (!dev->isoc_in.endp) { + printk(KERN_ERR "tm6000: probing error: no IN ISOC endpoint!\n"); + rc = -ENODEV; + goto free_device; + } + + /* save our data pointer in this interface device */ + usb_set_intfdata(interface, dev); + + printk(KERN_INFO "tm6000: Found %s\n", tm6000_boards[dev->model].name); + + rc = tm6000_init_dev(dev); + if (rc < 0) + goto free_device; + + return 0; + +free_device: + kfree(dev); +report_failure: + printk(KERN_ERR "tm6000: Error %d while registering\n", rc); + + clear_bit(nr, &tm6000_devused); +put_device: + usb_put_dev(usbdev); + return rc; +} + +/* + * tm6000_usb_disconnect() + * called when the device gets disconnected + * video device will be unregistered on v4l2_close in case it is still open + */ +static void tm6000_usb_disconnect(struct usb_interface *interface) +{ + struct tm6000_core *dev = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + if (!dev) + return; + + printk(KERN_INFO "tm6000: disconnecting %s\n", dev->name); + + flush_request_modules(dev); + + tm6000_ir_fini(dev); + + if (dev->gpio.power_led) { + switch (dev->model) { + case TM6010_BOARD_HAUPPAUGE_900H: + case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE: + case TM6010_BOARD_TWINHAN_TU501: + /* Power led off */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.power_led, 0x01); + msleep(15); + break; + case TM6010_BOARD_BEHOLD_WANDER: + case TM6010_BOARD_BEHOLD_VOYAGER: + case TM6010_BOARD_BEHOLD_WANDER_LITE: + case TM6010_BOARD_BEHOLD_VOYAGER_LITE: + /* Power led off */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.power_led, 0x00); + msleep(15); + break; + } + } + tm6000_v4l2_unregister(dev); + + tm6000_i2c_unregister(dev); + + v4l2_device_unregister(&dev->v4l2_dev); + + dev->state |= DEV_DISCONNECTED; + + usb_put_dev(dev->udev); + + tm6000_close_extension(dev); + tm6000_remove_from_devlist(dev); + + clear_bit(dev->devno, &tm6000_devused); + kfree(dev); +} + +static struct usb_driver tm6000_usb_driver = { + .name = "tm6000", + .probe = tm6000_usb_probe, + .disconnect = tm6000_usb_disconnect, + .id_table = tm6000_id_table, +}; + +module_usb_driver(tm6000_usb_driver); + +MODULE_DESCRIPTION("Trident TVMaster TM5600/TM6000/TM6010 USB2 adapter"); +MODULE_AUTHOR("Mauro Carvalho Chehab"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/deprecated/tm6000/tm6000-core.c b/drivers/staging/media/deprecated/tm6000/tm6000-core.c new file mode 100644 index 000000000000..5c8cbc5d6f72 --- /dev/null +++ b/drivers/staging/media/deprecated/tm6000/tm6000-core.c @@ -0,0 +1,916 @@ +// SPDX-License-Identifier: GPL-2.0 +// tm6000-core.c - driver for TM5600/TM6000/TM6010 USB video capture devices +// +// Copyright (c) 2006-2007 Mauro Carvalho Chehab <mchehab@kernel.org> +// +// Copyright (c) 2007 Michel Ludwig <michel.ludwig@gmail.com> +// - DVB-T support + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/usb.h> +#include <linux/i2c.h> +#include "tm6000.h" +#include "tm6000-regs.h" +#include <media/v4l2-common.h> +#include <media/tuner.h> + +#define USB_TIMEOUT (5 * HZ) /* ms */ + +int tm6000_read_write_usb(struct tm6000_core *dev, u8 req_type, u8 req, + u16 value, u16 index, u8 *buf, u16 len) +{ + int ret, i; + unsigned int pipe; + u8 *data = NULL; + int delay = 5000; + + if (len) { + data = kzalloc(len, GFP_KERNEL); + if (!data) + return -ENOMEM; + } + + mutex_lock(&dev->usb_lock); + + if (req_type & USB_DIR_IN) + pipe = usb_rcvctrlpipe(dev->udev, 0); + else { + pipe = usb_sndctrlpipe(dev->udev, 0); + memcpy(data, buf, len); + } + + if (tm6000_debug & V4L2_DEBUG_I2C) { + printk(KERN_DEBUG "(dev %p, pipe %08x): ", dev->udev, pipe); + + printk(KERN_CONT "%s: %02x %02x %02x %02x %02x %02x %02x %02x ", + (req_type & USB_DIR_IN) ? " IN" : "OUT", + req_type, req, value&0xff, value>>8, index&0xff, + index>>8, len&0xff, len>>8); + + if (!(req_type & USB_DIR_IN)) { + printk(KERN_CONT ">>> "); + for (i = 0; i < len; i++) + printk(KERN_CONT " %02x", buf[i]); + printk(KERN_CONT "\n"); + } + } + + ret = usb_control_msg(dev->udev, pipe, req, req_type, value, index, + data, len, USB_TIMEOUT); + + if (req_type & USB_DIR_IN) + memcpy(buf, data, len); + + if (tm6000_debug & V4L2_DEBUG_I2C) { + if (ret < 0) { + if (req_type & USB_DIR_IN) + printk(KERN_DEBUG "<<< (len=%d)\n", len); + + printk(KERN_CONT "%s: Error #%d\n", __func__, ret); + } else if (req_type & USB_DIR_IN) { + printk(KERN_CONT "<<< "); + for (i = 0; i < len; i++) + printk(KERN_CONT " %02x", buf[i]); + printk(KERN_CONT "\n"); + } + } + + kfree(data); + + if (dev->quirks & TM6000_QUIRK_NO_USB_DELAY) + delay = 0; + + if (req == REQ_16_SET_GET_I2C_WR1_RDN && !(req_type & USB_DIR_IN)) { + unsigned int tsleep; + /* Calculate delay time, 14000us for 64 bytes */ + tsleep = (len * 200) + 200; + if (tsleep < delay) + tsleep = delay; + usleep_range(tsleep, tsleep + 1000); + } + else if (delay) + usleep_range(delay, delay + 1000); + + mutex_unlock(&dev->usb_lock); + return ret; +} + +int tm6000_set_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index) +{ + return + tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR, + req, value, index, NULL, 0); +} +EXPORT_SYMBOL_GPL(tm6000_set_reg); + +int tm6000_get_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index) +{ + int rc; + u8 buf[1]; + + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req, + value, index, buf, 1); + + if (rc < 0) + return rc; + + return *buf; +} +EXPORT_SYMBOL_GPL(tm6000_get_reg); + +int tm6000_set_reg_mask(struct tm6000_core *dev, u8 req, u16 value, + u16 index, u16 mask) +{ + int rc; + u8 buf[1]; + u8 new_index; + + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req, + value, 0, buf, 1); + + if (rc < 0) + return rc; + + new_index = (buf[0] & ~mask) | (index & mask); + + if (new_index == buf[0]) + return 0; + + return tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR, + req, value, new_index, NULL, 0); +} +EXPORT_SYMBOL_GPL(tm6000_set_reg_mask); + +int tm6000_get_reg16(struct tm6000_core *dev, u8 req, u16 value, u16 index) +{ + int rc; + u8 buf[2]; + + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req, + value, index, buf, 2); + + if (rc < 0) + return rc; + + return buf[1]|buf[0]<<8; +} + +int tm6000_get_reg32(struct tm6000_core *dev, u8 req, u16 value, u16 index) +{ + int rc; + u8 buf[4]; + + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req, + value, index, buf, 4); + + if (rc < 0) + return rc; + + return buf[3] | buf[2] << 8 | buf[1] << 16 | buf[0] << 24; +} + +int tm6000_i2c_reset(struct tm6000_core *dev, u16 tsleep) +{ + int rc; + + rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_CLK, 0); + if (rc < 0) + return rc; + + msleep(tsleep); + + rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_CLK, 1); + msleep(tsleep); + + return rc; +} + +void tm6000_set_fourcc_format(struct tm6000_core *dev) +{ + if (dev->dev_type == TM6010) { + int val; + + val = tm6000_get_reg(dev, TM6010_REQ07_RCC_ACTIVE_IF, 0) & 0xfc; + if (dev->fourcc == V4L2_PIX_FMT_UYVY) + tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_IF, val); + else + tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_IF, val | 1); + } else { + if (dev->fourcc == V4L2_PIX_FMT_UYVY) + tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0xd0); + else + tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0x90); + } +} + +static void tm6000_set_vbi(struct tm6000_core *dev) +{ + /* + * FIXME: + * VBI lines and start/end are different between 60Hz and 50Hz + * So, it is very likely that we need to change the config to + * something that takes it into account, doing something different + * if (dev->norm & V4L2_STD_525_60) + */ + + if (dev->dev_type == TM6010) { + tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01); + tm6000_set_reg(dev, TM6010_REQ07_R41_TELETEXT_VBI_CODE1, 0x27); + tm6000_set_reg(dev, TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL, 0x55); + tm6000_set_reg(dev, TM6010_REQ07_R43_VBI_DATA_TYPE_LINE7, 0x66); + tm6000_set_reg(dev, TM6010_REQ07_R44_VBI_DATA_TYPE_LINE8, 0x66); + tm6000_set_reg(dev, TM6010_REQ07_R45_VBI_DATA_TYPE_LINE9, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R46_VBI_DATA_TYPE_LINE10, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R47_VBI_DATA_TYPE_LINE11, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R48_VBI_DATA_TYPE_LINE12, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R49_VBI_DATA_TYPE_LINE13, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R4A_VBI_DATA_TYPE_LINE14, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R4B_VBI_DATA_TYPE_LINE15, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R4C_VBI_DATA_TYPE_LINE16, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R4D_VBI_DATA_TYPE_LINE17, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R4E_VBI_DATA_TYPE_LINE18, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R4F_VBI_DATA_TYPE_LINE19, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R50_VBI_DATA_TYPE_LINE20, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R52_VBI_DATA_TYPE_LINE22, 0x66); + tm6000_set_reg(dev, + TM6010_REQ07_R53_VBI_DATA_TYPE_LINE23, 0x00); + tm6000_set_reg(dev, + TM6010_REQ07_R54_VBI_DATA_TYPE_RLINES, 0x00); + tm6000_set_reg(dev, + TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN, 0x01); + tm6000_set_reg(dev, + TM6010_REQ07_R56_VBI_LOOP_FILTER_I_GAIN, 0x00); + tm6000_set_reg(dev, + TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN, 0x02); + tm6000_set_reg(dev, TM6010_REQ07_R58_VBI_CAPTION_DTO1, 0x35); + tm6000_set_reg(dev, TM6010_REQ07_R59_VBI_CAPTION_DTO0, 0xa0); + tm6000_set_reg(dev, TM6010_REQ07_R5A_VBI_TELETEXT_DTO1, 0x11); + tm6000_set_reg(dev, TM6010_REQ07_R5B_VBI_TELETEXT_DTO0, 0x4c); + tm6000_set_reg(dev, TM6010_REQ07_R40_TELETEXT_VBI_CODE0, 0x01); + tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x00); + } +} + +int tm6000_init_analog_mode(struct tm6000_core *dev) +{ + struct v4l2_frequency f; + + if (dev->dev_type == TM6010) { + u8 active = TM6010_REQ07_RCC_ACTIVE_IF_AUDIO_ENABLE; + + if (!dev->radio) + active |= TM6010_REQ07_RCC_ACTIVE_IF_VIDEO_ENABLE; + + /* Enable video and audio */ + tm6000_set_reg_mask(dev, TM6010_REQ07_RCC_ACTIVE_IF, + active, 0x60); + /* Disable TS input */ + tm6000_set_reg_mask(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, + 0x00, 0x40); + } else { + /* Enables soft reset */ + tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01); + + if (dev->scaler) + /* Disable Hfilter and Enable TS Drop err */ + tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x20); + else /* Enable Hfilter and disable TS Drop err */ + tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x80); + + tm6000_set_reg(dev, TM6010_REQ07_RC3_HSTART1, 0x88); + tm6000_set_reg(dev, TM6000_REQ07_RDA_CLK_SEL, 0x23); + tm6000_set_reg(dev, TM6010_REQ07_RD1_ADDR_FOR_REQ1, 0xc0); + tm6000_set_reg(dev, TM6010_REQ07_RD2_ADDR_FOR_REQ2, 0xd8); + tm6000_set_reg(dev, TM6010_REQ07_RD6_ENDP_REQ1_REQ2, 0x06); + tm6000_set_reg(dev, TM6000_REQ07_RDF_PWDOWN_ACLK, 0x1f); + + /* AP Software reset */ + tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x08); + tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x00); + + tm6000_set_fourcc_format(dev); + + /* Disables soft reset */ + tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x00); + } + msleep(20); + + /* Tuner firmware can now be loaded */ + + /* + * FIXME: This is a hack! xc3028 "sleeps" when no channel is detected + * for more than a few seconds. Not sure why, as this behavior does + * not happen on other devices with xc3028. So, I suspect that it + * is yet another bug at tm6000. After start sleeping, decoding + * doesn't start automatically. Instead, it requires some + * I2C commands to wake it up. As we want to have image at the + * beginning, we needed to add this hack. The better would be to + * discover some way to make tm6000 to wake up without this hack. + */ + f.frequency = dev->freq; + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); + + msleep(100); + tm6000_set_standard(dev); + tm6000_set_vbi(dev); + tm6000_set_audio_bitrate(dev, 48000); + + /* switch dvb led off */ + if (dev->gpio.dvb_led) { + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.dvb_led, 0x01); + } + + return 0; +} + +int tm6000_init_digital_mode(struct tm6000_core *dev) +{ + if (dev->dev_type == TM6010) { + /* Disable video and audio */ + tm6000_set_reg_mask(dev, TM6010_REQ07_RCC_ACTIVE_IF, + 0x00, 0x60); + /* Enable TS input */ + tm6000_set_reg_mask(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, + 0x40, 0x40); + /* all power down, but not the digital data port */ + tm6000_set_reg(dev, TM6010_REQ07_RFE_POWER_DOWN, 0x28); + tm6000_set_reg(dev, TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xfc); + tm6000_set_reg(dev, TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0xff); + } else { + tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x08); + tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x00); + tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01); + tm6000_set_reg(dev, TM6000_REQ07_RDF_PWDOWN_ACLK, 0x08); + tm6000_set_reg(dev, TM6000_REQ07_RE2_VADC_STATUS_CTL, 0x0c); + tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0xff); + tm6000_set_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, 0xd8); + tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x40); + tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0xd0); + tm6000_set_reg(dev, TM6010_REQ07_RC3_HSTART1, 0x09); + tm6000_set_reg(dev, TM6000_REQ07_RDA_CLK_SEL, 0x37); + tm6000_set_reg(dev, TM6010_REQ07_RD1_ADDR_FOR_REQ1, 0xd8); + tm6000_set_reg(dev, TM6010_REQ07_RD2_ADDR_FOR_REQ2, 0xc0); + tm6000_set_reg(dev, TM6010_REQ07_RD6_ENDP_REQ1_REQ2, 0x60); + + tm6000_set_reg(dev, TM6000_REQ07_RE2_VADC_STATUS_CTL, 0x0c); + tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0xff); + tm6000_set_reg(dev, TM6000_REQ07_REB_VADC_AADC_MODE, 0x08); + msleep(50); + + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x00); + msleep(50); + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x01); + msleep(50); + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x00); + msleep(100); + } + + /* switch dvb led on */ + if (dev->gpio.dvb_led) { + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->gpio.dvb_led, 0x00); + } + + return 0; +} +EXPORT_SYMBOL(tm6000_init_digital_mode); + +struct reg_init { + u8 req; + u8 reg; + u8 val; +}; + +/* The meaning of those initializations are unknown */ +static struct reg_init tm6000_init_tab[] = { + /* REG VALUE */ + { TM6000_REQ07_RDF_PWDOWN_ACLK, 0x1f }, + { TM6010_REQ07_RFF_SOFT_RESET, 0x08 }, + { TM6010_REQ07_RFF_SOFT_RESET, 0x00 }, + { TM6010_REQ07_RD5_POWERSAVE, 0x4f }, + { TM6000_REQ07_RDA_CLK_SEL, 0x23 }, + { TM6000_REQ07_RDB_OUT_SEL, 0x08 }, + { TM6000_REQ07_RE2_VADC_STATUS_CTL, 0x00 }, + { TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x10 }, + { TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x00 }, + { TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x00 }, + { TM6000_REQ07_REB_VADC_AADC_MODE, 0x64 }, /* 48000 bits/sample, external input */ + { TM6000_REQ07_REE_VADC_CTRL_SEL_CONTROL, 0xc2 }, + + { TM6010_REQ07_R3F_RESET, 0x01 }, /* Start of soft reset */ + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x07 }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 }, + { TM6010_REQ07_R05_NOISE_THRESHOLD, 0x64 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01 }, + { TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, 0x82 }, + { TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, 0x36 }, + { TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, 0x50 }, + { TM6010_REQ07_R0C_CHROMA_AGC_CONTROL, 0x6a }, + { TM6010_REQ07_R11_AGC_PEAK_CONTROL, 0xc9 }, + { TM6010_REQ07_R12_AGC_GATE_STARTH, 0x07 }, + { TM6010_REQ07_R13_AGC_GATE_STARTL, 0x3b }, + { TM6010_REQ07_R14_AGC_GATE_WIDTH, 0x47 }, + { TM6010_REQ07_R15_AGC_BP_DELAY, 0x6f }, + { TM6010_REQ07_R17_HLOOP_MAXSTATE, 0xcd }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R20_HSYNC_RISING_EDGE_TIME, 0x3c }, + { TM6010_REQ07_R21_HSYNC_PHASE_OFFSET, 0x3c }, + { TM6010_REQ07_R2D_CHROMA_BURST_END, 0x48 }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, + { TM6010_REQ07_R32_VSYNC_HLOCK_MIN, 0x74 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c }, + { TM6010_REQ07_R34_VSYNC_AGC_MIN, 0x74 }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R36_VSYNC_VBI_MIN, 0x7a }, + { TM6010_REQ07_R37_VSYNC_VBI_MAX, 0x26 }, + { TM6010_REQ07_R38_VSYNC_THRESHOLD, 0x40 }, + { TM6010_REQ07_R39_VSYNC_TIME_CONSTANT, 0x0a }, + { TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL, 0x55 }, + { TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21, 0x11 }, + { TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN, 0x01 }, + { TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN, 0x02 }, + { TM6010_REQ07_R58_VBI_CAPTION_DTO1, 0x35 }, + { TM6010_REQ07_R59_VBI_CAPTION_DTO0, 0xa0 }, + { TM6010_REQ07_R80_COMB_FILTER_TRESHOLD, 0x15 }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, + { TM6010_REQ07_RC1_TRESHOLD, 0xd0 }, + { TM6010_REQ07_RC3_HSTART1, 0x88 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, /* End of the soft reset */ + { TM6010_REQ05_R18_IMASK7, 0x00 }, +}; + +static struct reg_init tm6010_init_tab[] = { + { TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x00 }, + { TM6010_REQ07_RC4_HSTART0, 0xa0 }, + { TM6010_REQ07_RC6_HEND0, 0x40 }, + { TM6010_REQ07_RCA_VEND0, 0x31 }, + { TM6010_REQ07_RCC_ACTIVE_IF, 0xe1 }, + { TM6010_REQ07_RE0_DVIDEO_SOURCE, 0x03 }, + { TM6010_REQ07_RFE_POWER_DOWN, 0x7f }, + + { TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0 }, + { TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4 }, + { TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf8 }, + { TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0x00 }, + { TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf2 }, + { TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xf0 }, + { TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2 }, + { TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, 0x60 }, + { TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc }, + + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x07 }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 }, + { TM6010_REQ07_R05_NOISE_THRESHOLD, 0x64 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01 }, + { TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, 0x82 }, + { TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, 0x36 }, + { TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, 0x50 }, + { TM6010_REQ07_R0C_CHROMA_AGC_CONTROL, 0x6a }, + { TM6010_REQ07_R11_AGC_PEAK_CONTROL, 0xc9 }, + { TM6010_REQ07_R12_AGC_GATE_STARTH, 0x07 }, + { TM6010_REQ07_R13_AGC_GATE_STARTL, 0x3b }, + { TM6010_REQ07_R14_AGC_GATE_WIDTH, 0x47 }, + { TM6010_REQ07_R15_AGC_BP_DELAY, 0x6f }, + { TM6010_REQ07_R17_HLOOP_MAXSTATE, 0xcd }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R20_HSYNC_RISING_EDGE_TIME, 0x3c }, + { TM6010_REQ07_R21_HSYNC_PHASE_OFFSET, 0x3c }, + { TM6010_REQ07_R2D_CHROMA_BURST_END, 0x48 }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, + { TM6010_REQ07_R32_VSYNC_HLOCK_MIN, 0x74 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c }, + { TM6010_REQ07_R34_VSYNC_AGC_MIN, 0x74 }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R36_VSYNC_VBI_MIN, 0x7a }, + { TM6010_REQ07_R37_VSYNC_VBI_MAX, 0x26 }, + { TM6010_REQ07_R38_VSYNC_THRESHOLD, 0x40 }, + { TM6010_REQ07_R39_VSYNC_TIME_CONSTANT, 0x0a }, + { TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL, 0x55 }, + { TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21, 0x11 }, + { TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN, 0x01 }, + { TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN, 0x02 }, + { TM6010_REQ07_R58_VBI_CAPTION_DTO1, 0x35 }, + { TM6010_REQ07_R59_VBI_CAPTION_DTO0, 0xa0 }, + { TM6010_REQ07_R80_COMB_FILTER_TRESHOLD, 0x15 }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, + { TM6010_REQ07_RC1_TRESHOLD, 0xd0 }, + { TM6010_REQ07_RC3_HSTART1, 0x88 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + + { TM6010_REQ05_R18_IMASK7, 0x00 }, + + { TM6010_REQ07_RDC_IR_LEADER1, 0xaa }, + { TM6010_REQ07_RDD_IR_LEADER0, 0x30 }, + { TM6010_REQ07_RDE_IR_PULSE_CNT1, 0x20 }, + { TM6010_REQ07_RDF_IR_PULSE_CNT0, 0xd0 }, + { REQ_04_EN_DISABLE_MCU_INT, 0x02, 0x00 }, + { TM6010_REQ07_RD8_IR, 0x0f }, + + /* set remote wakeup key:any key wakeup */ + { TM6010_REQ07_RE5_REMOTE_WAKEUP, 0xfe }, + { TM6010_REQ07_RDA_IR_WAKEUP_SEL, 0xff }, +}; + +int tm6000_init(struct tm6000_core *dev) +{ + int board, rc = 0, i, size; + struct reg_init *tab; + + /* Check board revision */ + board = tm6000_get_reg32(dev, REQ_40_GET_VERSION, 0, 0); + if (board >= 0) { + switch (board & 0xff) { + case 0xf3: + printk(KERN_INFO "Found tm6000\n"); + if (dev->dev_type != TM6000) + dev->dev_type = TM6000; + break; + case 0xf4: + printk(KERN_INFO "Found tm6010\n"); + if (dev->dev_type != TM6010) + dev->dev_type = TM6010; + break; + default: + printk(KERN_INFO "Unknown board version = 0x%08x\n", board); + } + } else + printk(KERN_ERR "Error %i while retrieving board version\n", board); + + if (dev->dev_type == TM6010) { + tab = tm6010_init_tab; + size = ARRAY_SIZE(tm6010_init_tab); + } else { + tab = tm6000_init_tab; + size = ARRAY_SIZE(tm6000_init_tab); + } + + /* Load board's initialization table */ + for (i = 0; i < size; i++) { + rc = tm6000_set_reg(dev, tab[i].req, tab[i].reg, tab[i].val); + if (rc < 0) { + printk(KERN_ERR "Error %i while setting req %d, reg %d to value %d\n", + rc, + tab[i].req, tab[i].reg, tab[i].val); + return rc; + } + } + + msleep(5); /* Just to be conservative */ + + rc = tm6000_cards_setup(dev); + + return rc; +} + + +int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate) +{ + int val = 0; + u8 areg_f0 = 0x60; /* ADC MCLK = 250 Fs */ + u8 areg_0a = 0x91; /* SIF 48KHz */ + + switch (bitrate) { + case 48000: + areg_f0 = 0x60; /* ADC MCLK = 250 Fs */ + areg_0a = 0x91; /* SIF 48KHz */ + dev->audio_bitrate = bitrate; + break; + case 32000: + areg_f0 = 0x00; /* ADC MCLK = 375 Fs */ + areg_0a = 0x90; /* SIF 32KHz */ + dev->audio_bitrate = bitrate; + break; + default: + return -EINVAL; + } + + + /* enable I2S, if we use sif or external I2S device */ + if (dev->dev_type == TM6010) { + val = tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, areg_0a); + if (val < 0) + return val; + + val = tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, + areg_f0, 0xf0); + if (val < 0) + return val; + } else { + val = tm6000_set_reg_mask(dev, TM6000_REQ07_REB_VADC_AADC_MODE, + areg_f0, 0xf0); + if (val < 0) + return val; + } + return 0; +} +EXPORT_SYMBOL_GPL(tm6000_set_audio_bitrate); + +int tm6000_set_audio_rinput(struct tm6000_core *dev) +{ + if (dev->dev_type == TM6010) { + /* Audio crossbar setting, default SIF1 */ + u8 areg_f0; + u8 areg_07 = 0x10; + + switch (dev->rinput.amux) { + case TM6000_AMUX_SIF1: + case TM6000_AMUX_SIF2: + areg_f0 = 0x03; + areg_07 = 0x30; + break; + case TM6000_AMUX_ADC1: + areg_f0 = 0x00; + break; + case TM6000_AMUX_ADC2: + areg_f0 = 0x08; + break; + case TM6000_AMUX_I2S: + areg_f0 = 0x04; + break; + default: + printk(KERN_INFO "%s: audio input doesn't support\n", + dev->name); + return 0; + break; + } + /* Set audio input crossbar */ + tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, + areg_f0, 0x0f); + /* Mux overflow workaround */ + tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL, + areg_07, 0xf0); + } else { + u8 areg_eb; + /* Audio setting, default LINE1 */ + switch (dev->rinput.amux) { + case TM6000_AMUX_ADC1: + areg_eb = 0x00; + break; + case TM6000_AMUX_ADC2: + areg_eb = 0x04; + break; + default: + printk(KERN_INFO "%s: audio input doesn't support\n", + dev->name); + return 0; + break; + } + /* Set audio input */ + tm6000_set_reg_mask(dev, TM6000_REQ07_REB_VADC_AADC_MODE, + areg_eb, 0x0f); + } + return 0; +} + +static void tm6010_set_mute_sif(struct tm6000_core *dev, u8 mute) +{ + u8 mute_reg = 0; + + if (mute) + mute_reg = 0x08; + + tm6000_set_reg_mask(dev, TM6010_REQ08_R0A_A_I2S_MOD, mute_reg, 0x08); +} + +static void tm6010_set_mute_adc(struct tm6000_core *dev, u8 mute) +{ + u8 mute_reg = 0; + + if (mute) + mute_reg = 0x20; + + if (dev->dev_type == TM6010) { + tm6000_set_reg_mask(dev, TM6010_REQ08_RF2_LEFT_CHANNEL_VOL, + mute_reg, 0x20); + tm6000_set_reg_mask(dev, TM6010_REQ08_RF3_RIGHT_CHANNEL_VOL, + mute_reg, 0x20); + } else { + tm6000_set_reg_mask(dev, TM6000_REQ07_REC_VADC_AADC_LVOL, + mute_reg, 0x20); + tm6000_set_reg_mask(dev, TM6000_REQ07_RED_VADC_AADC_RVOL, + mute_reg, 0x20); + } +} + +int tm6000_tvaudio_set_mute(struct tm6000_core *dev, u8 mute) +{ + enum tm6000_mux mux; + + if (dev->radio) + mux = dev->rinput.amux; + else + mux = dev->vinput[dev->input].amux; + + switch (mux) { + case TM6000_AMUX_SIF1: + case TM6000_AMUX_SIF2: + if (dev->dev_type == TM6010) + tm6010_set_mute_sif(dev, mute); + else { + printk(KERN_INFO "ERROR: TM5600 and TM6000 don't has SIF audio inputs. Please check the %s configuration.\n", + dev->name); + return -EINVAL; + } + break; + case TM6000_AMUX_ADC1: + case TM6000_AMUX_ADC2: + tm6010_set_mute_adc(dev, mute); + break; + default: + return -EINVAL; + break; + } + return 0; +} + +static void tm6010_set_volume_sif(struct tm6000_core *dev, int vol) +{ + u8 vol_reg; + + vol_reg = vol & 0x0F; + + if (vol < 0) + vol_reg |= 0x40; + + tm6000_set_reg(dev, TM6010_REQ08_R07_A_LEFT_VOL, vol_reg); + tm6000_set_reg(dev, TM6010_REQ08_R08_A_RIGHT_VOL, vol_reg); +} + +static void tm6010_set_volume_adc(struct tm6000_core *dev, int vol) +{ + u8 vol_reg; + + vol_reg = (vol + 0x10) & 0x1f; + + if (dev->dev_type == TM6010) { + tm6000_set_reg(dev, TM6010_REQ08_RF2_LEFT_CHANNEL_VOL, vol_reg); + tm6000_set_reg(dev, TM6010_REQ08_RF3_RIGHT_CHANNEL_VOL, vol_reg); + } else { + tm6000_set_reg(dev, TM6000_REQ07_REC_VADC_AADC_LVOL, vol_reg); + tm6000_set_reg(dev, TM6000_REQ07_RED_VADC_AADC_RVOL, vol_reg); + } +} + +void tm6000_set_volume(struct tm6000_core *dev, int vol) +{ + enum tm6000_mux mux; + + if (dev->radio) { + mux = dev->rinput.amux; + vol += 8; /* Offset to 0 dB */ + } else + mux = dev->vinput[dev->input].amux; + + switch (mux) { + case TM6000_AMUX_SIF1: + case TM6000_AMUX_SIF2: + if (dev->dev_type == TM6010) + tm6010_set_volume_sif(dev, vol); + else + printk(KERN_INFO "ERROR: TM5600 and TM6000 don't has SIF audio inputs. Please check the %s configuration.\n", + dev->name); + break; + case TM6000_AMUX_ADC1: + case TM6000_AMUX_ADC2: + tm6010_set_volume_adc(dev, vol); + break; + default: + break; + } +} + +static LIST_HEAD(tm6000_devlist); +static DEFINE_MUTEX(tm6000_devlist_mutex); + +/* + * tm6000_realease_resource() + */ + +void tm6000_remove_from_devlist(struct tm6000_core *dev) +{ + mutex_lock(&tm6000_devlist_mutex); + list_del(&dev->devlist); + mutex_unlock(&tm6000_devlist_mutex); +}; + +void tm6000_add_into_devlist(struct tm6000_core *dev) +{ + mutex_lock(&tm6000_devlist_mutex); + list_add_tail(&dev->devlist, &tm6000_devlist); + mutex_unlock(&tm6000_devlist_mutex); +}; + +/* + * Extension interface + */ + +static LIST_HEAD(tm6000_extension_devlist); + +int tm6000_call_fillbuf(struct tm6000_core *dev, enum tm6000_ops_type type, + char *buf, int size) +{ + struct tm6000_ops *ops = NULL; + + /* FIXME: tm6000_extension_devlist_lock should be a spinlock */ + + list_for_each_entry(ops, &tm6000_extension_devlist, next) { + if (ops->fillbuf && ops->type == type) + ops->fillbuf(dev, buf, size); + } + + return 0; +} + +int tm6000_register_extension(struct tm6000_ops *ops) +{ + struct tm6000_core *dev = NULL; + + mutex_lock(&tm6000_devlist_mutex); + list_add_tail(&ops->next, &tm6000_extension_devlist); + list_for_each_entry(dev, &tm6000_devlist, devlist) { + ops->init(dev); + printk(KERN_INFO "%s: Initialized (%s) extension\n", + dev->name, ops->name); + } + mutex_unlock(&tm6000_devlist_mutex); + return 0; +} +EXPORT_SYMBOL(tm6000_register_extension); + +void tm6000_unregister_extension(struct tm6000_ops *ops) +{ + struct tm6000_core *dev = NULL; + + mutex_lock(&tm6000_devlist_mutex); + list_for_each_entry(dev, &tm6000_devlist, devlist) + ops->fini(dev); + + printk(KERN_INFO "tm6000: Remove (%s) extension\n", ops->name); + list_del(&ops->next); + mutex_unlock(&tm6000_devlist_mutex); +} +EXPORT_SYMBOL(tm6000_unregister_extension); + +void tm6000_init_extension(struct tm6000_core *dev) +{ + struct tm6000_ops *ops = NULL; + + mutex_lock(&tm6000_devlist_mutex); + list_for_each_entry(ops, &tm6000_extension_devlist, next) { + if (ops->init) + ops->init(dev); + } + mutex_unlock(&tm6000_devlist_mutex); +} + +void tm6000_close_extension(struct tm6000_core *dev) +{ + struct tm6000_ops *ops = NULL; + + mutex_lock(&tm6000_devlist_mutex); + list_for_each_entry(ops, &tm6000_extension_devlist, next) { + if (ops->fini) + ops->fini(dev); + } + mutex_unlock(&tm6000_devlist_mutex); +} diff --git a/drivers/staging/media/deprecated/tm6000/tm6000-dvb.c b/drivers/staging/media/deprecated/tm6000/tm6000-dvb.c new file mode 100644 index 000000000000..ee04973cbf93 --- /dev/null +++ b/drivers/staging/media/deprecated/tm6000/tm6000-dvb.c @@ -0,0 +1,454 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * tm6000-dvb.c - dvb-t support for TM5600/TM6000/TM6010 USB video capture devices + * + * Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com> + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/usb.h> + +#include "tm6000.h" +#include "tm6000-regs.h" + +#include "zl10353.h" + +#include <media/tuner.h> + +#include "xc2028.h" +#include "xc5000.h" + +MODULE_DESCRIPTION("DVB driver extension module for tm5600/6000/6010 based TV cards"); +MODULE_AUTHOR("Mauro Carvalho Chehab"); +MODULE_LICENSE("GPL"); + +static int debug; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debug message"); + +static inline void print_err_status(struct tm6000_core *dev, + int packet, int status) +{ + char *errmsg = "Unknown"; + + switch (status) { + case -ENOENT: + errmsg = "unlinked synchronously"; + break; + case -ECONNRESET: + errmsg = "unlinked asynchronously"; + break; + case -ENOSR: + errmsg = "Buffer error (overrun)"; + break; + case -EPIPE: + errmsg = "Stalled (device not responding)"; + break; + case -EOVERFLOW: + errmsg = "Babble (bad cable?)"; + break; + case -EPROTO: + errmsg = "Bit-stuff error (bad cable?)"; + break; + case -EILSEQ: + errmsg = "CRC/Timeout (could be anything)"; + break; + case -ETIME: + errmsg = "Device does not respond"; + break; + } + if (packet < 0) { + dprintk(dev, 1, "URB status %d [%s].\n", + status, errmsg); + } else { + dprintk(dev, 1, "URB packet %d, status %d [%s].\n", + packet, status, errmsg); + } +} + +static void tm6000_urb_received(struct urb *urb) +{ + int ret; + struct tm6000_core *dev = urb->context; + + switch (urb->status) { + case 0: + case -ETIMEDOUT: + break; + case -ENOENT: + case -ECONNRESET: + case -ESHUTDOWN: + return; + default: + print_err_status(dev, 0, urb->status); + } + + if (urb->actual_length > 0) + dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer, + urb->actual_length); + + if (dev->dvb->streams > 0) { + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret < 0) { + printk(KERN_ERR "tm6000: error %s\n", __func__); + kfree(urb->transfer_buffer); + usb_free_urb(urb); + dev->dvb->bulk_urb = NULL; + } + } +} + +static int tm6000_start_stream(struct tm6000_core *dev) +{ + int ret; + unsigned int pipe, size; + struct tm6000_dvb *dvb = dev->dvb; + + printk(KERN_INFO "tm6000: got start stream request %s\n", __func__); + + if (dev->mode != TM6000_MODE_DIGITAL) { + tm6000_init_digital_mode(dev); + dev->mode = TM6000_MODE_DIGITAL; + } + + dvb->bulk_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dvb->bulk_urb) + return -ENOMEM; + + pipe = usb_rcvbulkpipe(dev->udev, dev->bulk_in.endp->desc.bEndpointAddress + & USB_ENDPOINT_NUMBER_MASK); + + size = usb_maxpacket(dev->udev, pipe); + size = size * 15; /* 512 x 8 or 12 or 15 */ + + dvb->bulk_urb->transfer_buffer = kzalloc(size, GFP_KERNEL); + if (!dvb->bulk_urb->transfer_buffer) { + usb_free_urb(dvb->bulk_urb); + dvb->bulk_urb = NULL; + return -ENOMEM; + } + + usb_fill_bulk_urb(dvb->bulk_urb, dev->udev, pipe, + dvb->bulk_urb->transfer_buffer, + size, + tm6000_urb_received, dev); + + ret = usb_clear_halt(dev->udev, pipe); + if (ret < 0) { + printk(KERN_ERR "tm6000: error %i in %s during pipe reset\n", + ret, __func__); + + kfree(dvb->bulk_urb->transfer_buffer); + usb_free_urb(dvb->bulk_urb); + dvb->bulk_urb = NULL; + return ret; + } else + printk(KERN_ERR "tm6000: pipe reset\n"); + +/* mutex_lock(&tm6000_driver.open_close_mutex); */ + ret = usb_submit_urb(dvb->bulk_urb, GFP_ATOMIC); + +/* mutex_unlock(&tm6000_driver.open_close_mutex); */ + if (ret) { + printk(KERN_ERR "tm6000: submit of urb failed (error=%i)\n", + ret); + + kfree(dvb->bulk_urb->transfer_buffer); + usb_free_urb(dvb->bulk_urb); + dvb->bulk_urb = NULL; + return ret; + } + + return 0; +} + +static void tm6000_stop_stream(struct tm6000_core *dev) +{ + struct tm6000_dvb *dvb = dev->dvb; + + if (dvb->bulk_urb) { + printk(KERN_INFO "urb killing\n"); + usb_kill_urb(dvb->bulk_urb); + printk(KERN_INFO "urb buffer free\n"); + kfree(dvb->bulk_urb->transfer_buffer); + usb_free_urb(dvb->bulk_urb); + dvb->bulk_urb = NULL; + } +} + +static int tm6000_start_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct tm6000_core *dev = demux->priv; + struct tm6000_dvb *dvb = dev->dvb; + printk(KERN_INFO "tm6000: got start feed request %s\n", __func__); + + mutex_lock(&dvb->mutex); + if (dvb->streams == 0) { + dvb->streams = 1; +/* mutex_init(&tm6000_dev->streming_mutex); */ + tm6000_start_stream(dev); + } else + ++(dvb->streams); + mutex_unlock(&dvb->mutex); + + return 0; +} + +static int tm6000_stop_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct tm6000_core *dev = demux->priv; + struct tm6000_dvb *dvb = dev->dvb; + + printk(KERN_INFO "tm6000: got stop feed request %s\n", __func__); + + mutex_lock(&dvb->mutex); + + printk(KERN_INFO "stream %#x\n", dvb->streams); + --(dvb->streams); + if (dvb->streams == 0) { + printk(KERN_INFO "stop stream\n"); + tm6000_stop_stream(dev); +/* mutex_destroy(&tm6000_dev->streaming_mutex); */ + } + mutex_unlock(&dvb->mutex); +/* mutex_destroy(&tm6000_dev->streaming_mutex); */ + + return 0; +} + +static int tm6000_dvb_attach_frontend(struct tm6000_core *dev) +{ + struct tm6000_dvb *dvb = dev->dvb; + + if (dev->caps.has_zl10353) { + struct zl10353_config config = { + .demod_address = dev->demod_addr, + .no_tuner = 1, + .parallel_ts = 1, + .if2 = 45700, + .disable_i2c_gate_ctrl = 1, + }; + + dvb->frontend = dvb_attach(zl10353_attach, &config, + &dev->i2c_adap); + } else { + printk(KERN_ERR "tm6000: no frontend defined for the device!\n"); + return -1; + } + + return (!dvb->frontend) ? -1 : 0; +} + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static int register_dvb(struct tm6000_core *dev) +{ + int ret = -1; + struct tm6000_dvb *dvb = dev->dvb; + + mutex_init(&dvb->mutex); + + dvb->streams = 0; + + /* attach the frontend */ + ret = tm6000_dvb_attach_frontend(dev); + if (ret < 0) { + printk(KERN_ERR "tm6000: couldn't attach the frontend!\n"); + goto err; + } + + ret = dvb_register_adapter(&dvb->adapter, "Trident TVMaster 6000 DVB-T", + THIS_MODULE, &dev->udev->dev, adapter_nr); + if (ret < 0) { + pr_err("tm6000: couldn't register the adapter!\n"); + goto err; + } + + dvb->adapter.priv = dev; + + if (dvb->frontend) { + switch (dev->tuner_type) { + case TUNER_XC2028: { + struct xc2028_config cfg = { + .i2c_adap = &dev->i2c_adap, + .i2c_addr = dev->tuner_addr, + }; + + dvb->frontend->callback = tm6000_tuner_callback; + ret = dvb_register_frontend(&dvb->adapter, dvb->frontend); + if (ret < 0) { + printk(KERN_ERR + "tm6000: couldn't register frontend\n"); + goto adapter_err; + } + + if (!dvb_attach(xc2028_attach, dvb->frontend, &cfg)) { + printk(KERN_ERR "tm6000: couldn't register frontend (xc3028)\n"); + ret = -EINVAL; + goto frontend_err; + } + printk(KERN_INFO "tm6000: XC2028/3028 asked to be attached to frontend!\n"); + break; + } + case TUNER_XC5000: { + struct xc5000_config cfg = { + .i2c_address = dev->tuner_addr, + }; + + dvb->frontend->callback = tm6000_xc5000_callback; + ret = dvb_register_frontend(&dvb->adapter, dvb->frontend); + if (ret < 0) { + printk(KERN_ERR + "tm6000: couldn't register frontend\n"); + goto adapter_err; + } + + if (!dvb_attach(xc5000_attach, dvb->frontend, &dev->i2c_adap, &cfg)) { + printk(KERN_ERR "tm6000: couldn't register frontend (xc5000)\n"); + ret = -EINVAL; + goto frontend_err; + } + printk(KERN_INFO "tm6000: XC5000 asked to be attached to frontend!\n"); + break; + } + } + } else + printk(KERN_ERR "tm6000: no frontend found\n"); + + dvb->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING + | DMX_MEMORY_BASED_FILTERING; + dvb->demux.priv = dev; + dvb->demux.filternum = 8; + dvb->demux.feednum = 8; + dvb->demux.start_feed = tm6000_start_feed; + dvb->demux.stop_feed = tm6000_stop_feed; + dvb->demux.write_to_decoder = NULL; + ret = dvb_dmx_init(&dvb->demux); + if (ret < 0) { + printk(KERN_ERR "tm6000: dvb_dmx_init failed (errno = %d)\n", ret); + goto frontend_err; + } + + dvb->dmxdev.filternum = dev->dvb->demux.filternum; + dvb->dmxdev.demux = &dev->dvb->demux.dmx; + dvb->dmxdev.capabilities = 0; + + ret = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter); + if (ret < 0) { + printk(KERN_ERR "tm6000: dvb_dmxdev_init failed (errno = %d)\n", ret); + goto dvb_dmx_err; + } + + return 0; + +dvb_dmx_err: + dvb_dmx_release(&dvb->demux); +frontend_err: + if (dvb->frontend) { + dvb_unregister_frontend(dvb->frontend); + dvb_frontend_detach(dvb->frontend); + } +adapter_err: + dvb_unregister_adapter(&dvb->adapter); +err: + return ret; +} + +static void unregister_dvb(struct tm6000_core *dev) +{ + struct tm6000_dvb *dvb = dev->dvb; + + if (dvb->bulk_urb) { + struct urb *bulk_urb = dvb->bulk_urb; + + kfree(bulk_urb->transfer_buffer); + bulk_urb->transfer_buffer = NULL; + usb_unlink_urb(bulk_urb); + usb_free_urb(bulk_urb); + } + +/* mutex_lock(&tm6000_driver.open_close_mutex); */ + if (dvb->frontend) { + dvb_unregister_frontend(dvb->frontend); + dvb_frontend_detach(dvb->frontend); + } + + dvb_dmxdev_release(&dvb->dmxdev); + dvb_dmx_release(&dvb->demux); + dvb_unregister_adapter(&dvb->adapter); + mutex_destroy(&dvb->mutex); +/* mutex_unlock(&tm6000_driver.open_close_mutex); */ +} + +static int dvb_init(struct tm6000_core *dev) +{ + struct tm6000_dvb *dvb; + int rc; + + if (!dev) + return 0; + + if (!dev->caps.has_dvb) + return 0; + + if (dev->udev->speed == USB_SPEED_FULL) { + printk(KERN_INFO "This USB2.0 device cannot be run on a USB1.1 port. (it lacks a hardware PID filter)\n"); + return 0; + } + + dvb = kzalloc(sizeof(struct tm6000_dvb), GFP_KERNEL); + if (!dvb) + return -ENOMEM; + + dev->dvb = dvb; + + rc = register_dvb(dev); + if (rc < 0) { + kfree(dvb); + dev->dvb = NULL; + return 0; + } + + return 0; +} + +static int dvb_fini(struct tm6000_core *dev) +{ + if (!dev) + return 0; + + if (!dev->caps.has_dvb) + return 0; + + if (dev->dvb) { + unregister_dvb(dev); + kfree(dev->dvb); + dev->dvb = NULL; + } + + return 0; +} + +static struct tm6000_ops dvb_ops = { + .type = TM6000_DVB, + .name = "TM6000 dvb Extension", + .init = dvb_init, + .fini = dvb_fini, +}; + +static int __init tm6000_dvb_register(void) +{ + return tm6000_register_extension(&dvb_ops); +} + +static void __exit tm6000_dvb_unregister(void) +{ + tm6000_unregister_extension(&dvb_ops); +} + +module_init(tm6000_dvb_register); +module_exit(tm6000_dvb_unregister); diff --git a/drivers/staging/media/deprecated/tm6000/tm6000-i2c.c b/drivers/staging/media/deprecated/tm6000/tm6000-i2c.c new file mode 100644 index 000000000000..7554b93b82e6 --- /dev/null +++ b/drivers/staging/media/deprecated/tm6000/tm6000-i2c.c @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: GPL-2.0 +// tm6000-i2c.c - driver for TM5600/TM6000/TM6010 USB video capture devices +// +// Copyright (c) 2006-2007 Mauro Carvalho Chehab <mchehab@kernel.org> +// +// Copyright (c) 2007 Michel Ludwig <michel.ludwig@gmail.com> +// - Fix SMBus Read Byte command + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/usb.h> +#include <linux/i2c.h> + +#include "tm6000.h" +#include "tm6000-regs.h" +#include <media/v4l2-common.h> +#include <media/tuner.h> +#include "xc2028.h" + + +/* ----------------------------------------------------------- */ + +static unsigned int i2c_debug; +module_param(i2c_debug, int, 0644); +MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); + +#define i2c_dprintk(lvl, fmt, args...) if (i2c_debug >= lvl) do { \ + printk(KERN_DEBUG "%s at %s: " fmt, \ + dev->name, __func__, ##args); } while (0) + +static int tm6000_i2c_send_regs(struct tm6000_core *dev, unsigned char addr, + __u8 reg, char *buf, int len) +{ + int rc; + unsigned int i2c_packet_limit = 16; + + if (dev->dev_type == TM6010) + i2c_packet_limit = 80; + + if (!buf) + return -1; + + if (len < 1 || len > i2c_packet_limit) { + printk(KERN_ERR "Incorrect length of i2c packet = %d, limit set to %d\n", + len, i2c_packet_limit); + return -1; + } + + /* capture mutex */ + rc = tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, REQ_16_SET_GET_I2C_WR1_RDN, + addr | reg << 8, 0, buf, len); + + if (rc < 0) { + /* release mutex */ + return rc; + } + + /* release mutex */ + return rc; +} + +/* Generic read - doesn't work fine with 16bit registers */ +static int tm6000_i2c_recv_regs(struct tm6000_core *dev, unsigned char addr, + __u8 reg, char *buf, int len) +{ + int rc; + u8 b[2]; + unsigned int i2c_packet_limit = 16; + + if (dev->dev_type == TM6010) + i2c_packet_limit = 64; + + if (!buf) + return -1; + + if (len < 1 || len > i2c_packet_limit) { + printk(KERN_ERR "Incorrect length of i2c packet = %d, limit set to %d\n", + len, i2c_packet_limit); + return -1; + } + + /* capture mutex */ + if ((dev->caps.has_zl10353) && (dev->demod_addr << 1 == addr) && (reg % 2 == 0)) { + /* + * Workaround an I2C bug when reading from zl10353 + */ + reg -= 1; + len += 1; + + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + REQ_16_SET_GET_I2C_WR1_RDN, addr | reg << 8, 0, b, len); + + *buf = b[1]; + } else { + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + REQ_16_SET_GET_I2C_WR1_RDN, addr | reg << 8, 0, buf, len); + } + + /* release mutex */ + return rc; +} + +/* + * read from a 16bit register + * for example xc2028, xc3028 or xc3028L + */ +static int tm6000_i2c_recv_regs16(struct tm6000_core *dev, unsigned char addr, + __u16 reg, char *buf, int len) +{ + int rc; + unsigned char ureg; + + if (!buf || len != 2) + return -1; + + /* capture mutex */ + if (dev->dev_type == TM6010) { + ureg = reg & 0xFF; + rc = tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, REQ_16_SET_GET_I2C_WR1_RDN, + addr | (reg & 0xFF00), 0, &ureg, 1); + + if (rc < 0) { + /* release mutex */ + return rc; + } + + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, REQ_35_AFTEK_TUNER_READ, + reg, 0, buf, len); + } else { + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, REQ_14_SET_GET_I2C_WR2_RDN, + addr, reg, buf, len); + } + + /* release mutex */ + return rc; +} + +static int tm6000_i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], int num) +{ + struct tm6000_core *dev = i2c_adap->algo_data; + int addr, rc, i, byte; + + for (i = 0; i < num; i++) { + addr = (msgs[i].addr << 1) & 0xff; + i2c_dprintk(2, "%s %s addr=0x%x len=%d:", + (msgs[i].flags & I2C_M_RD) ? "read" : "write", + i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len); + if (msgs[i].flags & I2C_M_RD) { + /* read request without preceding register selection */ + /* + * The TM6000 only supports a read transaction + * immediately after a 1 or 2 byte write to select + * a register. We cannot fulfill this request. + */ + i2c_dprintk(2, " read without preceding write not supported"); + rc = -EOPNOTSUPP; + goto err; + } else if (i + 1 < num && msgs[i].len <= 2 && + (msgs[i + 1].flags & I2C_M_RD) && + msgs[i].addr == msgs[i + 1].addr) { + /* 1 or 2 byte write followed by a read */ + if (i2c_debug >= 2) + for (byte = 0; byte < msgs[i].len; byte++) + printk(KERN_CONT " %02x", msgs[i].buf[byte]); + i2c_dprintk(2, "; joined to read %s len=%d:", + i == num - 2 ? "stop" : "nonstop", + msgs[i + 1].len); + + if (msgs[i].len == 2) { + rc = tm6000_i2c_recv_regs16(dev, addr, + msgs[i].buf[0] << 8 | msgs[i].buf[1], + msgs[i + 1].buf, msgs[i + 1].len); + } else { + rc = tm6000_i2c_recv_regs(dev, addr, msgs[i].buf[0], + msgs[i + 1].buf, msgs[i + 1].len); + } + + i++; + + if (addr == dev->tuner_addr << 1) { + tm6000_set_reg(dev, REQ_50_SET_START, 0, 0); + tm6000_set_reg(dev, REQ_51_SET_STOP, 0, 0); + } + if (i2c_debug >= 2) + for (byte = 0; byte < msgs[i].len; byte++) + printk(KERN_CONT " %02x", msgs[i].buf[byte]); + } else { + /* write bytes */ + if (i2c_debug >= 2) + for (byte = 0; byte < msgs[i].len; byte++) + printk(KERN_CONT " %02x", msgs[i].buf[byte]); + rc = tm6000_i2c_send_regs(dev, addr, msgs[i].buf[0], + msgs[i].buf + 1, msgs[i].len - 1); + } + if (i2c_debug >= 2) + printk(KERN_CONT "\n"); + if (rc < 0) + goto err; + } + + return num; +err: + i2c_dprintk(2, " ERROR: %i\n", rc); + return rc; +} + +static int tm6000_i2c_eeprom(struct tm6000_core *dev) +{ + int i, rc; + unsigned char *p = dev->eedata; + unsigned char bytes[17]; + + dev->i2c_client.addr = 0xa0 >> 1; + dev->eedata_size = 0; + + bytes[16] = '\0'; + for (i = 0; i < sizeof(dev->eedata); ) { + *p = i; + rc = tm6000_i2c_recv_regs(dev, 0xa0, i, p, 1); + if (rc < 1) { + if (p == dev->eedata) + goto noeeprom; + else { + printk(KERN_WARNING + "%s: i2c eeprom read error (err=%d)\n", + dev->name, rc); + } + return -EINVAL; + } + dev->eedata_size++; + p++; + if (0 == (i % 16)) + printk(KERN_INFO "%s: i2c eeprom %02x:", dev->name, i); + printk(KERN_CONT " %02x", dev->eedata[i]); + if ((dev->eedata[i] >= ' ') && (dev->eedata[i] <= 'z')) + bytes[i%16] = dev->eedata[i]; + else + bytes[i%16] = '.'; + + i++; + + if (0 == (i % 16)) { + bytes[16] = '\0'; + printk(KERN_CONT " %s\n", bytes); + } + } + if (0 != (i%16)) { + bytes[i%16] = '\0'; + for (i %= 16; i < 16; i++) + printk(KERN_CONT " "); + printk(KERN_CONT " %s\n", bytes); + } + + return 0; + +noeeprom: + printk(KERN_INFO "%s: Huh, no eeprom present (err=%d)?\n", + dev->name, rc); + return -EINVAL; +} + +/* ----------------------------------------------------------- */ + +/* + * functionality() + */ +static u32 functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm tm6000_algo = { + .master_xfer = tm6000_i2c_xfer, + .functionality = functionality, +}; + +/* ----------------------------------------------------------- */ + +/* + * tm6000_i2c_register() + * register i2c bus + */ +int tm6000_i2c_register(struct tm6000_core *dev) +{ + int rc; + + dev->i2c_adap.owner = THIS_MODULE; + dev->i2c_adap.algo = &tm6000_algo; + dev->i2c_adap.dev.parent = &dev->udev->dev; + strscpy(dev->i2c_adap.name, dev->name, sizeof(dev->i2c_adap.name)); + dev->i2c_adap.algo_data = dev; + i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev); + rc = i2c_add_adapter(&dev->i2c_adap); + if (rc) + return rc; + + dev->i2c_client.adapter = &dev->i2c_adap; + strscpy(dev->i2c_client.name, "tm6000 internal", I2C_NAME_SIZE); + tm6000_i2c_eeprom(dev); + + return 0; +} + +/* + * tm6000_i2c_unregister() + * unregister i2c_bus + */ +int tm6000_i2c_unregister(struct tm6000_core *dev) +{ + i2c_del_adapter(&dev->i2c_adap); + return 0; +} diff --git a/drivers/staging/media/deprecated/tm6000/tm6000-input.c b/drivers/staging/media/deprecated/tm6000/tm6000-input.c new file mode 100644 index 000000000000..5136e9e202f1 --- /dev/null +++ b/drivers/staging/media/deprecated/tm6000/tm6000-input.c @@ -0,0 +1,503 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * tm6000-input.c - driver for TM5600/TM6000/TM6010 USB video capture devices + * + * Copyright (C) 2010 Stefan Ringel <stefan.ringel@arcor.de> + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> + +#include <linux/input.h> +#include <linux/usb.h> + +#include <media/rc-core.h> + +#include "tm6000.h" +#include "tm6000-regs.h" + +static unsigned int ir_debug; +module_param(ir_debug, int, 0644); +MODULE_PARM_DESC(ir_debug, "debug message level"); + +static unsigned int enable_ir = 1; +module_param(enable_ir, int, 0644); +MODULE_PARM_DESC(enable_ir, "enable ir (default is enable)"); + +static unsigned int ir_clock_mhz = 12; +module_param(ir_clock_mhz, int, 0644); +MODULE_PARM_DESC(ir_clock_mhz, "ir clock, in MHz"); + +#define URB_SUBMIT_DELAY 100 /* ms - Delay to submit an URB request on retrial and init */ +#define URB_INT_LED_DELAY 100 /* ms - Delay to turn led on again on int mode */ + +#undef dprintk + +#define dprintk(level, fmt, arg...) do {\ + if (ir_debug >= level) \ + printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \ + } while (0) + +struct tm6000_ir_poll_result { + u16 rc_data; +}; + +struct tm6000_IR { + struct tm6000_core *dev; + struct rc_dev *rc; + char name[32]; + char phys[32]; + + /* poll expernal decoder */ + int polling; + struct delayed_work work; + u8 wait:1; + u8 pwled:2; + u8 submit_urb:1; + struct urb *int_urb; + + /* IR device properties */ + u64 rc_proto; +}; + +void tm6000_ir_wait(struct tm6000_core *dev, u8 state) +{ + struct tm6000_IR *ir = dev->ir; + + if (!dev->ir) + return; + + dprintk(2, "%s: %i\n",__func__, ir->wait); + + if (state) + ir->wait = 1; + else + ir->wait = 0; +} + +static int tm6000_ir_config(struct tm6000_IR *ir) +{ + struct tm6000_core *dev = ir->dev; + u32 pulse = 0, leader = 0; + + dprintk(2, "%s\n",__func__); + + /* + * The IR decoder supports RC-5 or NEC, with a configurable timing. + * The timing configuration there is not that accurate, as it uses + * approximate values. The NEC spec mentions a 562.5 unit period, + * and RC-5 uses a 888.8 period. + * Currently, driver assumes a clock provided by a 12 MHz XTAL, but + * a modprobe parameter can adjust it. + * Adjustments are required for other timings. + * It seems that the 900ms timing for NEC is used to detect a RC-5 + * IR, in order to discard such decoding + */ + + switch (ir->rc_proto) { + case RC_PROTO_BIT_NEC: + leader = 900; /* ms */ + pulse = 700; /* ms - the actual value would be 562 */ + break; + default: + case RC_PROTO_BIT_RC5: + leader = 900; /* ms - from the NEC decoding */ + pulse = 1780; /* ms - The actual value would be 1776 */ + break; + } + + pulse = ir_clock_mhz * pulse; + leader = ir_clock_mhz * leader; + if (ir->rc_proto == RC_PROTO_BIT_NEC) + leader = leader | 0x8000; + + dprintk(2, "%s: %s, %d MHz, leader = 0x%04x, pulse = 0x%06x \n", + __func__, + (ir->rc_proto == RC_PROTO_BIT_NEC) ? "NEC" : "RC-5", + ir_clock_mhz, leader, pulse); + + /* Remote WAKEUP = enable, normal mode, from IR decoder output */ + tm6000_set_reg(dev, TM6010_REQ07_RE5_REMOTE_WAKEUP, 0xfe); + + /* Enable IR reception on non-busrt mode */ + tm6000_set_reg(dev, TM6010_REQ07_RD8_IR, 0x2f); + + /* IR_WKUP_SEL = Low byte in decoded IR data */ + tm6000_set_reg(dev, TM6010_REQ07_RDA_IR_WAKEUP_SEL, 0xff); + /* IR_WKU_ADD code */ + tm6000_set_reg(dev, TM6010_REQ07_RDB_IR_WAKEUP_ADD, 0xff); + + tm6000_set_reg(dev, TM6010_REQ07_RDC_IR_LEADER1, leader >> 8); + tm6000_set_reg(dev, TM6010_REQ07_RDD_IR_LEADER0, leader); + + tm6000_set_reg(dev, TM6010_REQ07_RDE_IR_PULSE_CNT1, pulse >> 8); + tm6000_set_reg(dev, TM6010_REQ07_RDF_IR_PULSE_CNT0, pulse); + + if (!ir->polling) + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 0); + else + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 1); + msleep(10); + + /* Shows that IR is working via the LED */ + tm6000_flash_led(dev, 0); + msleep(100); + tm6000_flash_led(dev, 1); + ir->pwled = 1; + + return 0; +} + +static void tm6000_ir_keydown(struct tm6000_IR *ir, + const char *buf, unsigned int len) +{ + u8 device, command; + u32 scancode; + enum rc_proto protocol; + + if (len < 1) + return; + + command = buf[0]; + device = (len > 1 ? buf[1] : 0x0); + switch (ir->rc_proto) { + case RC_PROTO_BIT_RC5: + protocol = RC_PROTO_RC5; + scancode = RC_SCANCODE_RC5(device, command); + break; + case RC_PROTO_BIT_NEC: + protocol = RC_PROTO_NEC; + scancode = RC_SCANCODE_NEC(device, command); + break; + default: + protocol = RC_PROTO_OTHER; + scancode = RC_SCANCODE_OTHER(device << 8 | command); + break; + } + + dprintk(1, "%s, protocol: 0x%04x, scancode: 0x%08x\n", + __func__, protocol, scancode); + rc_keydown(ir->rc, protocol, scancode, 0); +} + +static void tm6000_ir_urb_received(struct urb *urb) +{ + struct tm6000_core *dev = urb->context; + struct tm6000_IR *ir = dev->ir; + char *buf; + + dprintk(2, "%s\n",__func__); + if (urb->status < 0 || urb->actual_length <= 0) { + printk(KERN_INFO "tm6000: IR URB failure: status: %i, length %i\n", + urb->status, urb->actual_length); + ir->submit_urb = 1; + schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_SUBMIT_DELAY)); + return; + } + buf = urb->transfer_buffer; + + if (ir_debug) + print_hex_dump(KERN_DEBUG, "tm6000: IR data: ", + DUMP_PREFIX_OFFSET,16, 1, + buf, urb->actual_length, false); + + tm6000_ir_keydown(ir, urb->transfer_buffer, urb->actual_length); + + usb_submit_urb(urb, GFP_ATOMIC); + /* + * Flash the led. We can't do it here, as it is running on IRQ context. + * So, use the scheduler to do it, in a few ms. + */ + ir->pwled = 2; + schedule_delayed_work(&ir->work, msecs_to_jiffies(10)); +} + +static void tm6000_ir_handle_key(struct work_struct *work) +{ + struct tm6000_IR *ir = container_of(work, struct tm6000_IR, work.work); + struct tm6000_core *dev = ir->dev; + int rc; + u8 buf[2]; + + if (ir->wait) + return; + + dprintk(3, "%s\n",__func__); + + rc = tm6000_read_write_usb(dev, USB_DIR_IN | + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + REQ_02_GET_IR_CODE, 0, 0, buf, 2); + if (rc < 0) + return; + + /* Check if something was read */ + if ((buf[0] & 0xff) == 0xff) { + if (!ir->pwled) { + tm6000_flash_led(dev, 1); + ir->pwled = 1; + } + return; + } + + tm6000_ir_keydown(ir, buf, rc); + tm6000_flash_led(dev, 0); + ir->pwled = 0; + + /* Re-schedule polling */ + schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); +} + +static void tm6000_ir_int_work(struct work_struct *work) +{ + struct tm6000_IR *ir = container_of(work, struct tm6000_IR, work.work); + struct tm6000_core *dev = ir->dev; + int rc; + + dprintk(3, "%s, submit_urb = %d, pwled = %d\n",__func__, ir->submit_urb, + ir->pwled); + + if (ir->submit_urb) { + dprintk(3, "Resubmit urb\n"); + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 0); + + rc = usb_submit_urb(ir->int_urb, GFP_ATOMIC); + if (rc < 0) { + printk(KERN_ERR "tm6000: Can't submit an IR interrupt. Error %i\n", + rc); + /* Retry in 100 ms */ + schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_SUBMIT_DELAY)); + return; + } + ir->submit_urb = 0; + } + + /* Led is enabled only if USB submit doesn't fail */ + if (ir->pwled == 2) { + tm6000_flash_led(dev, 0); + ir->pwled = 0; + schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_INT_LED_DELAY)); + } else if (!ir->pwled) { + tm6000_flash_led(dev, 1); + ir->pwled = 1; + } +} + +static int tm6000_ir_start(struct rc_dev *rc) +{ + struct tm6000_IR *ir = rc->priv; + + dprintk(2, "%s\n",__func__); + + schedule_delayed_work(&ir->work, 0); + + return 0; +} + +static void tm6000_ir_stop(struct rc_dev *rc) +{ + struct tm6000_IR *ir = rc->priv; + + dprintk(2, "%s\n",__func__); + + cancel_delayed_work_sync(&ir->work); +} + +static int tm6000_ir_change_protocol(struct rc_dev *rc, u64 *rc_proto) +{ + struct tm6000_IR *ir = rc->priv; + + if (!ir) + return 0; + + dprintk(2, "%s\n",__func__); + + ir->rc_proto = *rc_proto; + + tm6000_ir_config(ir); + /* TODO */ + return 0; +} + +static int __tm6000_ir_int_start(struct rc_dev *rc) +{ + struct tm6000_IR *ir = rc->priv; + struct tm6000_core *dev; + int pipe, size; + int err = -ENOMEM; + + if (!ir) + return -ENODEV; + dev = ir->dev; + + dprintk(2, "%s\n",__func__); + + ir->int_urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!ir->int_urb) + return -ENOMEM; + + pipe = usb_rcvintpipe(dev->udev, + dev->int_in.endp->desc.bEndpointAddress + & USB_ENDPOINT_NUMBER_MASK); + + size = usb_maxpacket(dev->udev, pipe); + dprintk(1, "IR max size: %d\n", size); + + ir->int_urb->transfer_buffer = kzalloc(size, GFP_ATOMIC); + if (!ir->int_urb->transfer_buffer) { + usb_free_urb(ir->int_urb); + return err; + } + dprintk(1, "int interval: %d\n", dev->int_in.endp->desc.bInterval); + + usb_fill_int_urb(ir->int_urb, dev->udev, pipe, + ir->int_urb->transfer_buffer, size, + tm6000_ir_urb_received, dev, + dev->int_in.endp->desc.bInterval); + + ir->submit_urb = 1; + schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_SUBMIT_DELAY)); + + return 0; +} + +static void __tm6000_ir_int_stop(struct rc_dev *rc) +{ + struct tm6000_IR *ir = rc->priv; + + if (!ir || !ir->int_urb) + return; + + dprintk(2, "%s\n",__func__); + + usb_kill_urb(ir->int_urb); + kfree(ir->int_urb->transfer_buffer); + usb_free_urb(ir->int_urb); + ir->int_urb = NULL; +} + +int tm6000_ir_int_start(struct tm6000_core *dev) +{ + struct tm6000_IR *ir = dev->ir; + + if (!ir) + return 0; + + return __tm6000_ir_int_start(ir->rc); +} + +void tm6000_ir_int_stop(struct tm6000_core *dev) +{ + struct tm6000_IR *ir = dev->ir; + + if (!ir || !ir->rc) + return; + + __tm6000_ir_int_stop(ir->rc); +} + +int tm6000_ir_init(struct tm6000_core *dev) +{ + struct tm6000_IR *ir; + struct rc_dev *rc; + int err = -ENOMEM; + u64 rc_proto; + + if (!enable_ir) + return -ENODEV; + + if (!dev->caps.has_remote) + return 0; + + if (!dev->ir_codes) + return 0; + + ir = kzalloc(sizeof(*ir), GFP_ATOMIC); + rc = rc_allocate_device(RC_DRIVER_SCANCODE); + if (!ir || !rc) + goto out; + + dprintk(2, "%s\n", __func__); + + /* record handles to ourself */ + ir->dev = dev; + dev->ir = ir; + ir->rc = rc; + + /* input setup */ + rc->allowed_protocols = RC_PROTO_BIT_RC5 | RC_PROTO_BIT_NEC; + /* Needed, in order to support NEC remotes with 24 or 32 bits */ + rc->scancode_mask = 0xffff; + rc->priv = ir; + rc->change_protocol = tm6000_ir_change_protocol; + if (dev->int_in.endp) { + rc->open = __tm6000_ir_int_start; + rc->close = __tm6000_ir_int_stop; + INIT_DELAYED_WORK(&ir->work, tm6000_ir_int_work); + } else { + rc->open = tm6000_ir_start; + rc->close = tm6000_ir_stop; + ir->polling = 50; + INIT_DELAYED_WORK(&ir->work, tm6000_ir_handle_key); + } + + snprintf(ir->name, sizeof(ir->name), "tm5600/60x0 IR (%s)", + dev->name); + + usb_make_path(dev->udev, ir->phys, sizeof(ir->phys)); + strlcat(ir->phys, "/input0", sizeof(ir->phys)); + + rc_proto = RC_PROTO_BIT_UNKNOWN; + tm6000_ir_change_protocol(rc, &rc_proto); + + rc->device_name = ir->name; + rc->input_phys = ir->phys; + rc->input_id.bustype = BUS_USB; + rc->input_id.version = 1; + rc->input_id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); + rc->input_id.product = le16_to_cpu(dev->udev->descriptor.idProduct); + rc->map_name = dev->ir_codes; + rc->driver_name = "tm6000"; + rc->dev.parent = &dev->udev->dev; + + /* ir register */ + err = rc_register_device(rc); + if (err) + goto out; + + return 0; + +out: + dev->ir = NULL; + rc_free_device(rc); + kfree(ir); + return err; +} + +int tm6000_ir_fini(struct tm6000_core *dev) +{ + struct tm6000_IR *ir = dev->ir; + + /* skip detach on non attached board */ + + if (!ir) + return 0; + + dprintk(2, "%s\n",__func__); + + if (!ir->polling) + __tm6000_ir_int_stop(ir->rc); + + tm6000_ir_stop(ir->rc); + + /* Turn off the led */ + tm6000_flash_led(dev, 0); + ir->pwled = 0; + + rc_unregister_device(ir->rc); + + kfree(ir); + dev->ir = NULL; + + return 0; +} diff --git a/drivers/staging/media/deprecated/tm6000/tm6000-regs.h b/drivers/staging/media/deprecated/tm6000/tm6000-regs.h new file mode 100644 index 000000000000..6a181f2e7ef2 --- /dev/null +++ b/drivers/staging/media/deprecated/tm6000/tm6000-regs.h @@ -0,0 +1,588 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * tm6000-regs.h - driver for TM5600/TM6000/TM6010 USB video capture devices + * + * Copyright (c) 2006-2007 Mauro Carvalho Chehab <mchehab@kernel.org> + */ + +/* + * Define TV Master TM5600/TM6000/TM6010 Request codes + */ +#define REQ_00_SET_IR_VALUE 0 +#define REQ_01_SET_WAKEUP_IRCODE 1 +#define REQ_02_GET_IR_CODE 2 +#define REQ_03_SET_GET_MCU_PIN 3 +#define REQ_04_EN_DISABLE_MCU_INT 4 +#define REQ_05_SET_GET_USBREG 5 + /* Write: RegNum, Value, 0 */ + /* Read : RegNum, Value, 1, RegStatus */ +#define REQ_06_SET_GET_USBREG_BIT 6 +#define REQ_07_SET_GET_AVREG 7 + /* Write: RegNum, Value, 0 */ + /* Read : RegNum, Value, 1, RegStatus */ +#define REQ_08_SET_GET_AVREG_BIT 8 +#define REQ_09_SET_GET_TUNER_FQ 9 +#define REQ_10_SET_TUNER_SYSTEM 10 +#define REQ_11_SET_EEPROM_ADDR 11 +#define REQ_12_SET_GET_EEPROMBYTE 12 +#define REQ_13_GET_EEPROM_SEQREAD 13 +#define REQ_14_SET_GET_I2C_WR2_RDN 14 +#define REQ_15_SET_GET_I2CBYTE 15 + /* Write: Subaddr, Slave Addr, value, 0 */ + /* Read : Subaddr, Slave Addr, value, 1 */ +#define REQ_16_SET_GET_I2C_WR1_RDN 16 + /* Subaddr, Slave Addr, 0, length */ +#define REQ_17_SET_GET_I2CFP 17 + /* Write: Slave Addr, register, value */ + /* Read : Slave Addr, register, 2, data */ +#define REQ_20_DATA_TRANSFER 20 +#define REQ_30_I2C_WRITE 30 +#define REQ_31_I2C_READ 31 +#define REQ_35_AFTEK_TUNER_READ 35 +#define REQ_40_GET_VERSION 40 +#define REQ_50_SET_START 50 +#define REQ_51_SET_STOP 51 +#define REQ_52_TRANSMIT_DATA 52 +#define REQ_53_SPI_INITIAL 53 +#define REQ_54_SPI_SETSTART 54 +#define REQ_55_SPI_INOUTDATA 55 +#define REQ_56_SPI_SETSTOP 56 + +/* + * Define TV Master TM5600/TM6000/TM6010 GPIO lines + */ + +#define TM6000_GPIO_CLK 0x101 +#define TM6000_GPIO_DATA 0x100 + +#define TM6000_GPIO_1 0x102 +#define TM6000_GPIO_2 0x103 +#define TM6000_GPIO_3 0x104 +#define TM6000_GPIO_4 0x300 +#define TM6000_GPIO_5 0x301 +#define TM6000_GPIO_6 0x304 +#define TM6000_GPIO_7 0x305 + +/* tm6010 defines GPIO with different values */ +#define TM6010_GPIO_0 0x0102 +#define TM6010_GPIO_1 0x0103 +#define TM6010_GPIO_2 0x0104 +#define TM6010_GPIO_3 0x0105 +#define TM6010_GPIO_4 0x0106 +#define TM6010_GPIO_5 0x0107 +#define TM6010_GPIO_6 0x0300 +#define TM6010_GPIO_7 0x0301 +#define TM6010_GPIO_9 0x0305 +/* + * Define TV Master TM5600/TM6000/TM6010 URB message codes and length + */ + +enum { + TM6000_URB_MSG_VIDEO = 1, + TM6000_URB_MSG_AUDIO, + TM6000_URB_MSG_VBI, + TM6000_URB_MSG_PTS, + TM6000_URB_MSG_ERR, +}; + +/* Define specific TM6000 Video decoder registers */ +#define TM6000_REQ07_RD8_TEST_SEL 0x07, 0xd8 +#define TM6000_REQ07_RD9_A_SIM_SEL 0x07, 0xd9 +#define TM6000_REQ07_RDA_CLK_SEL 0x07, 0xda +#define TM6000_REQ07_RDB_OUT_SEL 0x07, 0xdb +#define TM6000_REQ07_RDC_NSEL_I2S 0x07, 0xdc +#define TM6000_REQ07_RDD_GPIO2_MDRV 0x07, 0xdd +#define TM6000_REQ07_RDE_GPIO1_MDRV 0x07, 0xde +#define TM6000_REQ07_RDF_PWDOWN_ACLK 0x07, 0xdf +#define TM6000_REQ07_RE0_VADC_REF_CTL 0x07, 0xe0 +#define TM6000_REQ07_RE1_VADC_DACLIMP 0x07, 0xe1 +#define TM6000_REQ07_RE2_VADC_STATUS_CTL 0x07, 0xe2 +#define TM6000_REQ07_RE3_VADC_INP_LPF_SEL1 0x07, 0xe3 +#define TM6000_REQ07_RE4_VADC_TARGET1 0x07, 0xe4 +#define TM6000_REQ07_RE5_VADC_INP_LPF_SEL2 0x07, 0xe5 +#define TM6000_REQ07_RE6_VADC_TARGET2 0x07, 0xe6 +#define TM6000_REQ07_RE7_VADC_AGAIN_CTL 0x07, 0xe7 +#define TM6000_REQ07_RE8_VADC_PWDOWN_CTL 0x07, 0xe8 +#define TM6000_REQ07_RE9_VADC_INPUT_CTL1 0x07, 0xe9 +#define TM6000_REQ07_REA_VADC_INPUT_CTL2 0x07, 0xea +#define TM6000_REQ07_REB_VADC_AADC_MODE 0x07, 0xeb +#define TM6000_REQ07_REC_VADC_AADC_LVOL 0x07, 0xec +#define TM6000_REQ07_RED_VADC_AADC_RVOL 0x07, 0xed +#define TM6000_REQ07_REE_VADC_CTRL_SEL_CONTROL 0x07, 0xee +#define TM6000_REQ07_REF_VADC_GAIN_MAP_CTL 0x07, 0xef +#define TM6000_REQ07_RFD_BIST_ERR_VST_LOW 0x07, 0xfd +#define TM6000_REQ07_RFE_BIST_ERR_VST_HIGH 0x07, 0xfe + +/* Define TM6000/TM6010 Video decoder registers */ +#define TM6010_REQ07_R00_VIDEO_CONTROL0 0x07, 0x00 +#define TM6010_REQ07_R01_VIDEO_CONTROL1 0x07, 0x01 +#define TM6010_REQ07_R02_VIDEO_CONTROL2 0x07, 0x02 +#define TM6010_REQ07_R03_YC_SEP_CONTROL 0x07, 0x03 +#define TM6010_REQ07_R04_LUMA_HAGC_CONTROL 0x07, 0x04 +#define TM6010_REQ07_R05_NOISE_THRESHOLD 0x07, 0x05 +#define TM6010_REQ07_R06_AGC_GATE_THRESHOLD 0x07, 0x06 +#define TM6010_REQ07_R07_OUTPUT_CONTROL 0x07, 0x07 +#define TM6010_REQ07_R08_LUMA_CONTRAST_ADJ 0x07, 0x08 +#define TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ 0x07, 0x09 +#define TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ 0x07, 0x0a +#define TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ 0x07, 0x0b +#define TM6010_REQ07_R0C_CHROMA_AGC_CONTROL 0x07, 0x0c +#define TM6010_REQ07_R0D_CHROMA_KILL_LEVEL 0x07, 0x0d +#define TM6010_REQ07_R0F_CHROMA_AUTO_POSITION 0x07, 0x0f +#define TM6010_REQ07_R10_AGC_PEAK_NOMINAL 0x07, 0x10 +#define TM6010_REQ07_R11_AGC_PEAK_CONTROL 0x07, 0x11 +#define TM6010_REQ07_R12_AGC_GATE_STARTH 0x07, 0x12 +#define TM6010_REQ07_R13_AGC_GATE_STARTL 0x07, 0x13 +#define TM6010_REQ07_R14_AGC_GATE_WIDTH 0x07, 0x14 +#define TM6010_REQ07_R15_AGC_BP_DELAY 0x07, 0x15 +#define TM6010_REQ07_R16_LOCK_COUNT 0x07, 0x16 +#define TM6010_REQ07_R17_HLOOP_MAXSTATE 0x07, 0x17 +#define TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3 0x07, 0x18 +#define TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2 0x07, 0x19 +#define TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1 0x07, 0x1a +#define TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0 0x07, 0x1b +#define TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3 0x07, 0x1c +#define TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2 0x07, 0x1d +#define TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1 0x07, 0x1e +#define TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0 0x07, 0x1f +#define TM6010_REQ07_R20_HSYNC_RISING_EDGE_TIME 0x07, 0x20 +#define TM6010_REQ07_R21_HSYNC_PHASE_OFFSET 0x07, 0x21 +#define TM6010_REQ07_R22_HSYNC_PLL_START_TIME 0x07, 0x22 +#define TM6010_REQ07_R23_HSYNC_PLL_END_TIME 0x07, 0x23 +#define TM6010_REQ07_R24_HSYNC_TIP_START_TIME 0x07, 0x24 +#define TM6010_REQ07_R25_HSYNC_TIP_END_TIME 0x07, 0x25 +#define TM6010_REQ07_R26_HSYNC_RISING_EDGE_START 0x07, 0x26 +#define TM6010_REQ07_R27_HSYNC_RISING_EDGE_END 0x07, 0x27 +#define TM6010_REQ07_R28_BACKPORCH_START 0x07, 0x28 +#define TM6010_REQ07_R29_BACKPORCH_END 0x07, 0x29 +#define TM6010_REQ07_R2A_HSYNC_FILTER_START 0x07, 0x2a +#define TM6010_REQ07_R2B_HSYNC_FILTER_END 0x07, 0x2b +#define TM6010_REQ07_R2C_CHROMA_BURST_START 0x07, 0x2c +#define TM6010_REQ07_R2D_CHROMA_BURST_END 0x07, 0x2d +#define TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART 0x07, 0x2e +#define TM6010_REQ07_R2F_ACTIVE_VIDEO_HWIDTH 0x07, 0x2f +#define TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART 0x07, 0x30 +#define TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT 0x07, 0x31 +#define TM6010_REQ07_R32_VSYNC_HLOCK_MIN 0x07, 0x32 +#define TM6010_REQ07_R33_VSYNC_HLOCK_MAX 0x07, 0x33 +#define TM6010_REQ07_R34_VSYNC_AGC_MIN 0x07, 0x34 +#define TM6010_REQ07_R35_VSYNC_AGC_MAX 0x07, 0x35 +#define TM6010_REQ07_R36_VSYNC_VBI_MIN 0x07, 0x36 +#define TM6010_REQ07_R37_VSYNC_VBI_MAX 0x07, 0x37 +#define TM6010_REQ07_R38_VSYNC_THRESHOLD 0x07, 0x38 +#define TM6010_REQ07_R39_VSYNC_TIME_CONSTANT 0x07, 0x39 +#define TM6010_REQ07_R3A_STATUS1 0x07, 0x3a +#define TM6010_REQ07_R3B_STATUS2 0x07, 0x3b +#define TM6010_REQ07_R3C_STATUS3 0x07, 0x3c +#define TM6010_REQ07_R3F_RESET 0x07, 0x3f +#define TM6010_REQ07_R40_TELETEXT_VBI_CODE0 0x07, 0x40 +#define TM6010_REQ07_R41_TELETEXT_VBI_CODE1 0x07, 0x41 +#define TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL 0x07, 0x42 +#define TM6010_REQ07_R43_VBI_DATA_TYPE_LINE7 0x07, 0x43 +#define TM6010_REQ07_R44_VBI_DATA_TYPE_LINE8 0x07, 0x44 +#define TM6010_REQ07_R45_VBI_DATA_TYPE_LINE9 0x07, 0x45 +#define TM6010_REQ07_R46_VBI_DATA_TYPE_LINE10 0x07, 0x46 +#define TM6010_REQ07_R47_VBI_DATA_TYPE_LINE11 0x07, 0x47 +#define TM6010_REQ07_R48_VBI_DATA_TYPE_LINE12 0x07, 0x48 +#define TM6010_REQ07_R49_VBI_DATA_TYPE_LINE13 0x07, 0x49 +#define TM6010_REQ07_R4A_VBI_DATA_TYPE_LINE14 0x07, 0x4a +#define TM6010_REQ07_R4B_VBI_DATA_TYPE_LINE15 0x07, 0x4b +#define TM6010_REQ07_R4C_VBI_DATA_TYPE_LINE16 0x07, 0x4c +#define TM6010_REQ07_R4D_VBI_DATA_TYPE_LINE17 0x07, 0x4d +#define TM6010_REQ07_R4E_VBI_DATA_TYPE_LINE18 0x07, 0x4e +#define TM6010_REQ07_R4F_VBI_DATA_TYPE_LINE19 0x07, 0x4f +#define TM6010_REQ07_R50_VBI_DATA_TYPE_LINE20 0x07, 0x50 +#define TM6010_REQ07_R51_VBI_DATA_TYPE_LINE21 0x07, 0x51 +#define TM6010_REQ07_R52_VBI_DATA_TYPE_LINE22 0x07, 0x52 +#define TM6010_REQ07_R53_VBI_DATA_TYPE_LINE23 0x07, 0x53 +#define TM6010_REQ07_R54_VBI_DATA_TYPE_RLINES 0x07, 0x54 +#define TM6010_REQ07_R55_VBI_LOOP_FILTER_GAIN 0x07, 0x55 +#define TM6010_REQ07_R56_VBI_LOOP_FILTER_I_GAIN 0x07, 0x56 +#define TM6010_REQ07_R57_VBI_LOOP_FILTER_P_GAIN 0x07, 0x57 +#define TM6010_REQ07_R58_VBI_CAPTION_DTO1 0x07, 0x58 +#define TM6010_REQ07_R59_VBI_CAPTION_DTO0 0x07, 0x59 +#define TM6010_REQ07_R5A_VBI_TELETEXT_DTO1 0x07, 0x5a +#define TM6010_REQ07_R5B_VBI_TELETEXT_DTO0 0x07, 0x5b +#define TM6010_REQ07_R5C_VBI_WSS625_DTO1 0x07, 0x5c +#define TM6010_REQ07_R5D_VBI_WSS625_DTO0 0x07, 0x5d +#define TM6010_REQ07_R5E_VBI_CAPTION_FRAME_START 0x07, 0x5e +#define TM6010_REQ07_R5F_VBI_WSS625_FRAME_START 0x07, 0x5f +#define TM6010_REQ07_R60_TELETEXT_FRAME_START 0x07, 0x60 +#define TM6010_REQ07_R61_VBI_CCDATA1 0x07, 0x61 +#define TM6010_REQ07_R62_VBI_CCDATA2 0x07, 0x62 +#define TM6010_REQ07_R63_VBI_WSS625_DATA1 0x07, 0x63 +#define TM6010_REQ07_R64_VBI_WSS625_DATA2 0x07, 0x64 +#define TM6010_REQ07_R65_VBI_DATA_STATUS 0x07, 0x65 +#define TM6010_REQ07_R66_VBI_CAPTION_START 0x07, 0x66 +#define TM6010_REQ07_R67_VBI_WSS625_START 0x07, 0x67 +#define TM6010_REQ07_R68_VBI_TELETEXT_START 0x07, 0x68 +#define TM6010_REQ07_R70_HSYNC_DTO_INC_STATUS3 0x07, 0x70 +#define TM6010_REQ07_R71_HSYNC_DTO_INC_STATUS2 0x07, 0x71 +#define TM6010_REQ07_R72_HSYNC_DTO_INC_STATUS1 0x07, 0x72 +#define TM6010_REQ07_R73_HSYNC_DTO_INC_STATUS0 0x07, 0x73 +#define TM6010_REQ07_R74_CHROMA_DTO_INC_STATUS3 0x07, 0x74 +#define TM6010_REQ07_R75_CHROMA_DTO_INC_STATUS2 0x07, 0x75 +#define TM6010_REQ07_R76_CHROMA_DTO_INC_STATUS1 0x07, 0x76 +#define TM6010_REQ07_R77_CHROMA_DTO_INC_STATUS0 0x07, 0x77 +#define TM6010_REQ07_R78_AGC_AGAIN_STATUS 0x07, 0x78 +#define TM6010_REQ07_R79_AGC_DGAIN_STATUS 0x07, 0x79 +#define TM6010_REQ07_R7A_CHROMA_MAG_STATUS 0x07, 0x7a +#define TM6010_REQ07_R7B_CHROMA_GAIN_STATUS1 0x07, 0x7b +#define TM6010_REQ07_R7C_CHROMA_GAIN_STATUS0 0x07, 0x7c +#define TM6010_REQ07_R7D_CORDIC_FREQ_STATUS 0x07, 0x7d +#define TM6010_REQ07_R7F_STATUS_NOISE 0x07, 0x7f +#define TM6010_REQ07_R80_COMB_FILTER_TRESHOLD 0x07, 0x80 +#define TM6010_REQ07_R82_COMB_FILTER_CONFIG 0x07, 0x82 +#define TM6010_REQ07_R83_CHROMA_LOCK_CONFIG 0x07, 0x83 +#define TM6010_REQ07_R84_NOISE_NTSC_C 0x07, 0x84 +#define TM6010_REQ07_R85_NOISE_PAL_C 0x07, 0x85 +#define TM6010_REQ07_R86_NOISE_PHASE_C 0x07, 0x86 +#define TM6010_REQ07_R87_NOISE_PHASE_Y 0x07, 0x87 +#define TM6010_REQ07_R8A_CHROMA_LOOPFILTER_STATE 0x07, 0x8a +#define TM6010_REQ07_R8B_CHROMA_HRESAMPLER 0x07, 0x8b +#define TM6010_REQ07_R8D_CPUMP_DELAY_ADJ 0x07, 0x8d +#define TM6010_REQ07_R8E_CPUMP_ADJ 0x07, 0x8e +#define TM6010_REQ07_R8F_CPUMP_DELAY 0x07, 0x8f + +/* Define TM6000/TM6010 Miscellaneous registers */ +#define TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE 0x07, 0xc0 +#define TM6010_REQ07_RC1_TRESHOLD 0x07, 0xc1 +#define TM6010_REQ07_RC2_HSYNC_WIDTH 0x07, 0xc2 +#define TM6010_REQ07_RC3_HSTART1 0x07, 0xc3 +#define TM6010_REQ07_RC4_HSTART0 0x07, 0xc4 +#define TM6010_REQ07_RC5_HEND1 0x07, 0xc5 +#define TM6010_REQ07_RC6_HEND0 0x07, 0xc6 +#define TM6010_REQ07_RC7_VSTART1 0x07, 0xc7 +#define TM6010_REQ07_RC8_VSTART0 0x07, 0xc8 +#define TM6010_REQ07_RC9_VEND1 0x07, 0xc9 +#define TM6010_REQ07_RCA_VEND0 0x07, 0xca +#define TM6010_REQ07_RCB_DELAY 0x07, 0xcb +/* ONLY for TM6010 */ +#define TM6010_REQ07_RCC_ACTIVE_IF 0x07, 0xcc +#define TM6010_REQ07_RCC_ACTIVE_IF_VIDEO_ENABLE (1 << 5) +#define TM6010_REQ07_RCC_ACTIVE_IF_AUDIO_ENABLE (1 << 6) +#define TM6010_REQ07_RD0_USB_PERIPHERY_CONTROL 0x07, 0xd0 +#define TM6010_REQ07_RD1_ADDR_FOR_REQ1 0x07, 0xd1 +#define TM6010_REQ07_RD2_ADDR_FOR_REQ2 0x07, 0xd2 +#define TM6010_REQ07_RD3_ADDR_FOR_REQ3 0x07, 0xd3 +#define TM6010_REQ07_RD4_ADDR_FOR_REQ4 0x07, 0xd4 +#define TM6010_REQ07_RD5_POWERSAVE 0x07, 0xd5 +#define TM6010_REQ07_RD6_ENDP_REQ1_REQ2 0x07, 0xd6 +#define TM6010_REQ07_RD7_ENDP_REQ3_REQ4 0x07, 0xd7 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RD8_IR 0x07, 0xd8 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RD9_IR_BSIZE 0x07, 0xd9 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RDA_IR_WAKEUP_SEL 0x07, 0xda +/* ONLY for TM6010 */ +#define TM6010_REQ07_RDB_IR_WAKEUP_ADD 0x07, 0xdb +/* ONLY for TM6010 */ +#define TM6010_REQ07_RDC_IR_LEADER1 0x07, 0xdc +/* ONLY for TM6010 */ +#define TM6010_REQ07_RDD_IR_LEADER0 0x07, 0xdd +/* ONLY for TM6010 */ +#define TM6010_REQ07_RDE_IR_PULSE_CNT1 0x07, 0xde +/* ONLY for TM6010 */ +#define TM6010_REQ07_RDF_IR_PULSE_CNT0 0x07, 0xdf +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE0_DVIDEO_SOURCE 0x07, 0xe0 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE0_DVIDEO_SOURCE_IF 0x07, 0xe1 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE2_OUT_SEL2 0x07, 0xe2 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE3_OUT_SEL1 0x07, 0xe3 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE4_OUT_SEL0 0x07, 0xe4 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE5_REMOTE_WAKEUP 0x07, 0xe5 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE7_PUB_GPIO 0x07, 0xe7 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE8_TYPESEL_MOS_I2S 0x07, 0xe8 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RE9_TYPESEL_MOS_TS 0x07, 0xe9 +/* ONLY for TM6010 */ +#define TM6010_REQ07_REA_TYPESEL_MOS_CCIR 0x07, 0xea +/* ONLY for TM6010 */ +#define TM6010_REQ07_RF0_BIST_CRC_RESULT0 0x07, 0xf0 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RF1_BIST_CRC_RESULT1 0x07, 0xf1 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RF2_BIST_CRC_RESULT2 0x07, 0xf2 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RF3_BIST_CRC_RESULT3 0x07, 0xf3 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RF4_BIST_ERR_VST2 0x07, 0xf4 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RF5_BIST_ERR_VST1 0x07, 0xf5 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RF6_BIST_ERR_VST0 0x07, 0xf6 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RF7_BIST 0x07, 0xf7 +/* ONLY for TM6010 */ +#define TM6010_REQ07_RFE_POWER_DOWN 0x07, 0xfe +#define TM6010_REQ07_RFF_SOFT_RESET 0x07, 0xff + +/* Define TM6000/TM6010 USB registers */ +#define TM6010_REQ05_R00_MAIN_CTRL 0x05, 0x00 +#define TM6010_REQ05_R01_DEVADDR 0x05, 0x01 +#define TM6010_REQ05_R02_TEST 0x05, 0x02 +#define TM6010_REQ05_R04_SOFN0 0x05, 0x04 +#define TM6010_REQ05_R05_SOFN1 0x05, 0x05 +#define TM6010_REQ05_R06_SOFTM0 0x05, 0x06 +#define TM6010_REQ05_R07_SOFTM1 0x05, 0x07 +#define TM6010_REQ05_R08_PHY_TEST 0x05, 0x08 +#define TM6010_REQ05_R09_VCTL 0x05, 0x09 +#define TM6010_REQ05_R0A_VSTA 0x05, 0x0a +#define TM6010_REQ05_R0B_CX_CFG 0x05, 0x0b +#define TM6010_REQ05_R0C_ENDP0_REG0 0x05, 0x0c +#define TM6010_REQ05_R10_GMASK 0x05, 0x10 +#define TM6010_REQ05_R11_IMASK0 0x05, 0x11 +#define TM6010_REQ05_R12_IMASK1 0x05, 0x12 +#define TM6010_REQ05_R13_IMASK2 0x05, 0x13 +#define TM6010_REQ05_R14_IMASK3 0x05, 0x14 +#define TM6010_REQ05_R15_IMASK4 0x05, 0x15 +#define TM6010_REQ05_R16_IMASK5 0x05, 0x16 +#define TM6010_REQ05_R17_IMASK6 0x05, 0x17 +#define TM6010_REQ05_R18_IMASK7 0x05, 0x18 +#define TM6010_REQ05_R19_ZEROP0 0x05, 0x19 +#define TM6010_REQ05_R1A_ZEROP1 0x05, 0x1a +#define TM6010_REQ05_R1C_FIFO_EMP0 0x05, 0x1c +#define TM6010_REQ05_R1D_FIFO_EMP1 0x05, 0x1d +#define TM6010_REQ05_R20_IRQ_GROUP 0x05, 0x20 +#define TM6010_REQ05_R21_IRQ_SOURCE0 0x05, 0x21 +#define TM6010_REQ05_R22_IRQ_SOURCE1 0x05, 0x22 +#define TM6010_REQ05_R23_IRQ_SOURCE2 0x05, 0x23 +#define TM6010_REQ05_R24_IRQ_SOURCE3 0x05, 0x24 +#define TM6010_REQ05_R25_IRQ_SOURCE4 0x05, 0x25 +#define TM6010_REQ05_R26_IRQ_SOURCE5 0x05, 0x26 +#define TM6010_REQ05_R27_IRQ_SOURCE6 0x05, 0x27 +#define TM6010_REQ05_R28_IRQ_SOURCE7 0x05, 0x28 +#define TM6010_REQ05_R29_SEQ_ERR0 0x05, 0x29 +#define TM6010_REQ05_R2A_SEQ_ERR1 0x05, 0x2a +#define TM6010_REQ05_R2B_SEQ_ABORT0 0x05, 0x2b +#define TM6010_REQ05_R2C_SEQ_ABORT1 0x05, 0x2c +#define TM6010_REQ05_R2D_TX_ZERO0 0x05, 0x2d +#define TM6010_REQ05_R2E_TX_ZERO1 0x05, 0x2e +#define TM6010_REQ05_R2F_IDLE_CNT 0x05, 0x2f +#define TM6010_REQ05_R30_FNO_P1 0x05, 0x30 +#define TM6010_REQ05_R31_FNO_P2 0x05, 0x31 +#define TM6010_REQ05_R32_FNO_P3 0x05, 0x32 +#define TM6010_REQ05_R33_FNO_P4 0x05, 0x33 +#define TM6010_REQ05_R34_FNO_P5 0x05, 0x34 +#define TM6010_REQ05_R35_FNO_P6 0x05, 0x35 +#define TM6010_REQ05_R36_FNO_P7 0x05, 0x36 +#define TM6010_REQ05_R37_FNO_P8 0x05, 0x37 +#define TM6010_REQ05_R38_FNO_P9 0x05, 0x38 +#define TM6010_REQ05_R30_FNO_P10 0x05, 0x39 +#define TM6010_REQ05_R30_FNO_P11 0x05, 0x3a +#define TM6010_REQ05_R30_FNO_P12 0x05, 0x3b +#define TM6010_REQ05_R30_FNO_P13 0x05, 0x3c +#define TM6010_REQ05_R30_FNO_P14 0x05, 0x3d +#define TM6010_REQ05_R30_FNO_P15 0x05, 0x3e +#define TM6010_REQ05_R40_IN_MAXPS_LOW1 0x05, 0x40 +#define TM6010_REQ05_R41_IN_MAXPS_HIGH1 0x05, 0x41 +#define TM6010_REQ05_R42_IN_MAXPS_LOW2 0x05, 0x42 +#define TM6010_REQ05_R43_IN_MAXPS_HIGH2 0x05, 0x43 +#define TM6010_REQ05_R44_IN_MAXPS_LOW3 0x05, 0x44 +#define TM6010_REQ05_R45_IN_MAXPS_HIGH3 0x05, 0x45 +#define TM6010_REQ05_R46_IN_MAXPS_LOW4 0x05, 0x46 +#define TM6010_REQ05_R47_IN_MAXPS_HIGH4 0x05, 0x47 +#define TM6010_REQ05_R48_IN_MAXPS_LOW5 0x05, 0x48 +#define TM6010_REQ05_R49_IN_MAXPS_HIGH5 0x05, 0x49 +#define TM6010_REQ05_R4A_IN_MAXPS_LOW6 0x05, 0x4a +#define TM6010_REQ05_R4B_IN_MAXPS_HIGH6 0x05, 0x4b +#define TM6010_REQ05_R4C_IN_MAXPS_LOW7 0x05, 0x4c +#define TM6010_REQ05_R4D_IN_MAXPS_HIGH7 0x05, 0x4d +#define TM6010_REQ05_R4E_IN_MAXPS_LOW8 0x05, 0x4e +#define TM6010_REQ05_R4F_IN_MAXPS_HIGH8 0x05, 0x4f +#define TM6010_REQ05_R50_IN_MAXPS_LOW9 0x05, 0x50 +#define TM6010_REQ05_R51_IN_MAXPS_HIGH9 0x05, 0x51 +#define TM6010_REQ05_R40_IN_MAXPS_LOW10 0x05, 0x52 +#define TM6010_REQ05_R41_IN_MAXPS_HIGH10 0x05, 0x53 +#define TM6010_REQ05_R40_IN_MAXPS_LOW11 0x05, 0x54 +#define TM6010_REQ05_R41_IN_MAXPS_HIGH11 0x05, 0x55 +#define TM6010_REQ05_R40_IN_MAXPS_LOW12 0x05, 0x56 +#define TM6010_REQ05_R41_IN_MAXPS_HIGH12 0x05, 0x57 +#define TM6010_REQ05_R40_IN_MAXPS_LOW13 0x05, 0x58 +#define TM6010_REQ05_R41_IN_MAXPS_HIGH13 0x05, 0x59 +#define TM6010_REQ05_R40_IN_MAXPS_LOW14 0x05, 0x5a +#define TM6010_REQ05_R41_IN_MAXPS_HIGH14 0x05, 0x5b +#define TM6010_REQ05_R40_IN_MAXPS_LOW15 0x05, 0x5c +#define TM6010_REQ05_R41_IN_MAXPS_HIGH15 0x05, 0x5d +#define TM6010_REQ05_R60_OUT_MAXPS_LOW1 0x05, 0x60 +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH1 0x05, 0x61 +#define TM6010_REQ05_R62_OUT_MAXPS_LOW2 0x05, 0x62 +#define TM6010_REQ05_R63_OUT_MAXPS_HIGH2 0x05, 0x63 +#define TM6010_REQ05_R64_OUT_MAXPS_LOW3 0x05, 0x64 +#define TM6010_REQ05_R65_OUT_MAXPS_HIGH3 0x05, 0x65 +#define TM6010_REQ05_R66_OUT_MAXPS_LOW4 0x05, 0x66 +#define TM6010_REQ05_R67_OUT_MAXPS_HIGH4 0x05, 0x67 +#define TM6010_REQ05_R68_OUT_MAXPS_LOW5 0x05, 0x68 +#define TM6010_REQ05_R69_OUT_MAXPS_HIGH5 0x05, 0x69 +#define TM6010_REQ05_R6A_OUT_MAXPS_LOW6 0x05, 0x6a +#define TM6010_REQ05_R6B_OUT_MAXPS_HIGH6 0x05, 0x6b +#define TM6010_REQ05_R6C_OUT_MAXPS_LOW7 0x05, 0x6c +#define TM6010_REQ05_R6D_OUT_MAXPS_HIGH7 0x05, 0x6d +#define TM6010_REQ05_R6E_OUT_MAXPS_LOW8 0x05, 0x6e +#define TM6010_REQ05_R6F_OUT_MAXPS_HIGH8 0x05, 0x6f +#define TM6010_REQ05_R70_OUT_MAXPS_LOW9 0x05, 0x70 +#define TM6010_REQ05_R71_OUT_MAXPS_HIGH9 0x05, 0x71 +#define TM6010_REQ05_R60_OUT_MAXPS_LOW10 0x05, 0x72 +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH10 0x05, 0x73 +#define TM6010_REQ05_R60_OUT_MAXPS_LOW11 0x05, 0x74 +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH11 0x05, 0x75 +#define TM6010_REQ05_R60_OUT_MAXPS_LOW12 0x05, 0x76 +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH12 0x05, 0x77 +#define TM6010_REQ05_R60_OUT_MAXPS_LOW13 0x05, 0x78 +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH13 0x05, 0x79 +#define TM6010_REQ05_R60_OUT_MAXPS_LOW14 0x05, 0x7a +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH14 0x05, 0x7b +#define TM6010_REQ05_R60_OUT_MAXPS_LOW15 0x05, 0x7c +#define TM6010_REQ05_R61_OUT_MAXPS_HIGH15 0x05, 0x7d +#define TM6010_REQ05_R80_FIFO0 0x05, 0x80 +#define TM6010_REQ05_R81_FIFO1 0x05, 0x81 +#define TM6010_REQ05_R82_FIFO2 0x05, 0x82 +#define TM6010_REQ05_R83_FIFO3 0x05, 0x83 +#define TM6010_REQ05_R84_FIFO4 0x05, 0x84 +#define TM6010_REQ05_R85_FIFO5 0x05, 0x85 +#define TM6010_REQ05_R86_FIFO6 0x05, 0x86 +#define TM6010_REQ05_R87_FIFO7 0x05, 0x87 +#define TM6010_REQ05_R88_FIFO8 0x05, 0x88 +#define TM6010_REQ05_R89_FIFO9 0x05, 0x89 +#define TM6010_REQ05_R81_FIFO10 0x05, 0x8a +#define TM6010_REQ05_R81_FIFO11 0x05, 0x8b +#define TM6010_REQ05_R81_FIFO12 0x05, 0x8c +#define TM6010_REQ05_R81_FIFO13 0x05, 0x8d +#define TM6010_REQ05_R81_FIFO14 0x05, 0x8e +#define TM6010_REQ05_R81_FIFO15 0x05, 0x8f +#define TM6010_REQ05_R90_CFG_FIFO0 0x05, 0x90 +#define TM6010_REQ05_R91_CFG_FIFO1 0x05, 0x91 +#define TM6010_REQ05_R92_CFG_FIFO2 0x05, 0x92 +#define TM6010_REQ05_R93_CFG_FIFO3 0x05, 0x93 +#define TM6010_REQ05_R94_CFG_FIFO4 0x05, 0x94 +#define TM6010_REQ05_R95_CFG_FIFO5 0x05, 0x95 +#define TM6010_REQ05_R96_CFG_FIFO6 0x05, 0x96 +#define TM6010_REQ05_R97_CFG_FIFO7 0x05, 0x97 +#define TM6010_REQ05_R98_CFG_FIFO8 0x05, 0x98 +#define TM6010_REQ05_R99_CFG_FIFO9 0x05, 0x99 +#define TM6010_REQ05_R91_CFG_FIFO10 0x05, 0x9a +#define TM6010_REQ05_R91_CFG_FIFO11 0x05, 0x9b +#define TM6010_REQ05_R91_CFG_FIFO12 0x05, 0x9c +#define TM6010_REQ05_R91_CFG_FIFO13 0x05, 0x9d +#define TM6010_REQ05_R91_CFG_FIFO14 0x05, 0x9e +#define TM6010_REQ05_R91_CFG_FIFO15 0x05, 0x9f +#define TM6010_REQ05_RA0_CTL_FIFO0 0x05, 0xa0 +#define TM6010_REQ05_RA1_CTL_FIFO1 0x05, 0xa1 +#define TM6010_REQ05_RA2_CTL_FIFO2 0x05, 0xa2 +#define TM6010_REQ05_RA3_CTL_FIFO3 0x05, 0xa3 +#define TM6010_REQ05_RA4_CTL_FIFO4 0x05, 0xa4 +#define TM6010_REQ05_RA5_CTL_FIFO5 0x05, 0xa5 +#define TM6010_REQ05_RA6_CTL_FIFO6 0x05, 0xa6 +#define TM6010_REQ05_RA7_CTL_FIFO7 0x05, 0xa7 +#define TM6010_REQ05_RA8_CTL_FIFO8 0x05, 0xa8 +#define TM6010_REQ05_RA9_CTL_FIFO9 0x05, 0xa9 +#define TM6010_REQ05_RA1_CTL_FIFO10 0x05, 0xaa +#define TM6010_REQ05_RA1_CTL_FIFO11 0x05, 0xab +#define TM6010_REQ05_RA1_CTL_FIFO12 0x05, 0xac +#define TM6010_REQ05_RA1_CTL_FIFO13 0x05, 0xad +#define TM6010_REQ05_RA1_CTL_FIFO14 0x05, 0xae +#define TM6010_REQ05_RA1_CTL_FIFO15 0x05, 0xaf +#define TM6010_REQ05_RB0_BC_LOW_FIFO0 0x05, 0xb0 +#define TM6010_REQ05_RB1_BC_LOW_FIFO1 0x05, 0xb1 +#define TM6010_REQ05_RB2_BC_LOW_FIFO2 0x05, 0xb2 +#define TM6010_REQ05_RB3_BC_LOW_FIFO3 0x05, 0xb3 +#define TM6010_REQ05_RB4_BC_LOW_FIFO4 0x05, 0xb4 +#define TM6010_REQ05_RB5_BC_LOW_FIFO5 0x05, 0xb5 +#define TM6010_REQ05_RB6_BC_LOW_FIFO6 0x05, 0xb6 +#define TM6010_REQ05_RB7_BC_LOW_FIFO7 0x05, 0xb7 +#define TM6010_REQ05_RB8_BC_LOW_FIFO8 0x05, 0xb8 +#define TM6010_REQ05_RB9_BC_LOW_FIFO9 0x05, 0xb9 +#define TM6010_REQ05_RB1_BC_LOW_FIFO10 0x05, 0xba +#define TM6010_REQ05_RB1_BC_LOW_FIFO11 0x05, 0xbb +#define TM6010_REQ05_RB1_BC_LOW_FIFO12 0x05, 0xbc +#define TM6010_REQ05_RB1_BC_LOW_FIFO13 0x05, 0xbd +#define TM6010_REQ05_RB1_BC_LOW_FIFO14 0x05, 0xbe +#define TM6010_REQ05_RB1_BC_LOW_FIFO15 0x05, 0xbf +#define TM6010_REQ05_RC0_DATA_FIFO0 0x05, 0xc0 +#define TM6010_REQ05_RC4_DATA_FIFO1 0x05, 0xc4 +#define TM6010_REQ05_RC8_DATA_FIFO2 0x05, 0xc8 +#define TM6010_REQ05_RCC_DATA_FIFO3 0x05, 0xcc +#define TM6010_REQ05_RD0_DATA_FIFO4 0x05, 0xd0 +#define TM6010_REQ05_RD4_DATA_FIFO5 0x05, 0xd4 +#define TM6010_REQ05_RD8_DATA_FIFO6 0x05, 0xd8 +#define TM6010_REQ05_RDC_DATA_FIFO7 0x05, 0xdc +#define TM6010_REQ05_RE0_DATA_FIFO8 0x05, 0xe0 +#define TM6010_REQ05_RE4_DATA_FIFO9 0x05, 0xe4 +#define TM6010_REQ05_RC4_DATA_FIFO10 0x05, 0xe8 +#define TM6010_REQ05_RC4_DATA_FIFO11 0x05, 0xec +#define TM6010_REQ05_RC4_DATA_FIFO12 0x05, 0xf0 +#define TM6010_REQ05_RC4_DATA_FIFO13 0x05, 0xf4 +#define TM6010_REQ05_RC4_DATA_FIFO14 0x05, 0xf8 +#define TM6010_REQ05_RC4_DATA_FIFO15 0x05, 0xfc + +/* Define TM6010 Audio decoder registers */ +/* This core available only in TM6010 */ +#define TM6010_REQ08_R00_A_VERSION 0x08, 0x00 +#define TM6010_REQ08_R01_A_INIT 0x08, 0x01 +#define TM6010_REQ08_R02_A_FIX_GAIN_CTRL 0x08, 0x02 +#define TM6010_REQ08_R03_A_AUTO_GAIN_CTRL 0x08, 0x03 +#define TM6010_REQ08_R04_A_SIF_AMP_CTRL 0x08, 0x04 +#define TM6010_REQ08_R05_A_STANDARD_MOD 0x08, 0x05 +#define TM6010_REQ08_R06_A_SOUND_MOD 0x08, 0x06 +#define TM6010_REQ08_R07_A_LEFT_VOL 0x08, 0x07 +#define TM6010_REQ08_R08_A_RIGHT_VOL 0x08, 0x08 +#define TM6010_REQ08_R09_A_MAIN_VOL 0x08, 0x09 +#define TM6010_REQ08_R0A_A_I2S_MOD 0x08, 0x0a +#define TM6010_REQ08_R0B_A_ASD_THRES1 0x08, 0x0b +#define TM6010_REQ08_R0C_A_ASD_THRES2 0x08, 0x0c +#define TM6010_REQ08_R0D_A_AMD_THRES 0x08, 0x0d +#define TM6010_REQ08_R0E_A_MONO_THRES1 0x08, 0x0e +#define TM6010_REQ08_R0F_A_MONO_THRES2 0x08, 0x0f +#define TM6010_REQ08_R10_A_MUTE_THRES1 0x08, 0x10 +#define TM6010_REQ08_R11_A_MUTE_THRES2 0x08, 0x11 +#define TM6010_REQ08_R12_A_AGC_U 0x08, 0x12 +#define TM6010_REQ08_R13_A_AGC_ERR_T 0x08, 0x13 +#define TM6010_REQ08_R14_A_AGC_GAIN_INIT 0x08, 0x14 +#define TM6010_REQ08_R15_A_AGC_STEP_THR 0x08, 0x15 +#define TM6010_REQ08_R16_A_AGC_GAIN_MAX 0x08, 0x16 +#define TM6010_REQ08_R17_A_AGC_GAIN_MIN 0x08, 0x17 +#define TM6010_REQ08_R18_A_TR_CTRL 0x08, 0x18 +#define TM6010_REQ08_R19_A_FH_2FH_GAIN 0x08, 0x19 +#define TM6010_REQ08_R1A_A_NICAM_SER_MAX 0x08, 0x1a +#define TM6010_REQ08_R1B_A_NICAM_SER_MIN 0x08, 0x1b +#define TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT 0x08, 0x1e +#define TM6010_REQ08_R1F_A_TEST_INTF_SEL 0x08, 0x1f +#define TM6010_REQ08_R20_A_TEST_PIN_SEL 0x08, 0x20 +#define TM6010_REQ08_R21_A_AGC_ERR 0x08, 0x21 +#define TM6010_REQ08_R22_A_AGC_GAIN 0x08, 0x22 +#define TM6010_REQ08_R23_A_NICAM_INFO 0x08, 0x23 +#define TM6010_REQ08_R24_A_SER 0x08, 0x24 +#define TM6010_REQ08_R25_A_C1_AMP 0x08, 0x25 +#define TM6010_REQ08_R26_A_C2_AMP 0x08, 0x26 +#define TM6010_REQ08_R27_A_NOISE_AMP 0x08, 0x27 +#define TM6010_REQ08_R28_A_AUDIO_MODE_RES 0x08, 0x28 + +/* Define TM6010 Video ADC registers */ +#define TM6010_REQ08_RE0_ADC_REF 0x08, 0xe0 +#define TM6010_REQ08_RE1_DAC_CLMP 0x08, 0xe1 +#define TM6010_REQ08_RE2_POWER_DOWN_CTRL1 0x08, 0xe2 +#define TM6010_REQ08_RE3_ADC_IN1_SEL 0x08, 0xe3 +#define TM6010_REQ08_RE4_ADC_IN2_SEL 0x08, 0xe4 +#define TM6010_REQ08_RE5_GAIN_PARAM 0x08, 0xe5 +#define TM6010_REQ08_RE6_POWER_DOWN_CTRL2 0x08, 0xe6 +#define TM6010_REQ08_RE7_REG_GAIN_Y 0x08, 0xe7 +#define TM6010_REQ08_RE8_REG_GAIN_C 0x08, 0xe8 +#define TM6010_REQ08_RE9_BIAS_CTRL 0x08, 0xe9 +#define TM6010_REQ08_REA_BUFF_DRV_CTRL 0x08, 0xea +#define TM6010_REQ08_REB_SIF_GAIN_CTRL 0x08, 0xeb +#define TM6010_REQ08_REC_REVERSE_YC_CTRL 0x08, 0xec +#define TM6010_REQ08_RED_GAIN_SEL 0x08, 0xed + +/* Define TM6010 Audio ADC registers */ +#define TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG 0x08, 0xf0 +#define TM6010_REQ08_RF1_AADC_POWER_DOWN 0x08, 0xf1 +#define TM6010_REQ08_RF2_LEFT_CHANNEL_VOL 0x08, 0xf2 +#define TM6010_REQ08_RF3_RIGHT_CHANNEL_VOL 0x08, 0xf3 diff --git a/drivers/staging/media/deprecated/tm6000/tm6000-stds.c b/drivers/staging/media/deprecated/tm6000/tm6000-stds.c new file mode 100644 index 000000000000..858cb4f3a9ca --- /dev/null +++ b/drivers/staging/media/deprecated/tm6000/tm6000-stds.c @@ -0,0 +1,623 @@ +// SPDX-License-Identifier: GPL-2.0 +// tm6000-stds.c - driver for TM5600/TM6000/TM6010 USB video capture devices +// +// Copyright (c) 2007 Mauro Carvalho Chehab <mchehab@kernel.org> + +#include <linux/module.h> +#include <linux/kernel.h> +#include "tm6000.h" +#include "tm6000-regs.h" + +static unsigned int tm6010_a_mode; +module_param(tm6010_a_mode, int, 0644); +MODULE_PARM_DESC(tm6010_a_mode, "set tm6010 sif audio mode"); + +struct tm6000_reg_settings { + unsigned char req; + unsigned char reg; + unsigned char value; +}; + + +struct tm6000_std_settings { + v4l2_std_id id; + struct tm6000_reg_settings *common; +}; + +static struct tm6000_reg_settings composite_pal_m[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x04 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x83 }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x0a }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe0 }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x20 }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, + { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_reg_settings composite_pal_nc[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x36 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x91 }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x1f }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x0c }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, + { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_reg_settings composite_pal[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x32 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x25 }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0xd5 }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x63 }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x50 }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, + { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_reg_settings composite_secam[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x38 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x24 }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x92 }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xe8 }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xed }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2c }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x2c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x18 }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0xff }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_reg_settings composite_ntsc[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x00 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0f }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, + { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdd }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_std_settings composite_stds[] = { + { .id = V4L2_STD_PAL_M, .common = composite_pal_m, }, + { .id = V4L2_STD_PAL_Nc, .common = composite_pal_nc, }, + { .id = V4L2_STD_PAL, .common = composite_pal, }, + { .id = V4L2_STD_SECAM, .common = composite_secam, }, + { .id = V4L2_STD_NTSC, .common = composite_ntsc, }, +}; + +static struct tm6000_reg_settings svideo_pal_m[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x05 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x83 }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x0a }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe0 }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, + { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_reg_settings svideo_pal_nc[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x37 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x91 }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x1f }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x0c }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, + { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_reg_settings svideo_pal[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x33 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x30 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x25 }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0xd5 }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x63 }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0x50 }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2a }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x0c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x52 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, + { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_reg_settings svideo_secam[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x39 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x03 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31 }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x24 }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x92 }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xe8 }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xed }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x8c }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x2a }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0xc1 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x2c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x18 }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0xff }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_reg_settings svideo_ntsc[] = { + { TM6010_REQ07_R3F_RESET, 0x01 }, + { TM6010_REQ07_R00_VIDEO_CONTROL0, 0x01 }, + { TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0f }, + { TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f }, + { TM6010_REQ07_R03_YC_SEP_CONTROL, 0x03 }, + { TM6010_REQ07_R07_OUTPUT_CONTROL, 0x30 }, + { TM6010_REQ07_R17_HLOOP_MAXSTATE, 0x8b }, + { TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e }, + { TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b }, + { TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2 }, + { TM6010_REQ07_R1B_CHROMA_DTO_INCREMENT0, 0xe9 }, + { TM6010_REQ07_R1C_HSYNC_DTO_INCREMENT3, 0x1c }, + { TM6010_REQ07_R1D_HSYNC_DTO_INCREMENT2, 0xcc }, + { TM6010_REQ07_R1E_HSYNC_DTO_INCREMENT1, 0xcc }, + { TM6010_REQ07_R1F_HSYNC_DTO_INCREMENT0, 0xcd }, + { TM6010_REQ07_R2E_ACTIVE_VIDEO_HSTART, 0x88 }, + { TM6010_REQ07_R30_ACTIVE_VIDEO_VSTART, 0x22 }, + { TM6010_REQ07_R31_ACTIVE_VIDEO_VHIGHT, 0x61 }, + { TM6010_REQ07_R33_VSYNC_HLOCK_MAX, 0x1c }, + { TM6010_REQ07_R35_VSYNC_AGC_MAX, 0x1c }, + { TM6010_REQ07_R82_COMB_FILTER_CONFIG, 0x42 }, + { TM6010_REQ07_R83_CHROMA_LOCK_CONFIG, 0x6f }, + { TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdd }, + { TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07 }, + { TM6010_REQ07_R3F_RESET, 0x00 }, + { 0, 0, 0 } +}; + +static struct tm6000_std_settings svideo_stds[] = { + { .id = V4L2_STD_PAL_M, .common = svideo_pal_m, }, + { .id = V4L2_STD_PAL_Nc, .common = svideo_pal_nc, }, + { .id = V4L2_STD_PAL, .common = svideo_pal, }, + { .id = V4L2_STD_SECAM, .common = svideo_secam, }, + { .id = V4L2_STD_NTSC, .common = svideo_ntsc, }, +}; + +static int tm6000_set_audio_std(struct tm6000_core *dev) +{ + uint8_t areg_02 = 0x04; /* GC1 Fixed gain 0dB */ + uint8_t areg_05 = 0x01; /* Auto 4.5 = M Japan, Auto 6.5 = DK */ + uint8_t areg_06 = 0x02; /* Auto de-emphasis, manual channel mode */ + + if (dev->radio) { + tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R02_A_FIX_GAIN_CTRL, 0x04); + tm6000_set_reg(dev, TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R04_A_SIF_AMP_CTRL, 0x80); + tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD, 0x0c); + /* set mono or stereo */ + if (dev->amode == V4L2_TUNER_MODE_MONO) + tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x00); + else if (dev->amode == V4L2_TUNER_MODE_STEREO) + tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x02); + tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL, 0x18); + tm6000_set_reg(dev, TM6010_REQ08_R0C_A_ASD_THRES2, 0x0a); + tm6000_set_reg(dev, TM6010_REQ08_R0D_A_AMD_THRES, 0x40); + tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfe); + tm6000_set_reg(dev, TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT, 0x13); + tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80); + tm6000_set_reg(dev, TM6010_REQ07_RFE_POWER_DOWN, 0xff); + return 0; + } + + /* + * STD/MN shouldn't be affected by tm6010_a_mode, as there's just one + * audio standard for each V4L2_STD type. + */ + if ((dev->norm & V4L2_STD_NTSC) == V4L2_STD_NTSC_M_KR) { + areg_05 |= 0x04; + } else if ((dev->norm & V4L2_STD_NTSC) == V4L2_STD_NTSC_M_JP) { + areg_05 |= 0x43; + } else if (dev->norm & V4L2_STD_MN) { + areg_05 |= 0x22; + } else switch (tm6010_a_mode) { + /* auto */ + case 0: + if ((dev->norm & V4L2_STD_SECAM) == V4L2_STD_SECAM_L) + areg_05 |= 0x00; + else /* Other PAL/SECAM standards */ + areg_05 |= 0x10; + break; + /* A2 */ + case 1: + if (dev->norm & V4L2_STD_DK) + areg_05 = 0x09; + else + areg_05 = 0x05; + break; + /* NICAM */ + case 2: + if (dev->norm & V4L2_STD_DK) { + areg_05 = 0x06; + } else if (dev->norm & V4L2_STD_PAL_I) { + areg_05 = 0x08; + } else if (dev->norm & V4L2_STD_SECAM_L) { + areg_05 = 0x0a; + areg_02 = 0x02; + } else { + areg_05 = 0x07; + } + break; + /* other */ + case 3: + if (dev->norm & V4L2_STD_DK) { + areg_05 = 0x0b; + } else { + areg_05 = 0x02; + } + break; + } + + tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R02_A_FIX_GAIN_CTRL, areg_02); + tm6000_set_reg(dev, TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R04_A_SIF_AMP_CTRL, 0xa0); + tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD, areg_05); + tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, areg_06); + tm6000_set_reg(dev, TM6010_REQ08_R07_A_LEFT_VOL, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R08_A_RIGHT_VOL, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL, 0x08); + tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, 0x91); + tm6000_set_reg(dev, TM6010_REQ08_R0B_A_ASD_THRES1, 0x20); + tm6000_set_reg(dev, TM6010_REQ08_R0C_A_ASD_THRES2, 0x12); + tm6000_set_reg(dev, TM6010_REQ08_R0D_A_AMD_THRES, 0x20); + tm6000_set_reg(dev, TM6010_REQ08_R0E_A_MONO_THRES1, 0xf0); + tm6000_set_reg(dev, TM6010_REQ08_R0F_A_MONO_THRES2, 0x80); + tm6000_set_reg(dev, TM6010_REQ08_R10_A_MUTE_THRES1, 0xc0); + tm6000_set_reg(dev, TM6010_REQ08_R11_A_MUTE_THRES2, 0x80); + tm6000_set_reg(dev, TM6010_REQ08_R12_A_AGC_U, 0x12); + tm6000_set_reg(dev, TM6010_REQ08_R13_A_AGC_ERR_T, 0xfe); + tm6000_set_reg(dev, TM6010_REQ08_R14_A_AGC_GAIN_INIT, 0x20); + tm6000_set_reg(dev, TM6010_REQ08_R15_A_AGC_STEP_THR, 0x14); + tm6000_set_reg(dev, TM6010_REQ08_R16_A_AGC_GAIN_MAX, 0xfe); + tm6000_set_reg(dev, TM6010_REQ08_R17_A_AGC_GAIN_MIN, 0x01); + tm6000_set_reg(dev, TM6010_REQ08_R18_A_TR_CTRL, 0xa0); + tm6000_set_reg(dev, TM6010_REQ08_R19_A_FH_2FH_GAIN, 0x32); + tm6000_set_reg(dev, TM6010_REQ08_R1A_A_NICAM_SER_MAX, 0x64); + tm6000_set_reg(dev, TM6010_REQ08_R1B_A_NICAM_SER_MIN, 0x20); + tm6000_set_reg(dev, REQ_08_SET_GET_AVREG_BIT, 0x1c, 0x00); + tm6000_set_reg(dev, REQ_08_SET_GET_AVREG_BIT, 0x1d, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT, 0x13); + tm6000_set_reg(dev, TM6010_REQ08_R1F_A_TEST_INTF_SEL, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R20_A_TEST_PIN_SEL, 0x00); + tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80); + + return 0; +} + +void tm6000_get_std_res(struct tm6000_core *dev) +{ + /* Currently, those are the only supported resoltions */ + if (dev->norm & V4L2_STD_525_60) + dev->height = 480; + else + dev->height = 576; + + dev->width = 720; +} + +static int tm6000_load_std(struct tm6000_core *dev, struct tm6000_reg_settings *set) +{ + int i, rc; + + /* Load board's initialization table */ + for (i = 0; set[i].req; i++) { + rc = tm6000_set_reg(dev, set[i].req, set[i].reg, set[i].value); + if (rc < 0) { + printk(KERN_ERR "Error %i while setting req %d, reg %d to value %d\n", + rc, set[i].req, set[i].reg, set[i].value); + return rc; + } + } + + return 0; +} + +int tm6000_set_standard(struct tm6000_core *dev) +{ + struct tm6000_input *input; + int i, rc = 0; + u8 reg_07_fe = 0x8a; + u8 reg_08_f1 = 0xfc; + u8 reg_08_e2 = 0xf0; + u8 reg_08_e6 = 0x0f; + + tm6000_get_std_res(dev); + + if (!dev->radio) + input = &dev->vinput[dev->input]; + else + input = &dev->rinput; + + if (dev->dev_type == TM6010) { + switch (input->vmux) { + case TM6000_VMUX_VIDEO_A: + tm6000_set_reg(dev, TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4); + tm6000_set_reg(dev, TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1); + tm6000_set_reg(dev, TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0); + tm6000_set_reg(dev, TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2); + tm6000_set_reg(dev, TM6010_REQ08_RED_GAIN_SEL, 0xe8); + reg_07_fe |= 0x01; + break; + case TM6000_VMUX_VIDEO_B: + tm6000_set_reg(dev, TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf8); + tm6000_set_reg(dev, TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf1); + tm6000_set_reg(dev, TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xe0); + tm6000_set_reg(dev, TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2); + tm6000_set_reg(dev, TM6010_REQ08_RED_GAIN_SEL, 0xe8); + reg_07_fe |= 0x01; + break; + case TM6000_VMUX_VIDEO_AB: + tm6000_set_reg(dev, TM6010_REQ08_RE3_ADC_IN1_SEL, 0xfc); + tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf8); + reg_08_e6 = 0x00; + tm6000_set_reg(dev, TM6010_REQ08_REA_BUFF_DRV_CTRL, 0xf2); + tm6000_set_reg(dev, TM6010_REQ08_REB_SIF_GAIN_CTRL, 0xf0); + tm6000_set_reg(dev, TM6010_REQ08_REC_REVERSE_YC_CTRL, 0xc2); + tm6000_set_reg(dev, TM6010_REQ08_RED_GAIN_SEL, 0xe0); + break; + default: + break; + } + switch (input->amux) { + case TM6000_AMUX_ADC1: + tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, + 0x00, 0x0f); + /* Mux overflow workaround */ + tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL, + 0x10, 0xf0); + break; + case TM6000_AMUX_ADC2: + tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, + 0x08, 0x0f); + /* Mux overflow workaround */ + tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL, + 0x10, 0xf0); + break; + case TM6000_AMUX_SIF1: + reg_08_e2 |= 0x02; + reg_08_e6 = 0x08; + reg_07_fe |= 0x40; + reg_08_f1 |= 0x02; + tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf3); + tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, + 0x02, 0x0f); + /* Mux overflow workaround */ + tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL, + 0x30, 0xf0); + break; + case TM6000_AMUX_SIF2: + reg_08_e2 |= 0x02; + reg_08_e6 = 0x08; + reg_07_fe |= 0x40; + reg_08_f1 |= 0x02; + tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf7); + tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG, + 0x02, 0x0f); + /* Mux overflow workaround */ + tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL, + 0x30, 0xf0); + break; + default: + break; + } + tm6000_set_reg(dev, TM6010_REQ08_RE2_POWER_DOWN_CTRL1, reg_08_e2); + tm6000_set_reg(dev, TM6010_REQ08_RE6_POWER_DOWN_CTRL2, reg_08_e6); + tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, reg_08_f1); + tm6000_set_reg(dev, TM6010_REQ07_RFE_POWER_DOWN, reg_07_fe); + } else { + switch (input->vmux) { + case TM6000_VMUX_VIDEO_A: + tm6000_set_reg(dev, TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x10); + tm6000_set_reg(dev, TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x00); + tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x0f); + tm6000_set_reg(dev, + REQ_03_SET_GET_MCU_PIN, input->v_gpio, 0); + break; + case TM6000_VMUX_VIDEO_B: + tm6000_set_reg(dev, TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x00); + tm6000_set_reg(dev, TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x00); + tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x0f); + tm6000_set_reg(dev, + REQ_03_SET_GET_MCU_PIN, input->v_gpio, 0); + break; + case TM6000_VMUX_VIDEO_AB: + tm6000_set_reg(dev, TM6000_REQ07_RE3_VADC_INP_LPF_SEL1, 0x10); + tm6000_set_reg(dev, TM6000_REQ07_RE5_VADC_INP_LPF_SEL2, 0x10); + tm6000_set_reg(dev, TM6000_REQ07_RE8_VADC_PWDOWN_CTL, 0x00); + tm6000_set_reg(dev, + REQ_03_SET_GET_MCU_PIN, input->v_gpio, 1); + break; + default: + break; + } + switch (input->amux) { + case TM6000_AMUX_ADC1: + tm6000_set_reg_mask(dev, + TM6000_REQ07_REB_VADC_AADC_MODE, 0x00, 0x0f); + break; + case TM6000_AMUX_ADC2: + tm6000_set_reg_mask(dev, + TM6000_REQ07_REB_VADC_AADC_MODE, 0x04, 0x0f); + break; + default: + break; + } + } + if (input->type == TM6000_INPUT_SVIDEO) { + for (i = 0; i < ARRAY_SIZE(svideo_stds); i++) { + if (dev->norm & svideo_stds[i].id) { + rc = tm6000_load_std(dev, svideo_stds[i].common); + goto ret; + } + } + return -EINVAL; + } else { + for (i = 0; i < ARRAY_SIZE(composite_stds); i++) { + if (dev->norm & composite_stds[i].id) { + rc = tm6000_load_std(dev, composite_stds[i].common); + goto ret; + } + } + return -EINVAL; + } + +ret: + if (rc < 0) + return rc; + + if ((dev->dev_type == TM6010) && + ((input->amux == TM6000_AMUX_SIF1) || + (input->amux == TM6000_AMUX_SIF2))) + tm6000_set_audio_std(dev); + + msleep(40); + + return 0; +} diff --git a/drivers/staging/media/deprecated/tm6000/tm6000-usb-isoc.h b/drivers/staging/media/deprecated/tm6000/tm6000-usb-isoc.h new file mode 100644 index 000000000000..e3c6933f854d --- /dev/null +++ b/drivers/staging/media/deprecated/tm6000/tm6000-usb-isoc.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * tm6000-buf.c - driver for TM5600/TM6000/TM6010 USB video capture devices + * + * Copyright (c) 2006-2007 Mauro Carvalho Chehab <mchehab@kernel.org> + */ + +#include <linux/videodev2.h> + +#define TM6000_URB_MSG_LEN 180 + +struct usb_isoc_ctl { + /* max packet size of isoc transaction */ + int max_pkt_size; + + /* number of allocated urbs */ + int num_bufs; + + /* urb for isoc transfers */ + struct urb **urb; + + /* transfer buffers for isoc transfer */ + char **transfer_buffer; + + /* Last buffer command and region */ + u8 cmd; + int pos, size, pktsize; + + /* Last field: ODD or EVEN? */ + int vfield, field; + + /* Stores incomplete commands */ + u32 tmp_buf; + int tmp_buf_len; + + /* Stores already requested buffers */ + struct tm6000_buffer *buf; +}; diff --git a/drivers/staging/media/deprecated/tm6000/tm6000-video.c b/drivers/staging/media/deprecated/tm6000/tm6000-video.c new file mode 100644 index 000000000000..e06ed21edbdd --- /dev/null +++ b/drivers/staging/media/deprecated/tm6000/tm6000-video.c @@ -0,0 +1,1703 @@ +// SPDX-License-Identifier: GPL-2.0 +// tm6000-video.c - driver for TM5600/TM6000/TM6010 USB video capture devices +// +// Copyright (c) 2006-2007 Mauro Carvalho Chehab <mchehab@kernel.org> +// +// Copyright (c) 2007 Michel Ludwig <michel.ludwig@gmail.com> +// - Fixed module load/unload + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/random.h> +#include <linux/usb.h> +#include <linux/videodev2.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-event.h> +#include <media/tuner.h> +#include <linux/interrupt.h> +#include <linux/kthread.h> +#include <linux/highmem.h> +#include <linux/freezer.h> + +#include "tm6000-regs.h" +#include "tm6000.h" + +#define BUFFER_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ + +/* Limits minimum and default number of buffers */ +#define TM6000_MIN_BUF 4 +#define TM6000_DEF_BUF 8 + +#define TM6000_NUM_URB_BUF 8 + +#define TM6000_MAX_ISO_PACKETS 46 /* Max number of ISO packets */ + +/* Declare static vars that will be used as parameters */ +static unsigned int vid_limit = 16; /* Video memory limit, in Mb */ +static int video_nr = -1; /* /dev/videoN, -1 for autodetect */ +static int radio_nr = -1; /* /dev/radioN, -1 for autodetect */ +static bool keep_urb; /* keep urb buffers allocated */ + +/* Debug level */ +int tm6000_debug; +EXPORT_SYMBOL_GPL(tm6000_debug); + +static struct tm6000_fmt format[] = { + { + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = 16, + }, { + .fourcc = V4L2_PIX_FMT_UYVY, + .depth = 16, + }, { + .fourcc = V4L2_PIX_FMT_TM6000, + .depth = 16, + } +}; + +/* ------------------------------------------------------------------ + * DMA and thread functions + * ------------------------------------------------------------------ + */ + +#define norm_maxw(a) 720 +#define norm_maxh(a) 576 + +#define norm_minw(a) norm_maxw(a) +#define norm_minh(a) norm_maxh(a) + +/* + * video-buf generic routine to get the next available buffer + */ +static inline void get_next_buf(struct tm6000_dmaqueue *dma_q, + struct tm6000_buffer **buf) +{ + struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); + + if (list_empty(&dma_q->active)) { + dprintk(dev, V4L2_DEBUG_QUEUE, "No active queue to serve\n"); + *buf = NULL; + return; + } + + *buf = list_entry(dma_q->active.next, + struct tm6000_buffer, vb.queue); +} + +/* + * Announces that a buffer were filled and request the next + */ +static inline void buffer_filled(struct tm6000_core *dev, + struct tm6000_dmaqueue *dma_q, + struct tm6000_buffer *buf) +{ + /* Advice that buffer was filled */ + dprintk(dev, V4L2_DEBUG_ISOC, "[%p/%d] wakeup\n", buf, buf->vb.i); + buf->vb.state = VIDEOBUF_DONE; + buf->vb.field_count++; + buf->vb.ts = ktime_get_ns(); + + list_del(&buf->vb.queue); + wake_up(&buf->vb.done); +} + +/* + * Identify the tm5600/6000 buffer header type and properly handles + */ +static int copy_streams(u8 *data, unsigned long len, + struct urb *urb) +{ + struct tm6000_dmaqueue *dma_q = urb->context; + struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); + u8 *ptr = data, *endp = data+len; + unsigned long header = 0; + int rc = 0; + unsigned int cmd, cpysize, pktsize, size, field, block, line, pos = 0; + struct tm6000_buffer *vbuf = NULL; + char *voutp = NULL; + unsigned int linewidth; + + if (!dev->radio) { + /* get video buffer */ + get_next_buf(dma_q, &vbuf); + + if (!vbuf) + return rc; + voutp = videobuf_to_vmalloc(&vbuf->vb); + + if (!voutp) + return 0; + } + + for (ptr = data; ptr < endp;) { + if (!dev->isoc_ctl.cmd) { + /* Header */ + if (dev->isoc_ctl.tmp_buf_len > 0) { + /* from last urb or packet */ + header = dev->isoc_ctl.tmp_buf; + if (4 - dev->isoc_ctl.tmp_buf_len > 0) { + memcpy((u8 *)&header + + dev->isoc_ctl.tmp_buf_len, + ptr, + 4 - dev->isoc_ctl.tmp_buf_len); + ptr += 4 - dev->isoc_ctl.tmp_buf_len; + } + dev->isoc_ctl.tmp_buf_len = 0; + } else { + if (ptr + 3 >= endp) { + /* have incomplete header */ + dev->isoc_ctl.tmp_buf_len = endp - ptr; + memcpy(&dev->isoc_ctl.tmp_buf, ptr, + dev->isoc_ctl.tmp_buf_len); + return rc; + } + /* Seek for sync */ + for (; ptr < endp - 3; ptr++) { + if (*(ptr + 3) == 0x47) + break; + } + /* Get message header */ + header = *(unsigned long *)ptr; + ptr += 4; + } + + /* split the header fields */ + size = ((header & 0x7e) << 1); + if (size > 0) + size -= 4; + block = (header >> 7) & 0xf; + field = (header >> 11) & 0x1; + line = (header >> 12) & 0x1ff; + cmd = (header >> 21) & 0x7; + /* Validates header fields */ + if (size > TM6000_URB_MSG_LEN) + size = TM6000_URB_MSG_LEN; + pktsize = TM6000_URB_MSG_LEN; + /* + * calculate position in buffer and change the buffer + */ + switch (cmd) { + case TM6000_URB_MSG_VIDEO: + if (!dev->radio) { + if ((dev->isoc_ctl.vfield != field) && + (field == 1)) { + /* + * Announces that a new buffer + * were filled + */ + buffer_filled(dev, dma_q, vbuf); + dprintk(dev, V4L2_DEBUG_ISOC, + "new buffer filled\n"); + get_next_buf(dma_q, &vbuf); + if (!vbuf) + return rc; + voutp = videobuf_to_vmalloc(&vbuf->vb); + if (!voutp) + return rc; + memset(voutp, 0, vbuf->vb.size); + } + linewidth = vbuf->vb.width << 1; + pos = ((line << 1) - field - 1) * + linewidth + block * TM6000_URB_MSG_LEN; + /* Don't allow to write out of the buffer */ + if (pos + size > vbuf->vb.size) + cmd = TM6000_URB_MSG_ERR; + dev->isoc_ctl.vfield = field; + } + break; + case TM6000_URB_MSG_VBI: + break; + case TM6000_URB_MSG_AUDIO: + case TM6000_URB_MSG_PTS: + size = pktsize; /* Size is always 180 bytes */ + break; + } + } else { + /* Continue the last copy */ + cmd = dev->isoc_ctl.cmd; + size = dev->isoc_ctl.size; + pos = dev->isoc_ctl.pos; + pktsize = dev->isoc_ctl.pktsize; + field = dev->isoc_ctl.field; + } + cpysize = (endp - ptr > size) ? size : endp - ptr; + if (cpysize) { + /* copy data in different buffers */ + switch (cmd) { + case TM6000_URB_MSG_VIDEO: + /* Fills video buffer */ + if (vbuf) + memcpy(&voutp[pos], ptr, cpysize); + break; + case TM6000_URB_MSG_AUDIO: { + int i; + for (i = 0; i < cpysize; i += 2) + swab16s((u16 *)(ptr + i)); + + tm6000_call_fillbuf(dev, TM6000_AUDIO, ptr, cpysize); + break; + } + case TM6000_URB_MSG_VBI: + /* Need some code to copy vbi buffer */ + break; + case TM6000_URB_MSG_PTS: { + /* Need some code to copy pts */ + u32 pts; + pts = *(u32 *)ptr; + dprintk(dev, V4L2_DEBUG_ISOC, "field %d, PTS %x", + field, pts); + break; + } + } + } + if (ptr + pktsize > endp) { + /* + * End of URB packet, but cmd processing is not + * complete. Preserve the state for a next packet + */ + dev->isoc_ctl.pos = pos + cpysize; + dev->isoc_ctl.size = size - cpysize; + dev->isoc_ctl.cmd = cmd; + dev->isoc_ctl.field = field; + dev->isoc_ctl.pktsize = pktsize - (endp - ptr); + ptr += endp - ptr; + } else { + dev->isoc_ctl.cmd = 0; + ptr += pktsize; + } + } + return 0; +} + +/* + * Identify the tm5600/6000 buffer header type and properly handles + */ +static int copy_multiplexed(u8 *ptr, unsigned long len, + struct urb *urb) +{ + struct tm6000_dmaqueue *dma_q = urb->context; + struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); + unsigned int pos = dev->isoc_ctl.pos, cpysize; + int rc = 1; + struct tm6000_buffer *buf; + char *outp = NULL; + + get_next_buf(dma_q, &buf); + if (buf) + outp = videobuf_to_vmalloc(&buf->vb); + + if (!outp) + return 0; + + while (len > 0) { + cpysize = min(len, buf->vb.size-pos); + memcpy(&outp[pos], ptr, cpysize); + pos += cpysize; + ptr += cpysize; + len -= cpysize; + if (pos >= buf->vb.size) { + pos = 0; + /* Announces that a new buffer were filled */ + buffer_filled(dev, dma_q, buf); + dprintk(dev, V4L2_DEBUG_ISOC, "new buffer filled\n"); + get_next_buf(dma_q, &buf); + if (!buf) + break; + outp = videobuf_to_vmalloc(&(buf->vb)); + if (!outp) + return rc; + pos = 0; + } + } + + dev->isoc_ctl.pos = pos; + return rc; +} + +static inline void print_err_status(struct tm6000_core *dev, + int packet, int status) +{ + char *errmsg = "Unknown"; + + switch (status) { + case -ENOENT: + errmsg = "unlinked synchronously"; + break; + case -ECONNRESET: + errmsg = "unlinked asynchronously"; + break; + case -ENOSR: + errmsg = "Buffer error (overrun)"; + break; + case -EPIPE: + errmsg = "Stalled (device not responding)"; + break; + case -EOVERFLOW: + errmsg = "Babble (bad cable?)"; + break; + case -EPROTO: + errmsg = "Bit-stuff error (bad cable?)"; + break; + case -EILSEQ: + errmsg = "CRC/Timeout (could be anything)"; + break; + case -ETIME: + errmsg = "Device does not respond"; + break; + } + if (packet < 0) { + dprintk(dev, V4L2_DEBUG_QUEUE, "URB status %d [%s].\n", + status, errmsg); + } else { + dprintk(dev, V4L2_DEBUG_QUEUE, "URB packet %d, status %d [%s].\n", + packet, status, errmsg); + } +} + + +/* + * Controls the isoc copy of each urb packet + */ +static inline int tm6000_isoc_copy(struct urb *urb) +{ + struct tm6000_dmaqueue *dma_q = urb->context; + struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); + int i, len = 0, rc = 1, status; + char *p; + + if (urb->status < 0) { + print_err_status(dev, -1, urb->status); + return 0; + } + + for (i = 0; i < urb->number_of_packets; i++) { + status = urb->iso_frame_desc[i].status; + + if (status < 0) { + print_err_status(dev, i, status); + continue; + } + + len = urb->iso_frame_desc[i].actual_length; + + if (len > 0) { + p = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + if (!urb->iso_frame_desc[i].status) { + if ((dev->fourcc) == V4L2_PIX_FMT_TM6000) { + rc = copy_multiplexed(p, len, urb); + if (rc <= 0) + return rc; + } else { + copy_streams(p, len, urb); + } + } + } + } + return rc; +} + +/* ------------------------------------------------------------------ + * URB control + * ------------------------------------------------------------------ + */ + +/* + * IRQ callback, called by URB callback + */ +static void tm6000_irq_callback(struct urb *urb) +{ + struct tm6000_dmaqueue *dma_q = urb->context; + struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); + unsigned long flags; + int i; + + switch (urb->status) { + case 0: + case -ETIMEDOUT: + break; + + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + return; + + default: + tm6000_err("urb completion error %d.\n", urb->status); + break; + } + + spin_lock_irqsave(&dev->slock, flags); + tm6000_isoc_copy(urb); + spin_unlock_irqrestore(&dev->slock, flags); + + /* Reset urb buffers */ + for (i = 0; i < urb->number_of_packets; i++) { + urb->iso_frame_desc[i].status = 0; + urb->iso_frame_desc[i].actual_length = 0; + } + + urb->status = usb_submit_urb(urb, GFP_ATOMIC); + if (urb->status) + tm6000_err("urb resubmit failed (error=%i)\n", + urb->status); +} + +/* + * Allocate URB buffers + */ +static int tm6000_alloc_urb_buffers(struct tm6000_core *dev) +{ + int num_bufs = TM6000_NUM_URB_BUF; + int i; + + if (dev->urb_buffer) + return 0; + + dev->urb_buffer = kmalloc_array(num_bufs, sizeof(*dev->urb_buffer), + GFP_KERNEL); + if (!dev->urb_buffer) + return -ENOMEM; + + dev->urb_dma = kmalloc_array(num_bufs, sizeof(*dev->urb_dma), + GFP_KERNEL); + if (!dev->urb_dma) + return -ENOMEM; + + for (i = 0; i < num_bufs; i++) { + dev->urb_buffer[i] = usb_alloc_coherent( + dev->udev, dev->urb_size, + GFP_KERNEL, &dev->urb_dma[i]); + if (!dev->urb_buffer[i]) { + tm6000_err("unable to allocate %i bytes for transfer buffer %i\n", + dev->urb_size, i); + return -ENOMEM; + } + memset(dev->urb_buffer[i], 0, dev->urb_size); + } + + return 0; +} + +/* + * Free URB buffers + */ +static int tm6000_free_urb_buffers(struct tm6000_core *dev) +{ + int i; + + if (!dev->urb_buffer) + return 0; + + for (i = 0; i < TM6000_NUM_URB_BUF; i++) { + if (dev->urb_buffer[i]) { + usb_free_coherent(dev->udev, + dev->urb_size, + dev->urb_buffer[i], + dev->urb_dma[i]); + dev->urb_buffer[i] = NULL; + } + } + kfree(dev->urb_buffer); + kfree(dev->urb_dma); + dev->urb_buffer = NULL; + dev->urb_dma = NULL; + + return 0; +} + +/* + * Stop and Deallocate URBs + */ +static void tm6000_uninit_isoc(struct tm6000_core *dev) +{ + struct urb *urb; + int i; + + dev->isoc_ctl.buf = NULL; + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + urb = dev->isoc_ctl.urb[i]; + if (urb) { + usb_kill_urb(urb); + usb_unlink_urb(urb); + usb_free_urb(urb); + dev->isoc_ctl.urb[i] = NULL; + } + dev->isoc_ctl.transfer_buffer[i] = NULL; + } + + if (!keep_urb) + tm6000_free_urb_buffers(dev); + + kfree(dev->isoc_ctl.urb); + kfree(dev->isoc_ctl.transfer_buffer); + + dev->isoc_ctl.urb = NULL; + dev->isoc_ctl.transfer_buffer = NULL; + dev->isoc_ctl.num_bufs = 0; +} + +/* + * Assign URBs and start IRQ + */ +static int tm6000_prepare_isoc(struct tm6000_core *dev) +{ + struct tm6000_dmaqueue *dma_q = &dev->vidq; + int i, j, sb_size, pipe, size, max_packets; + int num_bufs = TM6000_NUM_URB_BUF; + struct urb *urb; + + /* De-allocates all pending stuff */ + tm6000_uninit_isoc(dev); + /* Stop interrupt USB pipe */ + tm6000_ir_int_stop(dev); + + usb_set_interface(dev->udev, + dev->isoc_in.bInterfaceNumber, + dev->isoc_in.bAlternateSetting); + + /* Start interrupt USB pipe */ + tm6000_ir_int_start(dev); + + pipe = usb_rcvisocpipe(dev->udev, + dev->isoc_in.endp->desc.bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK); + + size = usb_maxpacket(dev->udev, pipe); + + if (size > dev->isoc_in.maxsize) + size = dev->isoc_in.maxsize; + + dev->isoc_ctl.max_pkt_size = size; + + max_packets = TM6000_MAX_ISO_PACKETS; + sb_size = max_packets * size; + dev->urb_size = sb_size; + + dev->isoc_ctl.num_bufs = num_bufs; + + dev->isoc_ctl.urb = kmalloc_array(num_bufs, sizeof(void *), + GFP_KERNEL); + if (!dev->isoc_ctl.urb) + return -ENOMEM; + + dev->isoc_ctl.transfer_buffer = kmalloc_array(num_bufs, + sizeof(void *), + GFP_KERNEL); + if (!dev->isoc_ctl.transfer_buffer) { + kfree(dev->isoc_ctl.urb); + return -ENOMEM; + } + + dprintk(dev, V4L2_DEBUG_QUEUE, "Allocating %d x %d packets (%d bytes) of %d bytes each to handle %u size\n", + max_packets, num_bufs, sb_size, + dev->isoc_in.maxsize, size); + + + if (tm6000_alloc_urb_buffers(dev) < 0) { + tm6000_err("cannot allocate memory for urb buffers\n"); + + /* call free, as some buffers might have been allocated */ + tm6000_free_urb_buffers(dev); + kfree(dev->isoc_ctl.urb); + kfree(dev->isoc_ctl.transfer_buffer); + return -ENOMEM; + } + + /* allocate urbs and transfer buffers */ + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + urb = usb_alloc_urb(max_packets, GFP_KERNEL); + if (!urb) { + tm6000_uninit_isoc(dev); + tm6000_free_urb_buffers(dev); + return -ENOMEM; + } + dev->isoc_ctl.urb[i] = urb; + + urb->transfer_dma = dev->urb_dma[i]; + dev->isoc_ctl.transfer_buffer[i] = dev->urb_buffer[i]; + + usb_fill_bulk_urb(urb, dev->udev, pipe, + dev->isoc_ctl.transfer_buffer[i], sb_size, + tm6000_irq_callback, dma_q); + urb->interval = dev->isoc_in.endp->desc.bInterval; + urb->number_of_packets = max_packets; + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; + + for (j = 0; j < max_packets; j++) { + urb->iso_frame_desc[j].offset = size * j; + urb->iso_frame_desc[j].length = size; + } + } + + return 0; +} + +static int tm6000_start_thread(struct tm6000_core *dev) +{ + struct tm6000_dmaqueue *dma_q = &dev->vidq; + int i; + + dma_q->frame = 0; + dma_q->ini_jiffies = jiffies; + + init_waitqueue_head(&dma_q->wq); + + /* submit urbs and enables IRQ */ + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + int rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC); + if (rc) { + tm6000_err("submit of urb %i failed (error=%i)\n", i, + rc); + tm6000_uninit_isoc(dev); + return rc; + } + } + + return 0; +} + +/* ------------------------------------------------------------------ + * Videobuf operations + * ------------------------------------------------------------------ + */ + +static int +buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) +{ + struct tm6000_fh *fh = vq->priv_data; + + *size = fh->fmt->depth * fh->width * fh->height >> 3; + if (0 == *count) + *count = TM6000_DEF_BUF; + + if (*count < TM6000_MIN_BUF) + *count = TM6000_MIN_BUF; + + while (*size * *count > vid_limit * 1024 * 1024) + (*count)--; + + return 0; +} + +static void free_buffer(struct videobuf_queue *vq, struct tm6000_buffer *buf) +{ + struct tm6000_fh *fh = vq->priv_data; + struct tm6000_core *dev = fh->dev; + unsigned long flags; + + /* We used to wait for the buffer to finish here, but this didn't work + because, as we were keeping the state as VIDEOBUF_QUEUED, + videobuf_queue_cancel marked it as finished for us. + (Also, it could wedge forever if the hardware was misconfigured.) + + This should be safe; by the time we get here, the buffer isn't + queued anymore. If we ever start marking the buffers as + VIDEOBUF_ACTIVE, it won't be, though. + */ + spin_lock_irqsave(&dev->slock, flags); + if (dev->isoc_ctl.buf == buf) + dev->isoc_ctl.buf = NULL; + spin_unlock_irqrestore(&dev->slock, flags); + + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int +buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct tm6000_fh *fh = vq->priv_data; + struct tm6000_buffer *buf = container_of(vb, struct tm6000_buffer, vb); + struct tm6000_core *dev = fh->dev; + int rc = 0; + + BUG_ON(NULL == fh->fmt); + + + /* FIXME: It assumes depth=2 */ + /* The only currently supported format is 16 bits/pixel */ + buf->vb.size = fh->fmt->depth*fh->width*fh->height >> 3; + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + if (buf->fmt != fh->fmt || + buf->vb.width != fh->width || + buf->vb.height != fh->height || + buf->vb.field != field) { + buf->fmt = fh->fmt; + buf->vb.width = fh->width; + buf->vb.height = fh->height; + buf->vb.field = field; + buf->vb.state = VIDEOBUF_NEEDS_INIT; + } + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + rc = videobuf_iolock(vq, &buf->vb, NULL); + if (rc != 0) + goto fail; + } + + if (!dev->isoc_ctl.num_bufs) { + rc = tm6000_prepare_isoc(dev); + if (rc < 0) + goto fail; + + rc = tm6000_start_thread(dev); + if (rc < 0) + goto fail; + + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; + +fail: + free_buffer(vq, buf); + return rc; +} + +static void +buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct tm6000_buffer *buf = container_of(vb, struct tm6000_buffer, vb); + struct tm6000_fh *fh = vq->priv_data; + struct tm6000_core *dev = fh->dev; + struct tm6000_dmaqueue *vidq = &dev->vidq; + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &vidq->active); +} + +static void buffer_release(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct tm6000_buffer *buf = container_of(vb, struct tm6000_buffer, vb); + + free_buffer(vq, buf); +} + +static const struct videobuf_queue_ops tm6000_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +/* ------------------------------------------------------------------ + * IOCTL handling + * ------------------------------------------------------------------ + */ + +static bool is_res_read(struct tm6000_core *dev, struct tm6000_fh *fh) +{ + /* Is the current fh handling it? if so, that's OK */ + if (dev->resources == fh && dev->is_res_read) + return true; + + return false; +} + +static bool is_res_streaming(struct tm6000_core *dev, struct tm6000_fh *fh) +{ + /* Is the current fh handling it? if so, that's OK */ + if (dev->resources == fh) + return true; + + return false; +} + +static bool res_get(struct tm6000_core *dev, struct tm6000_fh *fh, + bool is_res_read) +{ + /* Is the current fh handling it? if so, that's OK */ + if (dev->resources == fh && dev->is_res_read == is_res_read) + return true; + + /* is it free? */ + if (dev->resources) + return false; + + /* grab it */ + dev->resources = fh; + dev->is_res_read = is_res_read; + dprintk(dev, V4L2_DEBUG_RES_LOCK, "res: get\n"); + return true; +} + +static void res_free(struct tm6000_core *dev, struct tm6000_fh *fh) +{ + /* Is the current fh handling it? if so, that's OK */ + if (dev->resources != fh) + return; + + dev->resources = NULL; + dprintk(dev, V4L2_DEBUG_RES_LOCK, "res: put\n"); +} + +/* ------------------------------------------------------------------ + * IOCTL vidioc handling + * ------------------------------------------------------------------ + */ +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct tm6000_core *dev = ((struct tm6000_fh *)priv)->dev; + + strscpy(cap->driver, "tm6000", sizeof(cap->driver)); + strscpy(cap->card, "Trident TM5600/6000/6010", sizeof(cap->card)); + usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_DEVICE_CAPS; + if (dev->tuner_type != TUNER_ABSENT) + cap->capabilities |= V4L2_CAP_TUNER; + if (dev->caps.has_radio) + cap->capabilities |= V4L2_CAP_RADIO; + + return 0; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index >= ARRAY_SIZE(format)) + return -EINVAL; + + f->pixelformat = format[f->index].fourcc; + return 0; +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tm6000_fh *fh = priv; + + f->fmt.pix.width = fh->width; + f->fmt.pix.height = fh->height; + f->fmt.pix.field = fh->vb_vidq.field; + f->fmt.pix.pixelformat = fh->fmt->fourcc; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fh->fmt->depth) >> 3; + f->fmt.pix.sizeimage = + f->fmt.pix.height * f->fmt.pix.bytesperline; + + return 0; +} + +static struct tm6000_fmt *format_by_fourcc(unsigned int fourcc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(format); i++) + if (format[i].fourcc == fourcc) + return format+i; + return NULL; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tm6000_core *dev = ((struct tm6000_fh *)priv)->dev; + struct tm6000_fmt *fmt; + enum v4l2_field field; + + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + if (NULL == fmt) { + dprintk(dev, 2, "Fourcc format (0x%08x) invalid.\n", + f->fmt.pix.pixelformat); + return -EINVAL; + } + + field = V4L2_FIELD_INTERLACED; + + tm6000_get_std_res(dev); + + f->fmt.pix.width = dev->width; + f->fmt.pix.height = dev->height; + + f->fmt.pix.width &= ~0x01; + + f->fmt.pix.field = field; + + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fmt->depth) >> 3; + f->fmt.pix.sizeimage = + f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + + return 0; +} + +/*FIXME: This seems to be generic enough to be at videodev2 */ +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + int ret = vidioc_try_fmt_vid_cap(file, fh, f); + if (ret < 0) + return ret; + + fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); + fh->width = f->fmt.pix.width; + fh->height = f->fmt.pix.height; + fh->vb_vidq.field = f->fmt.pix.field; + fh->type = f->type; + + dev->fourcc = f->fmt.pix.pixelformat; + + tm6000_set_fourcc_format(dev); + + return 0; +} + +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + struct tm6000_fh *fh = priv; + + return videobuf_reqbufs(&fh->vb_vidq, p); +} + +static int vidioc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct tm6000_fh *fh = priv; + + return videobuf_querybuf(&fh->vb_vidq, p); +} + +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct tm6000_fh *fh = priv; + + return videobuf_qbuf(&fh->vb_vidq, p); +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct tm6000_fh *fh = priv; + + return videobuf_dqbuf(&fh->vb_vidq, p, + file->f_flags & O_NONBLOCK); +} + +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (i != fh->type) + return -EINVAL; + + if (!res_get(dev, fh, false)) + return -EBUSY; + return videobuf_streamon(&fh->vb_vidq); +} + +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (i != fh->type) + return -EINVAL; + + videobuf_streamoff(&fh->vb_vidq); + res_free(dev, fh); + + return 0; +} + +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) +{ + int rc = 0; + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + dev->norm = norm; + rc = tm6000_init_analog_mode(dev); + + fh->width = dev->width; + fh->height = dev->height; + + if (rc < 0) + return rc; + + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_std, dev->norm); + + return 0; +} + +static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + *norm = dev->norm; + return 0; +} + +static const char *iname[] = { + [TM6000_INPUT_TV] = "Television", + [TM6000_INPUT_COMPOSITE1] = "Composite 1", + [TM6000_INPUT_COMPOSITE2] = "Composite 2", + [TM6000_INPUT_SVIDEO] = "S-Video", +}; + +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + unsigned int n; + + n = i->index; + if (n >= 3) + return -EINVAL; + + if (!dev->vinput[n].type) + return -EINVAL; + + i->index = n; + + if (dev->vinput[n].type == TM6000_INPUT_TV) + i->type = V4L2_INPUT_TYPE_TUNER; + else + i->type = V4L2_INPUT_TYPE_CAMERA; + + strscpy(i->name, iname[dev->vinput[n].type], sizeof(i->name)); + + i->std = TM6000_STD; + + return 0; +} + +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + *i = dev->input; + + return 0; +} + +static int vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + int rc = 0; + + if (i >= 3) + return -EINVAL; + if (!dev->vinput[i].type) + return -EINVAL; + + dev->input = i; + + rc = vidioc_s_std(file, priv, dev->norm); + + return rc; +} + +/* --- controls ---------------------------------------------- */ + +static int tm6000_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct tm6000_core *dev = container_of(ctrl->handler, struct tm6000_core, ctrl_handler); + u8 val = ctrl->val; + + switch (ctrl->id) { + case V4L2_CID_CONTRAST: + tm6000_set_reg(dev, TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, val); + return 0; + case V4L2_CID_BRIGHTNESS: + tm6000_set_reg(dev, TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, val); + return 0; + case V4L2_CID_SATURATION: + tm6000_set_reg(dev, TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, val); + return 0; + case V4L2_CID_HUE: + tm6000_set_reg(dev, TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ, val); + return 0; + } + return -EINVAL; +} + +static const struct v4l2_ctrl_ops tm6000_ctrl_ops = { + .s_ctrl = tm6000_s_ctrl, +}; + +static int tm6000_radio_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct tm6000_core *dev = container_of(ctrl->handler, + struct tm6000_core, radio_ctrl_handler); + u8 val = ctrl->val; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + dev->ctl_mute = val; + tm6000_tvaudio_set_mute(dev, val); + return 0; + case V4L2_CID_AUDIO_VOLUME: + dev->ctl_volume = val; + tm6000_set_volume(dev, val); + return 0; + } + return -EINVAL; +} + +static const struct v4l2_ctrl_ops tm6000_radio_ctrl_ops = { + .s_ctrl = tm6000_radio_s_ctrl, +}; + +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + if (UNSET == dev->tuner_type) + return -ENOTTY; + if (0 != t->index) + return -EINVAL; + + strscpy(t->name, "Television", sizeof(t->name)); + t->type = V4L2_TUNER_ANALOG_TV; + t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO; + t->rangehigh = 0xffffffffUL; + t->rxsubchans = V4L2_TUNER_SUB_STEREO; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); + + t->audmode = dev->amode; + + return 0; +} + +static int vidioc_s_tuner(struct file *file, void *priv, + const struct v4l2_tuner *t) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + if (UNSET == dev->tuner_type) + return -ENOTTY; + if (0 != t->index) + return -EINVAL; + + if (t->audmode > V4L2_TUNER_MODE_STEREO) + dev->amode = V4L2_TUNER_MODE_STEREO; + else + dev->amode = t->audmode; + dprintk(dev, 3, "audio mode: %x\n", t->audmode); + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); + + return 0; +} + +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + if (UNSET == dev->tuner_type) + return -ENOTTY; + if (f->tuner) + return -EINVAL; + + f->frequency = dev->freq; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_frequency, f); + + return 0; +} + +static int vidioc_s_frequency(struct file *file, void *priv, + const struct v4l2_frequency *f) +{ + struct tm6000_fh *fh = priv; + struct tm6000_core *dev = fh->dev; + + if (UNSET == dev->tuner_type) + return -ENOTTY; + if (f->tuner != 0) + return -EINVAL; + + dev->freq = f->frequency; + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, f); + + return 0; +} + +static int radio_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_core *dev = fh->dev; + + if (0 != t->index) + return -EINVAL; + + memset(t, 0, sizeof(*t)); + strscpy(t->name, "Radio", sizeof(t->name)); + t->type = V4L2_TUNER_RADIO; + t->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; + t->rxsubchans = V4L2_TUNER_SUB_STEREO; + t->audmode = V4L2_TUNER_MODE_STEREO; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); + + return 0; +} + +static int radio_s_tuner(struct file *file, void *priv, + const struct v4l2_tuner *t) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_core *dev = fh->dev; + + if (0 != t->index) + return -EINVAL; + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); + return 0; +} + +/* ------------------------------------------------------------------ + File operations for the device + ------------------------------------------------------------------*/ + +static int __tm6000_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct tm6000_core *dev = video_drvdata(file); + struct tm6000_fh *fh; + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + int rc; + int radio = 0; + + dprintk(dev, V4L2_DEBUG_OPEN, "tm6000: open called (dev=%s)\n", + video_device_node_name(vdev)); + + switch (vdev->vfl_type) { + case VFL_TYPE_VIDEO: + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + break; + case VFL_TYPE_VBI: + type = V4L2_BUF_TYPE_VBI_CAPTURE; + break; + case VFL_TYPE_RADIO: + radio = 1; + break; + default: + return -EINVAL; + } + + /* If more than one user, mutex should be added */ + dev->users++; + + dprintk(dev, V4L2_DEBUG_OPEN, "open dev=%s type=%s users=%d\n", + video_device_node_name(vdev), v4l2_type_names[type], + dev->users); + + /* allocate + initialize per filehandle data */ + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (NULL == fh) { + dev->users--; + return -ENOMEM; + } + + v4l2_fh_init(&fh->fh, vdev); + file->private_data = fh; + fh->dev = dev; + fh->radio = radio; + dev->radio = radio; + fh->type = type; + dev->fourcc = format[0].fourcc; + + fh->fmt = format_by_fourcc(dev->fourcc); + + tm6000_get_std_res(dev); + + fh->width = dev->width; + fh->height = dev->height; + + dprintk(dev, V4L2_DEBUG_OPEN, "Open: fh=%p, dev=%p, dev->vidq=%p\n", + fh, dev, &dev->vidq); + dprintk(dev, V4L2_DEBUG_OPEN, "Open: list_empty queued=%d\n", + list_empty(&dev->vidq.queued)); + dprintk(dev, V4L2_DEBUG_OPEN, "Open: list_empty active=%d\n", + list_empty(&dev->vidq.active)); + + /* initialize hardware on analog mode */ + rc = tm6000_init_analog_mode(dev); + if (rc < 0) { + v4l2_fh_exit(&fh->fh); + kfree(fh); + return rc; + } + + dev->mode = TM6000_MODE_ANALOG; + + if (!fh->radio) { + videobuf_queue_vmalloc_init(&fh->vb_vidq, &tm6000_video_qops, + NULL, &dev->slock, + fh->type, + V4L2_FIELD_INTERLACED, + sizeof(struct tm6000_buffer), fh, &dev->lock); + } else { + dprintk(dev, V4L2_DEBUG_OPEN, "video_open: setting radio device\n"); + tm6000_set_audio_rinput(dev); + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_radio); + tm6000_prepare_isoc(dev); + tm6000_start_thread(dev); + } + v4l2_fh_add(&fh->fh); + + return 0; +} + +static int tm6000_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + int res; + + mutex_lock(vdev->lock); + res = __tm6000_open(file); + mutex_unlock(vdev->lock); + return res; +} + +static ssize_t +tm6000_read(struct file *file, char __user *data, size_t count, loff_t *pos) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_core *dev = fh->dev; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + int res; + + if (!res_get(fh->dev, fh, true)) + return -EBUSY; + + if (mutex_lock_interruptible(&dev->lock)) + return -ERESTARTSYS; + res = videobuf_read_stream(&fh->vb_vidq, data, count, pos, 0, + file->f_flags & O_NONBLOCK); + mutex_unlock(&dev->lock); + return res; + } + return 0; +} + +static __poll_t +__tm6000_poll(struct file *file, struct poll_table_struct *wait) +{ + __poll_t req_events = poll_requested_events(wait); + struct tm6000_fh *fh = file->private_data; + struct tm6000_buffer *buf; + __poll_t res = 0; + + if (v4l2_event_pending(&fh->fh)) + res = EPOLLPRI; + else if (req_events & EPOLLPRI) + poll_wait(file, &fh->fh.wait, wait); + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) + return res | EPOLLERR; + + if (!!is_res_streaming(fh->dev, fh)) + return res | EPOLLERR; + + if (!is_res_read(fh->dev, fh)) { + /* streaming capture */ + if (list_empty(&fh->vb_vidq.stream)) + return res | EPOLLERR; + buf = list_entry(fh->vb_vidq.stream.next, struct tm6000_buffer, vb.stream); + poll_wait(file, &buf->vb.done, wait); + if (buf->vb.state == VIDEOBUF_DONE || + buf->vb.state == VIDEOBUF_ERROR) + return res | EPOLLIN | EPOLLRDNORM; + } else if (req_events & (EPOLLIN | EPOLLRDNORM)) { + /* read() capture */ + return res | videobuf_poll_stream(file, &fh->vb_vidq, wait); + } + return res; +} + +static __poll_t tm6000_poll(struct file *file, struct poll_table_struct *wait) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_core *dev = fh->dev; + __poll_t res; + + mutex_lock(&dev->lock); + res = __tm6000_poll(file, wait); + mutex_unlock(&dev->lock); + return res; +} + +static int tm6000_release(struct file *file) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_core *dev = fh->dev; + struct video_device *vdev = video_devdata(file); + + dprintk(dev, V4L2_DEBUG_OPEN, "tm6000: close called (dev=%s, users=%d)\n", + video_device_node_name(vdev), dev->users); + + mutex_lock(&dev->lock); + dev->users--; + + res_free(dev, fh); + + if (!dev->users) { + tm6000_uninit_isoc(dev); + + /* Stop interrupt USB pipe */ + tm6000_ir_int_stop(dev); + + usb_reset_configuration(dev->udev); + + if (dev->int_in.endp) + usb_set_interface(dev->udev, + dev->isoc_in.bInterfaceNumber, 2); + else + usb_set_interface(dev->udev, + dev->isoc_in.bInterfaceNumber, 0); + + /* Start interrupt USB pipe */ + tm6000_ir_int_start(dev); + + if (!fh->radio) + videobuf_mmap_free(&fh->vb_vidq); + } + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); + kfree(fh); + mutex_unlock(&dev->lock); + + return 0; +} + +static int tm6000_mmap(struct file *file, struct vm_area_struct * vma) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_core *dev = fh->dev; + int res; + + if (mutex_lock_interruptible(&dev->lock)) + return -ERESTARTSYS; + res = videobuf_mmap_mapper(&fh->vb_vidq, vma); + mutex_unlock(&dev->lock); + return res; +} + +static const struct v4l2_file_operations tm6000_fops = { + .owner = THIS_MODULE, + .open = tm6000_open, + .release = tm6000_release, + .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ + .read = tm6000_read, + .poll = tm6000_poll, + .mmap = tm6000_mmap, +}; + +static const struct v4l2_ioctl_ops video_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_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_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static struct video_device tm6000_template = { + .name = "tm6000", + .fops = &tm6000_fops, + .ioctl_ops = &video_ioctl_ops, + .release = video_device_release_empty, + .tvnorms = TM6000_STD, +}; + +static const struct v4l2_file_operations radio_fops = { + .owner = THIS_MODULE, + .open = tm6000_open, + .poll = v4l2_ctrl_poll, + .release = tm6000_release, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops radio_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = radio_g_tuner, + .vidioc_s_tuner = radio_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static struct video_device tm6000_radio_template = { + .name = "tm6000", + .fops = &radio_fops, + .ioctl_ops = &radio_ioctl_ops, +}; + +/* ----------------------------------------------------------------- + * Initialization and module stuff + * ------------------------------------------------------------------ + */ + +static void vdev_init(struct tm6000_core *dev, + struct video_device *vfd, + const struct video_device + *template, const char *type_name) +{ + *vfd = *template; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->release = video_device_release_empty; + vfd->lock = &dev->lock; + + snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); + + video_set_drvdata(vfd, dev); +} + +int tm6000_v4l2_register(struct tm6000_core *dev) +{ + int ret = 0; + + v4l2_ctrl_handler_init(&dev->ctrl_handler, 6); + v4l2_ctrl_handler_init(&dev->radio_ctrl_handler, 2); + v4l2_ctrl_new_std(&dev->radio_ctrl_handler, &tm6000_radio_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); + v4l2_ctrl_new_std(&dev->radio_ctrl_handler, &tm6000_radio_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, -15, 15, 1, 0); + v4l2_ctrl_new_std(&dev->ctrl_handler, &tm6000_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 54); + v4l2_ctrl_new_std(&dev->ctrl_handler, &tm6000_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 119); + v4l2_ctrl_new_std(&dev->ctrl_handler, &tm6000_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 112); + v4l2_ctrl_new_std(&dev->ctrl_handler, &tm6000_ctrl_ops, + V4L2_CID_HUE, -128, 127, 1, 0); + v4l2_ctrl_add_handler(&dev->ctrl_handler, + &dev->radio_ctrl_handler, NULL, false); + + if (dev->radio_ctrl_handler.error) + ret = dev->radio_ctrl_handler.error; + if (!ret && dev->ctrl_handler.error) + ret = dev->ctrl_handler.error; + if (ret) + goto free_ctrl; + + vdev_init(dev, &dev->vfd, &tm6000_template, "video"); + + dev->vfd.ctrl_handler = &dev->ctrl_handler; + dev->vfd.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE; + if (dev->tuner_type != TUNER_ABSENT) + dev->vfd.device_caps |= V4L2_CAP_TUNER; + + /* init video dma queues */ + INIT_LIST_HEAD(&dev->vidq.active); + INIT_LIST_HEAD(&dev->vidq.queued); + + ret = video_register_device(&dev->vfd, VFL_TYPE_VIDEO, video_nr); + + if (ret < 0) { + printk(KERN_INFO "%s: can't register video device\n", + dev->name); + goto free_ctrl; + } + + printk(KERN_INFO "%s: registered device %s\n", + dev->name, video_device_node_name(&dev->vfd)); + + if (dev->caps.has_radio) { + vdev_init(dev, &dev->radio_dev, &tm6000_radio_template, + "radio"); + dev->radio_dev.ctrl_handler = &dev->radio_ctrl_handler; + dev->radio_dev.device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER; + ret = video_register_device(&dev->radio_dev, VFL_TYPE_RADIO, + radio_nr); + if (ret < 0) { + printk(KERN_INFO "%s: can't register radio device\n", + dev->name); + goto unreg_video; + } + + printk(KERN_INFO "%s: registered device %s\n", + dev->name, video_device_node_name(&dev->radio_dev)); + } + + printk(KERN_INFO "Trident TVMaster TM5600/TM6000/TM6010 USB2 board (Load status: %d)\n", ret); + return ret; + +unreg_video: + video_unregister_device(&dev->vfd); +free_ctrl: + v4l2_ctrl_handler_free(&dev->ctrl_handler); + v4l2_ctrl_handler_free(&dev->radio_ctrl_handler); + return ret; +} + +int tm6000_v4l2_unregister(struct tm6000_core *dev) +{ + video_unregister_device(&dev->vfd); + + /* if URB buffers are still allocated free them now */ + tm6000_free_urb_buffers(dev); + + video_unregister_device(&dev->radio_dev); + return 0; +} + +int tm6000_v4l2_exit(void) +{ + return 0; +} + +module_param(video_nr, int, 0); +MODULE_PARM_DESC(video_nr, "Allow changing video device number"); + +module_param_named(debug, tm6000_debug, int, 0444); +MODULE_PARM_DESC(debug, "activates debug info"); + +module_param(vid_limit, int, 0644); +MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); + +module_param(keep_urb, bool, 0); +MODULE_PARM_DESC(keep_urb, "Keep urb buffers allocated even when the device is closed by the user"); diff --git a/drivers/staging/media/deprecated/tm6000/tm6000.h b/drivers/staging/media/deprecated/tm6000/tm6000.h new file mode 100644 index 000000000000..c08c95312739 --- /dev/null +++ b/drivers/staging/media/deprecated/tm6000/tm6000.h @@ -0,0 +1,396 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * tm6000.h - driver for TM5600/TM6000/TM6010 USB video capture devices + * + * Copyright (c) 2006-2007 Mauro Carvalho Chehab <mchehab@kernel.org> + * + * Copyright (c) 2007 Michel Ludwig <michel.ludwig@gmail.com> + * - DVB-T support + */ + +#include <linux/videodev2.h> +#include <media/v4l2-common.h> +#include <media/videobuf-vmalloc.h> +#include "tm6000-usb-isoc.h" +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-fh.h> + +#include <linux/dvb/frontend.h> +#include <media/dvb_demux.h> +#include <media/dvb_frontend.h> +#include <media/dmxdev.h> + +/* Inputs */ +enum tm6000_itype { + TM6000_INPUT_TV = 1, + TM6000_INPUT_COMPOSITE1, + TM6000_INPUT_COMPOSITE2, + TM6000_INPUT_SVIDEO, + TM6000_INPUT_DVB, + TM6000_INPUT_RADIO, +}; + +enum tm6000_mux { + TM6000_VMUX_VIDEO_A = 1, + TM6000_VMUX_VIDEO_B, + TM6000_VMUX_VIDEO_AB, + TM6000_AMUX_ADC1, + TM6000_AMUX_ADC2, + TM6000_AMUX_SIF1, + TM6000_AMUX_SIF2, + TM6000_AMUX_I2S, +}; + +enum tm6000_devtype { + TM6000 = 0, + TM5600, + TM6010, +}; + +struct tm6000_input { + enum tm6000_itype type; + enum tm6000_mux vmux; + enum tm6000_mux amux; + unsigned int v_gpio; + unsigned int a_gpio; +}; + +/* ------------------------------------------------------------------ + * Basic structures + * ------------------------------------------------------------------ + */ + +struct tm6000_fmt { + u32 fourcc; /* v4l2 format id */ + int depth; +}; + +/* buffer for one video frame */ +struct tm6000_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + + struct tm6000_fmt *fmt; +}; + +struct tm6000_dmaqueue { + struct list_head active; + struct list_head queued; + + /* thread for generating video stream*/ + struct task_struct *kthread; + wait_queue_head_t wq; + /* Counters to control fps rate */ + int frame; + int ini_jiffies; +}; + +/* device states */ +enum tm6000_core_state { + DEV_INITIALIZED = 0x01, + DEV_DISCONNECTED = 0x02, + DEV_MISCONFIGURED = 0x04, +}; + +/* io methods */ +enum tm6000_io_method { + IO_NONE, + IO_READ, + IO_MMAP, +}; + +enum tm6000_mode { + TM6000_MODE_UNKNOWN = 0, + TM6000_MODE_ANALOG, + TM6000_MODE_DIGITAL, +}; + +struct tm6000_gpio { + int tuner_reset; + int tuner_on; + int demod_reset; + int demod_on; + int power_led; + int dvb_led; + int ir; +}; + +struct tm6000_capabilities { + unsigned int has_tuner:1; + unsigned int has_tda9874:1; + unsigned int has_dvb:1; + unsigned int has_zl10353:1; + unsigned int has_eeprom:1; + unsigned int has_remote:1; + unsigned int has_radio:1; +}; + +struct tm6000_dvb { + struct dvb_adapter adapter; + struct dvb_demux demux; + struct dvb_frontend *frontend; + struct dmxdev dmxdev; + unsigned int streams; + struct urb *bulk_urb; + struct mutex mutex; +}; + +struct snd_tm6000_card { + struct snd_card *card; + spinlock_t reg_lock; + struct tm6000_core *core; + struct snd_pcm_substream *substream; + + /* temporary data for buffer fill processing */ + unsigned buf_pos; + unsigned period_pos; +}; + +struct tm6000_endpoint { + struct usb_host_endpoint *endp; + __u8 bInterfaceNumber; + __u8 bAlternateSetting; + unsigned maxsize; +}; + +#define TM6000_QUIRK_NO_USB_DELAY (1 << 0) + +struct tm6000_core { + /* generic device properties */ + char name[30]; /* name (including minor) of the device */ + int model; /* index in the device_data struct */ + int devno; /* marks the number of this device */ + enum tm6000_devtype dev_type; /* type of device */ + unsigned char eedata[256]; /* Eeprom data */ + unsigned eedata_size; /* Size of the eeprom info */ + + v4l2_std_id norm; /* Current norm */ + int width, height; /* Selected resolution */ + + enum tm6000_core_state state; + + /* Device Capabilities*/ + struct tm6000_capabilities caps; + + /* Used to load alsa/dvb */ + struct work_struct request_module_wk; + + /* Tuner configuration */ + int tuner_type; /* type of the tuner */ + int tuner_addr; /* tuner address */ + + struct tm6000_gpio gpio; + + char *ir_codes; + + __u8 radio; + + /* Demodulator configuration */ + int demod_addr; /* demodulator address */ + + int audio_bitrate; + /* i2c i/o */ + struct i2c_adapter i2c_adap; + struct i2c_client i2c_client; + + + /* extension */ + struct list_head devlist; + + /* video for linux */ + int users; + + /* various device info */ + struct tm6000_fh *resources; /* Points to fh that is streaming */ + bool is_res_read; + + struct video_device vfd; + struct video_device radio_dev; + struct tm6000_dmaqueue vidq; + struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl_handler radio_ctrl_handler; + + int input; + struct tm6000_input vinput[3]; /* video input */ + struct tm6000_input rinput; /* radio input */ + + int freq; + unsigned int fourcc; + + enum tm6000_mode mode; + + int ctl_mute; /* audio */ + int ctl_volume; + int amode; + + /* DVB-T support */ + struct tm6000_dvb *dvb; + + /* audio support */ + struct snd_tm6000_card *adev; + struct work_struct wq_trigger; /* Trigger to start/stop audio for alsa module */ + atomic_t stream_started; /* stream should be running if true */ + + struct tm6000_IR *ir; + + /* locks */ + struct mutex lock; + struct mutex usb_lock; + + /* usb transfer */ + struct usb_device *udev; /* the usb device */ + + struct tm6000_endpoint bulk_in, bulk_out, isoc_in, isoc_out; + struct tm6000_endpoint int_in, int_out; + + /* scaler!=0 if scaler is active*/ + int scaler; + + /* Isoc control struct */ + struct usb_isoc_ctl isoc_ctl; + + spinlock_t slock; + + /* urb dma buffers */ + char **urb_buffer; + dma_addr_t *urb_dma; + unsigned int urb_size; + + unsigned long quirks; +}; + +enum tm6000_ops_type { + TM6000_AUDIO = 0x10, + TM6000_DVB = 0x20, +}; + +struct tm6000_ops { + struct list_head next; + char *name; + enum tm6000_ops_type type; + int (*init)(struct tm6000_core *); + int (*fini)(struct tm6000_core *); + int (*fillbuf)(struct tm6000_core *, char *buf, int size); +}; + +struct tm6000_fh { + struct v4l2_fh fh; + struct tm6000_core *dev; + unsigned int radio; + + /* video capture */ + struct tm6000_fmt *fmt; + unsigned int width, height; + struct videobuf_queue vb_vidq; + + enum v4l2_buf_type type; +}; + +#define TM6000_STD (V4L2_STD_PAL|V4L2_STD_PAL_N|V4L2_STD_PAL_Nc| \ + V4L2_STD_PAL_M|V4L2_STD_PAL_60|V4L2_STD_NTSC_M| \ + V4L2_STD_NTSC_M_JP|V4L2_STD_SECAM) + +/* In tm6000-cards.c */ + +int tm6000_tuner_callback(void *ptr, int component, int command, int arg); +int tm6000_xc5000_callback(void *ptr, int component, int command, int arg); +int tm6000_cards_setup(struct tm6000_core *dev); +void tm6000_flash_led(struct tm6000_core *dev, u8 state); + +/* In tm6000-core.c */ + +int tm6000_read_write_usb(struct tm6000_core *dev, u8 reqtype, u8 req, + u16 value, u16 index, u8 *buf, u16 len); +int tm6000_get_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index); +int tm6000_get_reg16(struct tm6000_core *dev, u8 req, u16 value, u16 index); +int tm6000_get_reg32(struct tm6000_core *dev, u8 req, u16 value, u16 index); +int tm6000_set_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index); +int tm6000_set_reg_mask(struct tm6000_core *dev, u8 req, u16 value, + u16 index, u16 mask); +int tm6000_i2c_reset(struct tm6000_core *dev, u16 tsleep); +int tm6000_init(struct tm6000_core *dev); +int tm6000_reset(struct tm6000_core *dev); + +int tm6000_init_analog_mode(struct tm6000_core *dev); +int tm6000_init_digital_mode(struct tm6000_core *dev); +int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate); +int tm6000_set_audio_rinput(struct tm6000_core *dev); +int tm6000_tvaudio_set_mute(struct tm6000_core *dev, u8 mute); +void tm6000_set_volume(struct tm6000_core *dev, int vol); + +int tm6000_v4l2_register(struct tm6000_core *dev); +int tm6000_v4l2_unregister(struct tm6000_core *dev); +int tm6000_v4l2_exit(void); +void tm6000_set_fourcc_format(struct tm6000_core *dev); + +void tm6000_remove_from_devlist(struct tm6000_core *dev); +void tm6000_add_into_devlist(struct tm6000_core *dev); +int tm6000_register_extension(struct tm6000_ops *ops); +void tm6000_unregister_extension(struct tm6000_ops *ops); +void tm6000_init_extension(struct tm6000_core *dev); +void tm6000_close_extension(struct tm6000_core *dev); +int tm6000_call_fillbuf(struct tm6000_core *dev, enum tm6000_ops_type type, + char *buf, int size); + + +/* In tm6000-stds.c */ +void tm6000_get_std_res(struct tm6000_core *dev); +int tm6000_set_standard(struct tm6000_core *dev); + +/* In tm6000-i2c.c */ +int tm6000_i2c_register(struct tm6000_core *dev); +int tm6000_i2c_unregister(struct tm6000_core *dev); + +/* In tm6000-queue.c */ + +int tm6000_v4l2_mmap(struct file *filp, struct vm_area_struct *vma); + +int tm6000_vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type i); +int tm6000_vidioc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type i); +int tm6000_vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *rb); +int tm6000_vidioc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *b); +int tm6000_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b); +int tm6000_vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b); +ssize_t tm6000_v4l2_read(struct file *filp, char __user * buf, size_t count, + loff_t *f_pos); +unsigned int tm6000_v4l2_poll(struct file *file, + struct poll_table_struct *wait); +int tm6000_queue_init(struct tm6000_core *dev); + +/* In tm6000-alsa.c */ +/*int tm6000_audio_init(struct tm6000_core *dev, int idx);*/ + +/* In tm6000-input.c */ +int tm6000_ir_init(struct tm6000_core *dev); +int tm6000_ir_fini(struct tm6000_core *dev); +void tm6000_ir_wait(struct tm6000_core *dev, u8 state); +int tm6000_ir_int_start(struct tm6000_core *dev); +void tm6000_ir_int_stop(struct tm6000_core *dev); + +/* Debug stuff */ + +extern int tm6000_debug; + +#define dprintk(dev, level, fmt, arg...) do {\ + if (tm6000_debug & level) \ + printk(KERN_INFO "(%lu) %s %s :"fmt, jiffies, \ + dev->name, __func__ , ##arg); } while (0) + +#define V4L2_DEBUG_REG 0x0004 +#define V4L2_DEBUG_I2C 0x0008 +#define V4L2_DEBUG_QUEUE 0x0010 +#define V4L2_DEBUG_ISOC 0x0020 +#define V4L2_DEBUG_RES_LOCK 0x0040 /* Resource locking */ +#define V4L2_DEBUG_OPEN 0x0080 /* video open/close debug */ + +#define tm6000_err(fmt, arg...) do {\ + printk(KERN_ERR "tm6000 %s :"fmt, \ + __func__ , ##arg); } while (0) diff --git a/drivers/staging/media/deprecated/vpfe_capture/Kconfig b/drivers/staging/media/deprecated/vpfe_capture/Kconfig new file mode 100644 index 000000000000..10250e7e566b --- /dev/null +++ b/drivers/staging/media/deprecated/vpfe_capture/Kconfig @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_DM6446_CCDC + tristate "TI DM6446 CCDC video capture driver" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV + depends on ARCH_DAVINCI || COMPILE_TEST + depends on I2C + select VIDEOBUF_DMA_CONTIG + help + Enables DaVinci CCD hw module. DaVinci CCDC hw interfaces + with decoder modules such as TVP5146 over BT656 or + sensor module such as MT9T001 over a raw interface. This + module configures the interface and CCDC/ISIF to do + video frame capture from slave decoders. + + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + + To compile this driver as a module, choose M here. There will + be two modules called vpfe_capture.ko and dm644x_ccdc.ko + +config VIDEO_DM355_CCDC + tristate "TI DM355 CCDC video capture driver" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV + depends on ARCH_DAVINCI || COMPILE_TEST + depends on I2C + select VIDEOBUF_DMA_CONTIG + help + Enables DM355 CCD hw module. DM355 CCDC hw interfaces + with decoder modules such as TVP5146 over BT656 or + sensor module such as MT9T001 over a raw interface. This + module configures the interface and CCDC/ISIF to do + video frame capture from a slave decoders + + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + + To compile this driver as a module, choose M here. There will + be two modules called vpfe_capture.ko and dm355_ccdc.ko + +config VIDEO_DM365_ISIF + tristate "TI DM365 ISIF video capture driver" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV + depends on ARCH_DAVINCI || COMPILE_TEST + depends on I2C + select VIDEOBUF_DMA_CONTIG + help + Enables ISIF hw module. This is the hardware module for + configuring ISIF in VPFE to capture Raw Bayer RGB data from + a image sensor or YUV data from a YUV source. + + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + + To compile this driver as a module, choose M here. There will + be two modules called vpfe_capture.ko and isif.ko diff --git a/drivers/staging/media/deprecated/vpfe_capture/Makefile b/drivers/staging/media/deprecated/vpfe_capture/Makefile new file mode 100644 index 000000000000..609e8dc09ce7 --- /dev/null +++ b/drivers/staging/media/deprecated/vpfe_capture/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_VIDEO_DM6446_CCDC) += vpfe_capture.o dm644x_ccdc.o +obj-$(CONFIG_VIDEO_DM355_CCDC) += vpfe_capture.o dm355_ccdc.o +obj-$(CONFIG_VIDEO_DM365_ISIF) += vpfe_capture.o isif.o diff --git a/drivers/staging/media/deprecated/vpfe_capture/TODO b/drivers/staging/media/deprecated/vpfe_capture/TODO new file mode 100644 index 000000000000..ce654d7337af --- /dev/null +++ b/drivers/staging/media/deprecated/vpfe_capture/TODO @@ -0,0 +1,7 @@ +These are one of the few drivers still not using the vb2 +framework, so these drivers are now deprecated with the intent of +removing them altogether by the beginning of 2023. + +In order to keep these drivers they have to be converted to vb2. +If someone is interested in doing this work, then contact the +linux-media mailinglist (https://linuxtv.org/lists.php). diff --git a/drivers/staging/media/deprecated/vpfe_capture/ccdc_hw_device.h b/drivers/staging/media/deprecated/vpfe_capture/ccdc_hw_device.h new file mode 100644 index 000000000000..a545052a95a9 --- /dev/null +++ b/drivers/staging/media/deprecated/vpfe_capture/ccdc_hw_device.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * ccdc device API + */ +#ifndef _CCDC_HW_DEVICE_H +#define _CCDC_HW_DEVICE_H + +#ifdef __KERNEL__ +#include <linux/videodev2.h> +#include <linux/device.h> +#include <media/davinci/vpfe_types.h> +#include <media/davinci/ccdc_types.h> + +/* + * ccdc hw operations + */ +struct ccdc_hw_ops { + /* Pointer to initialize function to initialize ccdc device */ + int (*open) (struct device *dev); + /* Pointer to deinitialize function */ + int (*close) (struct device *dev); + /* set ccdc base address */ + void (*set_ccdc_base)(void *base, int size); + /* Pointer to function to enable or disable ccdc */ + void (*enable) (int en); + /* reset sbl. only for 6446 */ + void (*reset) (void); + /* enable output to sdram */ + void (*enable_out_to_sdram) (int en); + /* Pointer to function to set hw parameters */ + int (*set_hw_if_params) (struct vpfe_hw_if_param *param); + /* get interface parameters */ + int (*get_hw_if_params) (struct vpfe_hw_if_param *param); + /* Pointer to function to configure ccdc */ + int (*configure) (void); + + /* Pointer to function to set buffer type */ + int (*set_buftype) (enum ccdc_buftype buf_type); + /* Pointer to function to get buffer type */ + enum ccdc_buftype (*get_buftype) (void); + /* Pointer to function to set frame format */ + int (*set_frame_format) (enum ccdc_frmfmt frm_fmt); + /* Pointer to function to get frame format */ + enum ccdc_frmfmt (*get_frame_format) (void); + /* enumerate hw pix formats */ + int (*enum_pix)(u32 *hw_pix, int i); + /* Pointer to function to set buffer type */ + u32 (*get_pixel_format) (void); + /* Pointer to function to get pixel format. */ + int (*set_pixel_format) (u32 pixfmt); + /* Pointer to function to set image window */ + int (*set_image_window) (struct v4l2_rect *win); + /* Pointer to function to set image window */ + void (*get_image_window) (struct v4l2_rect *win); + /* Pointer to function to get line length */ + unsigned int (*get_line_length) (void); + + /* Pointer to function to set frame buffer address */ + void (*setfbaddr) (unsigned long addr); + /* Pointer to function to get field id */ + int (*getfid) (void); +}; + +struct ccdc_hw_device { + /* ccdc device name */ + char name[32]; + /* module owner */ + struct module *owner; + /* hw ops */ + struct ccdc_hw_ops hw_ops; +}; + +/* Used by CCDC module to register & unregister with vpfe capture driver */ +int vpfe_register_ccdc_device(const struct ccdc_hw_device *dev); +void vpfe_unregister_ccdc_device(const struct ccdc_hw_device *dev); + +#endif +#endif diff --git a/drivers/staging/media/deprecated/vpfe_capture/dm355_ccdc.c b/drivers/staging/media/deprecated/vpfe_capture/dm355_ccdc.c new file mode 100644 index 000000000000..da8db53e9498 --- /dev/null +++ b/drivers/staging/media/deprecated/vpfe_capture/dm355_ccdc.c @@ -0,0 +1,934 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2005-2009 Texas Instruments Inc + * + * CCDC hardware module for DM355 + * ------------------------------ + * + * This module is for configuring DM355 CCD controller of VPFE to capture + * Raw yuv or Bayer RGB data from a decoder. CCDC has several modules + * such as Defect Pixel Correction, Color Space Conversion etc to + * pre-process the Bayer RGB data, before writing it to SDRAM. + * + * TODO: 1) Raw bayer parameter settings and bayer capture + * 2) Split module parameter structure to module specific ioctl structs + * 3) add support for lense shading correction + * 4) investigate if enum used for user space type definition + * to be replaced by #defines or integer + */ +#include <linux/platform_device.h> +#include <linux/uaccess.h> +#include <linux/videodev2.h> +#include <linux/err.h> +#include <linux/module.h> + +#include "dm355_ccdc.h" +#include <media/davinci/vpss.h> + +#include "dm355_ccdc_regs.h" +#include "ccdc_hw_device.h" + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("CCDC Driver for DM355"); +MODULE_AUTHOR("Texas Instruments"); + +static struct ccdc_oper_config { + struct device *dev; + /* CCDC interface type */ + enum vpfe_hw_if_type if_type; + /* Raw Bayer configuration */ + struct ccdc_params_raw bayer; + /* YCbCr configuration */ + struct ccdc_params_ycbcr ycbcr; + /* ccdc base address */ + void __iomem *base_addr; +} ccdc_cfg = { + /* Raw configurations */ + .bayer = { + .pix_fmt = CCDC_PIXFMT_RAW, + .frm_fmt = CCDC_FRMFMT_PROGRESSIVE, + .win = CCDC_WIN_VGA, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .gain = { + .r_ye = 256, + .gb_g = 256, + .gr_cy = 256, + .b_mg = 256 + }, + .config_params = { + .datasft = 2, + .mfilt1 = CCDC_NO_MEDIAN_FILTER1, + .mfilt2 = CCDC_NO_MEDIAN_FILTER2, + .alaw = { + .gamma_wd = 2, + }, + .blk_clamp = { + .sample_pixel = 1, + .dc_sub = 25 + }, + .col_pat_field0 = { + .olop = CCDC_GREEN_BLUE, + .olep = CCDC_BLUE, + .elop = CCDC_RED, + .elep = CCDC_GREEN_RED + }, + .col_pat_field1 = { + .olop = CCDC_GREEN_BLUE, + .olep = CCDC_BLUE, + .elop = CCDC_RED, + .elep = CCDC_GREEN_RED + }, + }, + }, + /* YCbCr configuration */ + .ycbcr = { + .win = CCDC_WIN_PAL, + .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT, + .frm_fmt = CCDC_FRMFMT_INTERLACED, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .bt656_enable = 1, + .pix_order = CCDC_PIXORDER_CBYCRY, + .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED + }, +}; + + +/* Raw Bayer formats */ +static u32 ccdc_raw_bayer_pix_formats[] = + {V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16}; + +/* Raw YUV formats */ +static u32 ccdc_raw_yuv_pix_formats[] = + {V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV}; + +/* register access routines */ +static inline u32 regr(u32 offset) +{ + return __raw_readl(ccdc_cfg.base_addr + offset); +} + +static inline void regw(u32 val, u32 offset) +{ + __raw_writel(val, ccdc_cfg.base_addr + offset); +} + +static void ccdc_enable(int en) +{ + unsigned int temp; + temp = regr(SYNCEN); + temp &= (~CCDC_SYNCEN_VDHDEN_MASK); + temp |= (en & CCDC_SYNCEN_VDHDEN_MASK); + regw(temp, SYNCEN); +} + +static void ccdc_enable_output_to_sdram(int en) +{ + unsigned int temp; + temp = regr(SYNCEN); + temp &= (~(CCDC_SYNCEN_WEN_MASK)); + temp |= ((en << CCDC_SYNCEN_WEN_SHIFT) & CCDC_SYNCEN_WEN_MASK); + regw(temp, SYNCEN); +} + +static void ccdc_config_gain_offset(void) +{ + /* configure gain */ + regw(ccdc_cfg.bayer.gain.r_ye, RYEGAIN); + regw(ccdc_cfg.bayer.gain.gr_cy, GRCYGAIN); + regw(ccdc_cfg.bayer.gain.gb_g, GBGGAIN); + regw(ccdc_cfg.bayer.gain.b_mg, BMGGAIN); + /* configure offset */ + regw(ccdc_cfg.bayer.ccdc_offset, OFFSET); +} + +/* + * ccdc_restore_defaults() + * This function restore power on defaults in the ccdc registers + */ +static int ccdc_restore_defaults(void) +{ + int i; + + dev_dbg(ccdc_cfg.dev, "\nstarting ccdc_restore_defaults..."); + /* set all registers to zero */ + for (i = 0; i <= CCDC_REG_LAST; i += 4) + regw(0, i); + + /* now override the values with power on defaults in registers */ + regw(MODESET_DEFAULT, MODESET); + /* no culling support */ + regw(CULH_DEFAULT, CULH); + regw(CULV_DEFAULT, CULV); + /* Set default Gain and Offset */ + ccdc_cfg.bayer.gain.r_ye = GAIN_DEFAULT; + ccdc_cfg.bayer.gain.gb_g = GAIN_DEFAULT; + ccdc_cfg.bayer.gain.gr_cy = GAIN_DEFAULT; + ccdc_cfg.bayer.gain.b_mg = GAIN_DEFAULT; + ccdc_config_gain_offset(); + regw(OUTCLIP_DEFAULT, OUTCLIP); + regw(LSCCFG2_DEFAULT, LSCCFG2); + /* select ccdc input */ + if (vpss_select_ccdc_source(VPSS_CCDCIN)) { + dev_dbg(ccdc_cfg.dev, "\ncouldn't select ccdc input source"); + return -EFAULT; + } + /* select ccdc clock */ + if (vpss_enable_clock(VPSS_CCDC_CLOCK, 1) < 0) { + dev_dbg(ccdc_cfg.dev, "\ncouldn't enable ccdc clock"); + return -EFAULT; + } + dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_restore_defaults..."); + return 0; +} + +static int ccdc_open(struct device *device) +{ + return ccdc_restore_defaults(); +} + +static int ccdc_close(struct device *device) +{ + /* disable clock */ + vpss_enable_clock(VPSS_CCDC_CLOCK, 0); + /* do nothing for now */ + return 0; +} +/* + * ccdc_setwin() + * This function will configure the window size to + * be capture in CCDC reg. + */ +static void ccdc_setwin(struct v4l2_rect *image_win, + enum ccdc_frmfmt frm_fmt, int ppc) +{ + int horz_start, horz_nr_pixels; + int vert_start, vert_nr_lines; + int mid_img = 0; + + dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_setwin..."); + + /* + * ppc - per pixel count. indicates how many pixels per cell + * output to SDRAM. example, for ycbcr, it is one y and one c, so 2. + * raw capture this is 1 + */ + horz_start = image_win->left << (ppc - 1); + horz_nr_pixels = ((image_win->width) << (ppc - 1)) - 1; + + /* Writing the horizontal info into the registers */ + regw(horz_start, SPH); + regw(horz_nr_pixels, NPH); + vert_start = image_win->top; + + if (frm_fmt == CCDC_FRMFMT_INTERLACED) { + vert_nr_lines = (image_win->height >> 1) - 1; + vert_start >>= 1; + /* Since first line doesn't have any data */ + vert_start += 1; + /* configure VDINT0 and VDINT1 */ + regw(vert_start, VDINT0); + } else { + /* Since first line doesn't have any data */ + vert_start += 1; + vert_nr_lines = image_win->height - 1; + /* configure VDINT0 and VDINT1 */ + mid_img = vert_start + (image_win->height / 2); + regw(vert_start, VDINT0); + regw(mid_img, VDINT1); + } + regw(vert_start & CCDC_START_VER_ONE_MASK, SLV0); + regw(vert_start & CCDC_START_VER_TWO_MASK, SLV1); + regw(vert_nr_lines & CCDC_NUM_LINES_VER, NLV); + dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_setwin..."); +} + +/* This function will configure CCDC for YCbCr video capture */ +static void ccdc_config_ycbcr(void) +{ + struct ccdc_params_ycbcr *params = &ccdc_cfg.ycbcr; + u32 temp; + + /* first set the CCDC power on defaults values in all registers */ + dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_ycbcr..."); + ccdc_restore_defaults(); + + /* configure pixel format & video frame format */ + temp = (((params->pix_fmt & CCDC_INPUT_MODE_MASK) << + CCDC_INPUT_MODE_SHIFT) | + ((params->frm_fmt & CCDC_FRM_FMT_MASK) << + CCDC_FRM_FMT_SHIFT)); + + /* setup BT.656 sync mode */ + if (params->bt656_enable) { + regw(CCDC_REC656IF_BT656_EN, REC656IF); + /* + * configure the FID, VD, HD pin polarity fld,hd pol positive, + * vd negative, 8-bit pack mode + */ + temp |= CCDC_VD_POL_NEGATIVE; + } else { /* y/c external sync mode */ + temp |= (((params->fid_pol & CCDC_FID_POL_MASK) << + CCDC_FID_POL_SHIFT) | + ((params->hd_pol & CCDC_HD_POL_MASK) << + CCDC_HD_POL_SHIFT) | + ((params->vd_pol & CCDC_VD_POL_MASK) << + CCDC_VD_POL_SHIFT)); + } + + /* pack the data to 8-bit */ + temp |= CCDC_DATA_PACK_ENABLE; + + regw(temp, MODESET); + + /* configure video window */ + ccdc_setwin(¶ms->win, params->frm_fmt, 2); + + /* configure the order of y cb cr in SD-RAM */ + temp = (params->pix_order << CCDC_Y8POS_SHIFT); + temp |= CCDC_LATCH_ON_VSYNC_DISABLE | CCDC_CCDCFG_FIDMD_NO_LATCH_VSYNC; + regw(temp, CCDCFG); + + /* + * configure the horizontal line offset. This is done by rounding up + * width to a multiple of 16 pixels and multiply by two to account for + * y:cb:cr 4:2:2 data + */ + regw(((params->win.width * 2 + 31) >> 5), HSIZE); + + /* configure the memory line offset */ + if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) { + /* two fields are interleaved in memory */ + regw(CCDC_SDOFST_FIELD_INTERLEAVED, SDOFST); + } + + dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_config_ycbcr...\n"); +} + +/* + * ccdc_config_black_clamp() + * configure parameters for Optical Black Clamp + */ +static void ccdc_config_black_clamp(struct ccdc_black_clamp *bclamp) +{ + u32 val; + + if (!bclamp->b_clamp_enable) { + /* configure DCSub */ + regw(bclamp->dc_sub & CCDC_BLK_DC_SUB_MASK, DCSUB); + regw(0x0000, CLAMP); + return; + } + /* Enable the Black clamping, set sample lines and pixels */ + val = (bclamp->start_pixel & CCDC_BLK_ST_PXL_MASK) | + ((bclamp->sample_pixel & CCDC_BLK_SAMPLE_LN_MASK) << + CCDC_BLK_SAMPLE_LN_SHIFT) | CCDC_BLK_CLAMP_ENABLE; + regw(val, CLAMP); + + /* If Black clamping is enable then make dcsub 0 */ + val = (bclamp->sample_ln & CCDC_NUM_LINE_CALC_MASK) + << CCDC_NUM_LINE_CALC_SHIFT; + regw(val, DCSUB); +} + +/* + * ccdc_config_black_compense() + * configure parameters for Black Compensation + */ +static void ccdc_config_black_compense(struct ccdc_black_compensation *bcomp) +{ + u32 val; + + val = (bcomp->b & CCDC_BLK_COMP_MASK) | + ((bcomp->gb & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_GB_COMP_SHIFT); + regw(val, BLKCMP1); + + val = ((bcomp->gr & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_GR_COMP_SHIFT) | + ((bcomp->r & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_R_COMP_SHIFT); + regw(val, BLKCMP0); +} + +/* + * ccdc_write_dfc_entry() + * write an entry in the dfc table. + */ +static int ccdc_write_dfc_entry(int index, struct ccdc_vertical_dft *dfc) +{ +/* TODO This is to be re-visited and adjusted */ +#define DFC_WRITE_WAIT_COUNT 1000 + u32 val, count = DFC_WRITE_WAIT_COUNT; + + regw(dfc->dft_corr_vert[index], DFCMEM0); + regw(dfc->dft_corr_horz[index], DFCMEM1); + regw(dfc->dft_corr_sub1[index], DFCMEM2); + regw(dfc->dft_corr_sub2[index], DFCMEM3); + regw(dfc->dft_corr_sub3[index], DFCMEM4); + /* set WR bit to write */ + val = regr(DFCMEMCTL) | CCDC_DFCMEMCTL_DFCMWR_MASK; + regw(val, DFCMEMCTL); + + /* + * Assume, it is very short. If we get an error, we need to + * adjust this value + */ + while (regr(DFCMEMCTL) & CCDC_DFCMEMCTL_DFCMWR_MASK) + count--; + /* + * TODO We expect the count to be non-zero to be successful. Adjust + * the count if write requires more time + */ + + if (count) { + dev_err(ccdc_cfg.dev, "defect table write timeout !!!\n"); + return -1; + } + return 0; +} + +/* + * ccdc_config_vdfc() + * configure parameters for Vertical Defect Correction + */ +static int ccdc_config_vdfc(struct ccdc_vertical_dft *dfc) +{ + u32 val; + int i; + + /* Configure General Defect Correction. The table used is from IPIPE */ + val = dfc->gen_dft_en & CCDC_DFCCTL_GDFCEN_MASK; + + /* Configure Vertical Defect Correction if needed */ + if (!dfc->ver_dft_en) { + /* Enable only General Defect Correction */ + regw(val, DFCCTL); + return 0; + } + + if (dfc->table_size > CCDC_DFT_TABLE_SIZE) + return -EINVAL; + + val |= CCDC_DFCCTL_VDFC_DISABLE; + val |= (dfc->dft_corr_ctl.vdfcsl & CCDC_DFCCTL_VDFCSL_MASK) << + CCDC_DFCCTL_VDFCSL_SHIFT; + val |= (dfc->dft_corr_ctl.vdfcuda & CCDC_DFCCTL_VDFCUDA_MASK) << + CCDC_DFCCTL_VDFCUDA_SHIFT; + val |= (dfc->dft_corr_ctl.vdflsft & CCDC_DFCCTL_VDFLSFT_MASK) << + CCDC_DFCCTL_VDFLSFT_SHIFT; + regw(val , DFCCTL); + + /* clear address ptr to offset 0 */ + val = CCDC_DFCMEMCTL_DFCMARST_MASK << CCDC_DFCMEMCTL_DFCMARST_SHIFT; + + /* write defect table entries */ + for (i = 0; i < dfc->table_size; i++) { + /* increment address for non zero index */ + if (i != 0) + val = CCDC_DFCMEMCTL_INC_ADDR; + regw(val, DFCMEMCTL); + if (ccdc_write_dfc_entry(i, dfc) < 0) + return -EFAULT; + } + + /* update saturation level and enable dfc */ + regw(dfc->saturation_ctl & CCDC_VDC_DFCVSAT_MASK, DFCVSAT); + val = regr(DFCCTL) | (CCDC_DFCCTL_VDFCEN_MASK << + CCDC_DFCCTL_VDFCEN_SHIFT); + regw(val, DFCCTL); + return 0; +} + +/* + * ccdc_config_csc() + * configure parameters for color space conversion + * Each register CSCM0-7 has two values in S8Q5 format. + */ +static void ccdc_config_csc(struct ccdc_csc *csc) +{ + u32 val1 = 0, val2; + int i; + + if (!csc->enable) + return; + + /* Enable the CSC sub-module */ + regw(CCDC_CSC_ENABLE, CSCCTL); + + /* Converting the co-eff as per the format of the register */ + for (i = 0; i < CCDC_CSC_COEFF_TABLE_SIZE; i++) { + if ((i % 2) == 0) { + /* CSCM - LSB */ + val1 = (csc->coeff[i].integer & + CCDC_CSC_COEF_INTEG_MASK) + << CCDC_CSC_COEF_INTEG_SHIFT; + /* + * convert decimal part to binary. Use 2 decimal + * precision, user values range from .00 - 0.99 + */ + val1 |= (((csc->coeff[i].decimal & + CCDC_CSC_COEF_DECIMAL_MASK) * + CCDC_CSC_DEC_MAX) / 100); + } else { + + /* CSCM - MSB */ + val2 = (csc->coeff[i].integer & + CCDC_CSC_COEF_INTEG_MASK) + << CCDC_CSC_COEF_INTEG_SHIFT; + val2 |= (((csc->coeff[i].decimal & + CCDC_CSC_COEF_DECIMAL_MASK) * + CCDC_CSC_DEC_MAX) / 100); + val2 <<= CCDC_CSCM_MSB_SHIFT; + val2 |= val1; + regw(val2, (CSCM0 + ((i - 1) << 1))); + } + } +} + +/* + * ccdc_config_color_patterns() + * configure parameters for color patterns + */ +static void ccdc_config_color_patterns(struct ccdc_col_pat *pat0, + struct ccdc_col_pat *pat1) +{ + u32 val; + + val = (pat0->olop | (pat0->olep << 2) | (pat0->elop << 4) | + (pat0->elep << 6) | (pat1->olop << 8) | (pat1->olep << 10) | + (pat1->elop << 12) | (pat1->elep << 14)); + regw(val, COLPTN); +} + +/* This function will configure CCDC for Raw mode image capture */ +static int ccdc_config_raw(void) +{ + struct ccdc_params_raw *params = &ccdc_cfg.bayer; + struct ccdc_config_params_raw *config_params = + &ccdc_cfg.bayer.config_params; + unsigned int val; + + dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_raw..."); + + /* restore power on defaults to register */ + ccdc_restore_defaults(); + + /* CCDCFG register: + * set CCD Not to swap input since input is RAW data + * set FID detection function to Latch at V-Sync + * set WENLOG - ccdc valid area to AND + * set TRGSEL to WENBIT + * set EXTRG to DISABLE + * disable latching function on VSYNC - shadowed registers + */ + regw(CCDC_YCINSWP_RAW | CCDC_CCDCFG_FIDMD_LATCH_VSYNC | + CCDC_CCDCFG_WENLOG_AND | CCDC_CCDCFG_TRGSEL_WEN | + CCDC_CCDCFG_EXTRG_DISABLE | CCDC_LATCH_ON_VSYNC_DISABLE, CCDCFG); + + /* + * Set VDHD direction to input, input type to raw input + * normal data polarity, do not use external WEN + */ + val = (CCDC_VDHDOUT_INPUT | CCDC_RAW_IP_MODE | CCDC_DATAPOL_NORMAL | + CCDC_EXWEN_DISABLE); + + /* + * Configure the vertical sync polarity (MODESET.VDPOL), horizontal + * sync polarity (MODESET.HDPOL), field id polarity (MODESET.FLDPOL), + * frame format(progressive or interlace), & pixel format (Input mode) + */ + val |= (((params->vd_pol & CCDC_VD_POL_MASK) << CCDC_VD_POL_SHIFT) | + ((params->hd_pol & CCDC_HD_POL_MASK) << CCDC_HD_POL_SHIFT) | + ((params->fid_pol & CCDC_FID_POL_MASK) << CCDC_FID_POL_SHIFT) | + ((params->frm_fmt & CCDC_FRM_FMT_MASK) << CCDC_FRM_FMT_SHIFT) | + ((params->pix_fmt & CCDC_PIX_FMT_MASK) << CCDC_PIX_FMT_SHIFT)); + + /* set pack for alaw compression */ + if ((config_params->data_sz == CCDC_DATA_8BITS) || + config_params->alaw.enable) + val |= CCDC_DATA_PACK_ENABLE; + + /* Configure for LPF */ + if (config_params->lpf_enable) + val |= (config_params->lpf_enable & CCDC_LPF_MASK) << + CCDC_LPF_SHIFT; + + /* Configure the data shift */ + val |= (config_params->datasft & CCDC_DATASFT_MASK) << + CCDC_DATASFT_SHIFT; + regw(val , MODESET); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to MODESET...\n", val); + + /* Configure the Median Filter threshold */ + regw((config_params->med_filt_thres) & CCDC_MED_FILT_THRESH, MEDFILT); + + /* Configure GAMMAWD register. defaur 11-2, and Mosaic cfa pattern */ + val = CCDC_GAMMA_BITS_11_2 << CCDC_GAMMAWD_INPUT_SHIFT | + CCDC_CFA_MOSAIC; + + /* Enable and configure aLaw register if needed */ + if (config_params->alaw.enable) { + val |= (CCDC_ALAW_ENABLE | + ((config_params->alaw.gamma_wd & + CCDC_ALAW_GAMMA_WD_MASK) << + CCDC_GAMMAWD_INPUT_SHIFT)); + } + + /* Configure Median filter1 & filter2 */ + val |= ((config_params->mfilt1 << CCDC_MFILT1_SHIFT) | + (config_params->mfilt2 << CCDC_MFILT2_SHIFT)); + + regw(val, GAMMAWD); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to GAMMAWD...\n", val); + + /* configure video window */ + ccdc_setwin(¶ms->win, params->frm_fmt, 1); + + /* Optical Clamp Averaging */ + ccdc_config_black_clamp(&config_params->blk_clamp); + + /* Black level compensation */ + ccdc_config_black_compense(&config_params->blk_comp); + + /* Vertical Defect Correction if needed */ + if (ccdc_config_vdfc(&config_params->vertical_dft) < 0) + return -EFAULT; + + /* color space conversion */ + ccdc_config_csc(&config_params->csc); + + /* color pattern */ + ccdc_config_color_patterns(&config_params->col_pat_field0, + &config_params->col_pat_field1); + + /* Configure the Gain & offset control */ + ccdc_config_gain_offset(); + + dev_dbg(ccdc_cfg.dev, "\nWriting %x to COLPTN...\n", val); + + /* Configure DATAOFST register */ + val = (config_params->data_offset.horz_offset & CCDC_DATAOFST_MASK) << + CCDC_DATAOFST_H_SHIFT; + val |= (config_params->data_offset.vert_offset & CCDC_DATAOFST_MASK) << + CCDC_DATAOFST_V_SHIFT; + regw(val, DATAOFST); + + /* configuring HSIZE register */ + val = (params->horz_flip_enable & CCDC_HSIZE_FLIP_MASK) << + CCDC_HSIZE_FLIP_SHIFT; + + /* If pack 8 is enable then 1 pixel will take 1 byte */ + if ((config_params->data_sz == CCDC_DATA_8BITS) || + config_params->alaw.enable) { + val |= (((params->win.width) + 31) >> 5) & + CCDC_HSIZE_VAL_MASK; + + /* adjust to multiple of 32 */ + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to HSIZE...\n", + (((params->win.width) + 31) >> 5) & + CCDC_HSIZE_VAL_MASK); + } else { + /* else one pixel will take 2 byte */ + val |= (((params->win.width * 2) + 31) >> 5) & + CCDC_HSIZE_VAL_MASK; + + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to HSIZE...\n", + (((params->win.width * 2) + 31) >> 5) & + CCDC_HSIZE_VAL_MASK); + } + regw(val, HSIZE); + + /* Configure SDOFST register */ + if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) { + if (params->image_invert_enable) { + /* For interlace inverse mode */ + regw(CCDC_SDOFST_INTERLACE_INVERSE, SDOFST); + dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n", + CCDC_SDOFST_INTERLACE_INVERSE); + } else { + /* For interlace non inverse mode */ + regw(CCDC_SDOFST_INTERLACE_NORMAL, SDOFST); + dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n", + CCDC_SDOFST_INTERLACE_NORMAL); + } + } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) { + if (params->image_invert_enable) { + /* For progessive inverse mode */ + regw(CCDC_SDOFST_PROGRESSIVE_INVERSE, SDOFST); + dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n", + CCDC_SDOFST_PROGRESSIVE_INVERSE); + } else { + /* For progessive non inverse mode */ + regw(CCDC_SDOFST_PROGRESSIVE_NORMAL, SDOFST); + dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n", + CCDC_SDOFST_PROGRESSIVE_NORMAL); + } + } + dev_dbg(ccdc_cfg.dev, "\nend of ccdc_config_raw..."); + return 0; +} + +static int ccdc_configure(void) +{ + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + return ccdc_config_raw(); + else + ccdc_config_ycbcr(); + return 0; +} + +static int ccdc_set_buftype(enum ccdc_buftype buf_type) +{ + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + ccdc_cfg.bayer.buf_type = buf_type; + else + ccdc_cfg.ycbcr.buf_type = buf_type; + return 0; +} +static enum ccdc_buftype ccdc_get_buftype(void) +{ + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + return ccdc_cfg.bayer.buf_type; + return ccdc_cfg.ycbcr.buf_type; +} + +static int ccdc_enum_pix(u32 *pix, int i) +{ + int ret = -EINVAL; + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) { + if (i < ARRAY_SIZE(ccdc_raw_bayer_pix_formats)) { + *pix = ccdc_raw_bayer_pix_formats[i]; + ret = 0; + } + } else { + if (i < ARRAY_SIZE(ccdc_raw_yuv_pix_formats)) { + *pix = ccdc_raw_yuv_pix_formats[i]; + ret = 0; + } + } + return ret; +} + +static int ccdc_set_pixel_format(u32 pixfmt) +{ + struct ccdc_a_law *alaw = &ccdc_cfg.bayer.config_params.alaw; + + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) { + ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; + if (pixfmt == V4L2_PIX_FMT_SBGGR8) + alaw->enable = 1; + else if (pixfmt != V4L2_PIX_FMT_SBGGR16) + return -EINVAL; + } else { + if (pixfmt == V4L2_PIX_FMT_YUYV) + ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR; + else if (pixfmt == V4L2_PIX_FMT_UYVY) + ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; + else + return -EINVAL; + } + return 0; +} +static u32 ccdc_get_pixel_format(void) +{ + struct ccdc_a_law *alaw = &ccdc_cfg.bayer.config_params.alaw; + u32 pixfmt; + + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + if (alaw->enable) + pixfmt = V4L2_PIX_FMT_SBGGR8; + else + pixfmt = V4L2_PIX_FMT_SBGGR16; + else { + if (ccdc_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR) + pixfmt = V4L2_PIX_FMT_YUYV; + else + pixfmt = V4L2_PIX_FMT_UYVY; + } + return pixfmt; +} +static int ccdc_set_image_window(struct v4l2_rect *win) +{ + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + ccdc_cfg.bayer.win = *win; + else + ccdc_cfg.ycbcr.win = *win; + return 0; +} + +static void ccdc_get_image_window(struct v4l2_rect *win) +{ + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + *win = ccdc_cfg.bayer.win; + else + *win = ccdc_cfg.ycbcr.win; +} + +static unsigned int ccdc_get_line_length(void) +{ + struct ccdc_config_params_raw *config_params = + &ccdc_cfg.bayer.config_params; + unsigned int len; + + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) { + if ((config_params->alaw.enable) || + (config_params->data_sz == CCDC_DATA_8BITS)) + len = ccdc_cfg.bayer.win.width; + else + len = ccdc_cfg.bayer.win.width * 2; + } else + len = ccdc_cfg.ycbcr.win.width * 2; + return ALIGN(len, 32); +} + +static int ccdc_set_frame_format(enum ccdc_frmfmt frm_fmt) +{ + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + ccdc_cfg.bayer.frm_fmt = frm_fmt; + else + ccdc_cfg.ycbcr.frm_fmt = frm_fmt; + return 0; +} + +static enum ccdc_frmfmt ccdc_get_frame_format(void) +{ + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + return ccdc_cfg.bayer.frm_fmt; + else + return ccdc_cfg.ycbcr.frm_fmt; +} + +static int ccdc_getfid(void) +{ + return (regr(MODESET) >> 15) & 1; +} + +/* misc operations */ +static inline void ccdc_setfbaddr(unsigned long addr) +{ + regw((addr >> 21) & 0x007f, STADRH); + regw((addr >> 5) & 0x0ffff, STADRL); +} + +static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params) +{ + ccdc_cfg.if_type = params->if_type; + + switch (params->if_type) { + case VPFE_BT656: + case VPFE_YCBCR_SYNC_16: + case VPFE_YCBCR_SYNC_8: + ccdc_cfg.ycbcr.vd_pol = params->vdpol; + ccdc_cfg.ycbcr.hd_pol = params->hdpol; + break; + default: + /* TODO add support for raw bayer here */ + return -EINVAL; + } + return 0; +} + +static const struct ccdc_hw_device ccdc_hw_dev = { + .name = "DM355 CCDC", + .owner = THIS_MODULE, + .hw_ops = { + .open = ccdc_open, + .close = ccdc_close, + .enable = ccdc_enable, + .enable_out_to_sdram = ccdc_enable_output_to_sdram, + .set_hw_if_params = ccdc_set_hw_if_params, + .configure = ccdc_configure, + .set_buftype = ccdc_set_buftype, + .get_buftype = ccdc_get_buftype, + .enum_pix = ccdc_enum_pix, + .set_pixel_format = ccdc_set_pixel_format, + .get_pixel_format = ccdc_get_pixel_format, + .set_frame_format = ccdc_set_frame_format, + .get_frame_format = ccdc_get_frame_format, + .set_image_window = ccdc_set_image_window, + .get_image_window = ccdc_get_image_window, + .get_line_length = ccdc_get_line_length, + .setfbaddr = ccdc_setfbaddr, + .getfid = ccdc_getfid, + }, +}; + +static int dm355_ccdc_probe(struct platform_device *pdev) +{ + void (*setup_pinmux)(void); + struct resource *res; + int status = 0; + + /* + * first try to register with vpfe. If not correct platform, then we + * don't have to iomap + */ + status = vpfe_register_ccdc_device(&ccdc_hw_dev); + if (status < 0) + return status; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + status = -ENODEV; + goto fail_nores; + } + + res = request_mem_region(res->start, resource_size(res), res->name); + if (!res) { + status = -EBUSY; + goto fail_nores; + } + + ccdc_cfg.base_addr = ioremap(res->start, resource_size(res)); + if (!ccdc_cfg.base_addr) { + status = -ENOMEM; + goto fail_nomem; + } + + /* Platform data holds setup_pinmux function ptr */ + if (NULL == pdev->dev.platform_data) { + status = -ENODEV; + goto fail_nomap; + } + setup_pinmux = pdev->dev.platform_data; + /* + * setup Mux configuration for ccdc which may be different for + * different SoCs using this CCDC + */ + setup_pinmux(); + ccdc_cfg.dev = &pdev->dev; + printk(KERN_NOTICE "%s is registered with vpfe.\n", ccdc_hw_dev.name); + return 0; +fail_nomap: + iounmap(ccdc_cfg.base_addr); +fail_nomem: + release_mem_region(res->start, resource_size(res)); +fail_nores: + vpfe_unregister_ccdc_device(&ccdc_hw_dev); + return status; +} + +static int dm355_ccdc_remove(struct platform_device *pdev) +{ + struct resource *res; + + iounmap(ccdc_cfg.base_addr); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + vpfe_unregister_ccdc_device(&ccdc_hw_dev); + return 0; +} + +static struct platform_driver dm355_ccdc_driver = { + .driver = { + .name = "dm355_ccdc", + }, + .remove = dm355_ccdc_remove, + .probe = dm355_ccdc_probe, +}; + +module_platform_driver(dm355_ccdc_driver); diff --git a/drivers/staging/media/deprecated/vpfe_capture/dm355_ccdc.h b/drivers/staging/media/deprecated/vpfe_capture/dm355_ccdc.h new file mode 100644 index 000000000000..1f3d00aa46d1 --- /dev/null +++ b/drivers/staging/media/deprecated/vpfe_capture/dm355_ccdc.h @@ -0,0 +1,308 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2005-2009 Texas Instruments Inc + */ +#ifndef _DM355_CCDC_H +#define _DM355_CCDC_H +#include <media/davinci/ccdc_types.h> +#include <media/davinci/vpfe_types.h> + +/* enum for No of pixel per line to be avg. in Black Clamping */ +enum ccdc_sample_length { + CCDC_SAMPLE_1PIXELS, + CCDC_SAMPLE_2PIXELS, + CCDC_SAMPLE_4PIXELS, + CCDC_SAMPLE_8PIXELS, + CCDC_SAMPLE_16PIXELS +}; + +/* enum for No of lines in Black Clamping */ +enum ccdc_sample_line { + CCDC_SAMPLE_1LINES, + CCDC_SAMPLE_2LINES, + CCDC_SAMPLE_4LINES, + CCDC_SAMPLE_8LINES, + CCDC_SAMPLE_16LINES +}; + +/* enum for Alaw gamma width */ +enum ccdc_gamma_width { + CCDC_GAMMA_BITS_13_4, + CCDC_GAMMA_BITS_12_3, + CCDC_GAMMA_BITS_11_2, + CCDC_GAMMA_BITS_10_1, + CCDC_GAMMA_BITS_09_0 +}; + +enum ccdc_colpats { + CCDC_RED, + CCDC_GREEN_RED, + CCDC_GREEN_BLUE, + CCDC_BLUE +}; + +struct ccdc_col_pat { + enum ccdc_colpats olop; + enum ccdc_colpats olep; + enum ccdc_colpats elop; + enum ccdc_colpats elep; +}; + +enum ccdc_datasft { + CCDC_DATA_NO_SHIFT, + CCDC_DATA_SHIFT_1BIT, + CCDC_DATA_SHIFT_2BIT, + CCDC_DATA_SHIFT_3BIT, + CCDC_DATA_SHIFT_4BIT, + CCDC_DATA_SHIFT_5BIT, + CCDC_DATA_SHIFT_6BIT +}; + +enum ccdc_data_size { + CCDC_DATA_16BITS, + CCDC_DATA_15BITS, + CCDC_DATA_14BITS, + CCDC_DATA_13BITS, + CCDC_DATA_12BITS, + CCDC_DATA_11BITS, + CCDC_DATA_10BITS, + CCDC_DATA_8BITS +}; +enum ccdc_mfilt1 { + CCDC_NO_MEDIAN_FILTER1, + CCDC_AVERAGE_FILTER1, + CCDC_MEDIAN_FILTER1 +}; + +enum ccdc_mfilt2 { + CCDC_NO_MEDIAN_FILTER2, + CCDC_AVERAGE_FILTER2, + CCDC_MEDIAN_FILTER2 +}; + +/* structure for ALaw */ +struct ccdc_a_law { + /* Enable/disable A-Law */ + unsigned char enable; + /* Gamma Width Input */ + enum ccdc_gamma_width gamma_wd; +}; + +/* structure for Black Clamping */ +struct ccdc_black_clamp { + /* only if bClampEnable is TRUE */ + unsigned char b_clamp_enable; + /* only if bClampEnable is TRUE */ + enum ccdc_sample_length sample_pixel; + /* only if bClampEnable is TRUE */ + enum ccdc_sample_line sample_ln; + /* only if bClampEnable is TRUE */ + unsigned short start_pixel; + /* only if bClampEnable is FALSE */ + unsigned short sgain; + unsigned short dc_sub; +}; + +/* structure for Black Level Compensation */ +struct ccdc_black_compensation { + /* Constant value to subtract from Red component */ + unsigned char r; + /* Constant value to subtract from Gr component */ + unsigned char gr; + /* Constant value to subtract from Blue component */ + unsigned char b; + /* Constant value to subtract from Gb component */ + unsigned char gb; +}; + +struct ccdc_float { + int integer; + unsigned int decimal; +}; + +#define CCDC_CSC_COEFF_TABLE_SIZE 16 +/* structure for color space converter */ +struct ccdc_csc { + unsigned char enable; + /* + * S8Q5. Use 2 decimal precision, user values range from -3.00 to 3.99. + * example - to use 1.03, set integer part as 1, and decimal part as 3 + * to use -1.03, set integer part as -1 and decimal part as 3 + */ + struct ccdc_float coeff[CCDC_CSC_COEFF_TABLE_SIZE]; +}; + +/* Structures for Vertical Defect Correction*/ +enum ccdc_vdf_csl { + CCDC_VDF_NORMAL, + CCDC_VDF_HORZ_INTERPOL_SAT, + CCDC_VDF_HORZ_INTERPOL +}; + +enum ccdc_vdf_cuda { + CCDC_VDF_WHOLE_LINE_CORRECT, + CCDC_VDF_UPPER_DISABLE +}; + +enum ccdc_dfc_mwr { + CCDC_DFC_MWR_WRITE_COMPLETE, + CCDC_DFC_WRITE_REG +}; + +enum ccdc_dfc_mrd { + CCDC_DFC_READ_COMPLETE, + CCDC_DFC_READ_REG +}; + +enum ccdc_dfc_ma_rst { + CCDC_DFC_INCR_ADDR, + CCDC_DFC_CLR_ADDR +}; + +enum ccdc_dfc_mclr { + CCDC_DFC_CLEAR_COMPLETE, + CCDC_DFC_CLEAR +}; + +struct ccdc_dft_corr_ctl { + enum ccdc_vdf_csl vdfcsl; + enum ccdc_vdf_cuda vdfcuda; + unsigned int vdflsft; +}; + +struct ccdc_dft_corr_mem_ctl { + enum ccdc_dfc_mwr dfcmwr; + enum ccdc_dfc_mrd dfcmrd; + enum ccdc_dfc_ma_rst dfcmarst; + enum ccdc_dfc_mclr dfcmclr; +}; + +#define CCDC_DFT_TABLE_SIZE 16 +/* + * Main Structure for vertical defect correction. Vertical defect + * correction can correct up to 16 defects if defects less than 16 + * then pad the rest with 0 + */ +struct ccdc_vertical_dft { + unsigned char ver_dft_en; + unsigned char gen_dft_en; + unsigned int saturation_ctl; + struct ccdc_dft_corr_ctl dft_corr_ctl; + struct ccdc_dft_corr_mem_ctl dft_corr_mem_ctl; + int table_size; + unsigned int dft_corr_horz[CCDC_DFT_TABLE_SIZE]; + unsigned int dft_corr_vert[CCDC_DFT_TABLE_SIZE]; + unsigned int dft_corr_sub1[CCDC_DFT_TABLE_SIZE]; + unsigned int dft_corr_sub2[CCDC_DFT_TABLE_SIZE]; + unsigned int dft_corr_sub3[CCDC_DFT_TABLE_SIZE]; +}; + +struct ccdc_data_offset { + unsigned char horz_offset; + unsigned char vert_offset; +}; + +/* + * Structure for CCDC configuration parameters for raw capture mode passed + * by application + */ +struct ccdc_config_params_raw { + /* data shift to be applied before storing */ + enum ccdc_datasft datasft; + /* data size value from 8 to 16 bits */ + enum ccdc_data_size data_sz; + /* median filter for sdram */ + enum ccdc_mfilt1 mfilt1; + enum ccdc_mfilt2 mfilt2; + /* low pass filter enable/disable */ + unsigned char lpf_enable; + /* Threshold of median filter */ + int med_filt_thres; + /* + * horz and vertical data offset. Applicable for defect correction + * and lsc + */ + struct ccdc_data_offset data_offset; + /* Structure for Optional A-Law */ + struct ccdc_a_law alaw; + /* Structure for Optical Black Clamp */ + struct ccdc_black_clamp blk_clamp; + /* Structure for Black Compensation */ + struct ccdc_black_compensation blk_comp; + /* structure for vertical Defect Correction Module Configuration */ + struct ccdc_vertical_dft vertical_dft; + /* structure for color space converter Module Configuration */ + struct ccdc_csc csc; + /* color patters for bayer capture */ + struct ccdc_col_pat col_pat_field0; + struct ccdc_col_pat col_pat_field1; +}; + +#ifdef __KERNEL__ +#include <linux/io.h> + +#define CCDC_WIN_PAL {0, 0, 720, 576} +#define CCDC_WIN_VGA {0, 0, 640, 480} + +struct ccdc_params_ycbcr { + /* pixel format */ + enum ccdc_pixfmt pix_fmt; + /* progressive or interlaced frame */ + enum ccdc_frmfmt frm_fmt; + /* video window */ + struct v4l2_rect win; + /* field id polarity */ + enum vpfe_pin_pol fid_pol; + /* vertical sync polarity */ + enum vpfe_pin_pol vd_pol; + /* horizontal sync polarity */ + enum vpfe_pin_pol hd_pol; + /* enable BT.656 embedded sync mode */ + int bt656_enable; + /* cb:y:cr:y or y:cb:y:cr in memory */ + enum ccdc_pixorder pix_order; + /* interleaved or separated fields */ + enum ccdc_buftype buf_type; +}; + +/* Gain applied to Raw Bayer data */ +struct ccdc_gain { + unsigned short r_ye; + unsigned short gr_cy; + unsigned short gb_g; + unsigned short b_mg; +}; + +/* Structure for CCDC configuration parameters for raw capture mode */ +struct ccdc_params_raw { + /* pixel format */ + enum ccdc_pixfmt pix_fmt; + /* progressive or interlaced frame */ + enum ccdc_frmfmt frm_fmt; + /* video window */ + struct v4l2_rect win; + /* field id polarity */ + enum vpfe_pin_pol fid_pol; + /* vertical sync polarity */ + enum vpfe_pin_pol vd_pol; + /* horizontal sync polarity */ + enum vpfe_pin_pol hd_pol; + /* interleaved or separated fields */ + enum ccdc_buftype buf_type; + /* Gain values */ + struct ccdc_gain gain; + /* offset */ + unsigned int ccdc_offset; + /* horizontal flip enable */ + unsigned char horz_flip_enable; + /* + * enable to store the image in inverse order in memory + * (bottom to top) + */ + unsigned char image_invert_enable; + /* Configurable part of raw data */ + struct ccdc_config_params_raw config_params; +}; + +#endif +#endif /* DM355_CCDC_H */ diff --git a/drivers/staging/media/deprecated/vpfe_capture/dm355_ccdc_regs.h b/drivers/staging/media/deprecated/vpfe_capture/dm355_ccdc_regs.h new file mode 100644 index 000000000000..eb381f075245 --- /dev/null +++ b/drivers/staging/media/deprecated/vpfe_capture/dm355_ccdc_regs.h @@ -0,0 +1,297 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2005-2009 Texas Instruments Inc + */ +#ifndef _DM355_CCDC_REGS_H +#define _DM355_CCDC_REGS_H + +/**************************************************************************\ +* Register OFFSET Definitions +\**************************************************************************/ +#define SYNCEN 0x00 +#define MODESET 0x04 +#define HDWIDTH 0x08 +#define VDWIDTH 0x0c +#define PPLN 0x10 +#define LPFR 0x14 +#define SPH 0x18 +#define NPH 0x1c +#define SLV0 0x20 +#define SLV1 0x24 +#define NLV 0x28 +#define CULH 0x2c +#define CULV 0x30 +#define HSIZE 0x34 +#define SDOFST 0x38 +#define STADRH 0x3c +#define STADRL 0x40 +#define CLAMP 0x44 +#define DCSUB 0x48 +#define COLPTN 0x4c +#define BLKCMP0 0x50 +#define BLKCMP1 0x54 +#define MEDFILT 0x58 +#define RYEGAIN 0x5c +#define GRCYGAIN 0x60 +#define GBGGAIN 0x64 +#define BMGGAIN 0x68 +#define OFFSET 0x6c +#define OUTCLIP 0x70 +#define VDINT0 0x74 +#define VDINT1 0x78 +#define RSV0 0x7c +#define GAMMAWD 0x80 +#define REC656IF 0x84 +#define CCDCFG 0x88 +#define FMTCFG 0x8c +#define FMTPLEN 0x90 +#define FMTSPH 0x94 +#define FMTLNH 0x98 +#define FMTSLV 0x9c +#define FMTLNV 0xa0 +#define FMTRLEN 0xa4 +#define FMTHCNT 0xa8 +#define FMT_ADDR_PTR_B 0xac +#define FMT_ADDR_PTR(i) (FMT_ADDR_PTR_B + (i * 4)) +#define FMTPGM_VF0 0xcc +#define FMTPGM_VF1 0xd0 +#define FMTPGM_AP0 0xd4 +#define FMTPGM_AP1 0xd8 +#define FMTPGM_AP2 0xdc +#define FMTPGM_AP3 0xe0 +#define FMTPGM_AP4 0xe4 +#define FMTPGM_AP5 0xe8 +#define FMTPGM_AP6 0xec +#define FMTPGM_AP7 0xf0 +#define LSCCFG1 0xf4 +#define LSCCFG2 0xf8 +#define LSCH0 0xfc +#define LSCV0 0x100 +#define LSCKH 0x104 +#define LSCKV 0x108 +#define LSCMEMCTL 0x10c +#define LSCMEMD 0x110 +#define LSCMEMQ 0x114 +#define DFCCTL 0x118 +#define DFCVSAT 0x11c +#define DFCMEMCTL 0x120 +#define DFCMEM0 0x124 +#define DFCMEM1 0x128 +#define DFCMEM2 0x12c +#define DFCMEM3 0x130 +#define DFCMEM4 0x134 +#define CSCCTL 0x138 +#define CSCM0 0x13c +#define CSCM1 0x140 +#define CSCM2 0x144 +#define CSCM3 0x148 +#define CSCM4 0x14c +#define CSCM5 0x150 +#define CSCM6 0x154 +#define CSCM7 0x158 +#define DATAOFST 0x15c +#define CCDC_REG_LAST DATAOFST +/************************************************************** +* Define for various register bit mask and shifts for CCDC +* +**************************************************************/ +#define CCDC_RAW_IP_MODE 0 +#define CCDC_VDHDOUT_INPUT 0 +#define CCDC_YCINSWP_RAW (0 << 4) +#define CCDC_EXWEN_DISABLE 0 +#define CCDC_DATAPOL_NORMAL 0 +#define CCDC_CCDCFG_FIDMD_LATCH_VSYNC 0 +#define CCDC_CCDCFG_FIDMD_NO_LATCH_VSYNC (1 << 6) +#define CCDC_CCDCFG_WENLOG_AND 0 +#define CCDC_CCDCFG_TRGSEL_WEN 0 +#define CCDC_CCDCFG_EXTRG_DISABLE 0 +#define CCDC_CFA_MOSAIC 0 +#define CCDC_Y8POS_SHIFT 11 + +#define CCDC_VDC_DFCVSAT_MASK 0x3fff +#define CCDC_DATAOFST_MASK 0x0ff +#define CCDC_DATAOFST_H_SHIFT 0 +#define CCDC_DATAOFST_V_SHIFT 8 +#define CCDC_GAMMAWD_CFA_MASK 1 +#define CCDC_GAMMAWD_CFA_SHIFT 5 +#define CCDC_GAMMAWD_INPUT_SHIFT 2 +#define CCDC_FID_POL_MASK 1 +#define CCDC_FID_POL_SHIFT 4 +#define CCDC_HD_POL_MASK 1 +#define CCDC_HD_POL_SHIFT 3 +#define CCDC_VD_POL_MASK 1 +#define CCDC_VD_POL_SHIFT 2 +#define CCDC_VD_POL_NEGATIVE (1 << 2) +#define CCDC_FRM_FMT_MASK 1 +#define CCDC_FRM_FMT_SHIFT 7 +#define CCDC_DATA_SZ_MASK 7 +#define CCDC_DATA_SZ_SHIFT 8 +#define CCDC_VDHDOUT_MASK 1 +#define CCDC_VDHDOUT_SHIFT 0 +#define CCDC_EXWEN_MASK 1 +#define CCDC_EXWEN_SHIFT 5 +#define CCDC_INPUT_MODE_MASK 3 +#define CCDC_INPUT_MODE_SHIFT 12 +#define CCDC_PIX_FMT_MASK 3 +#define CCDC_PIX_FMT_SHIFT 12 +#define CCDC_DATAPOL_MASK 1 +#define CCDC_DATAPOL_SHIFT 6 +#define CCDC_WEN_ENABLE (1 << 1) +#define CCDC_VDHDEN_ENABLE (1 << 16) +#define CCDC_LPF_ENABLE (1 << 14) +#define CCDC_ALAW_ENABLE 1 +#define CCDC_ALAW_GAMMA_WD_MASK 7 +#define CCDC_REC656IF_BT656_EN 3 + +#define CCDC_FMTCFG_FMTMODE_MASK 3 +#define CCDC_FMTCFG_FMTMODE_SHIFT 1 +#define CCDC_FMTCFG_LNUM_MASK 3 +#define CCDC_FMTCFG_LNUM_SHIFT 4 +#define CCDC_FMTCFG_ADDRINC_MASK 7 +#define CCDC_FMTCFG_ADDRINC_SHIFT 8 + +#define CCDC_CCDCFG_FIDMD_SHIFT 6 +#define CCDC_CCDCFG_WENLOG_SHIFT 8 +#define CCDC_CCDCFG_TRGSEL_SHIFT 9 +#define CCDC_CCDCFG_EXTRG_SHIFT 10 +#define CCDC_CCDCFG_MSBINVI_SHIFT 13 + +#define CCDC_HSIZE_FLIP_SHIFT 12 +#define CCDC_HSIZE_FLIP_MASK 1 +#define CCDC_HSIZE_VAL_MASK 0xFFF +#define CCDC_SDOFST_FIELD_INTERLEAVED 0x249 +#define CCDC_SDOFST_INTERLACE_INVERSE 0x4B6D +#define CCDC_SDOFST_INTERLACE_NORMAL 0x0B6D +#define CCDC_SDOFST_PROGRESSIVE_INVERSE 0x4000 +#define CCDC_SDOFST_PROGRESSIVE_NORMAL 0 +#define CCDC_START_PX_HOR_MASK 0x7FFF +#define CCDC_NUM_PX_HOR_MASK 0x7FFF +#define CCDC_START_VER_ONE_MASK 0x7FFF +#define CCDC_START_VER_TWO_MASK 0x7FFF +#define CCDC_NUM_LINES_VER 0x7FFF + +#define CCDC_BLK_CLAMP_ENABLE (1 << 15) +#define CCDC_BLK_SGAIN_MASK 0x1F +#define CCDC_BLK_ST_PXL_MASK 0x1FFF +#define CCDC_BLK_SAMPLE_LN_MASK 3 +#define CCDC_BLK_SAMPLE_LN_SHIFT 13 + +#define CCDC_NUM_LINE_CALC_MASK 3 +#define CCDC_NUM_LINE_CALC_SHIFT 14 + +#define CCDC_BLK_DC_SUB_MASK 0x3FFF +#define CCDC_BLK_COMP_MASK 0xFF +#define CCDC_BLK_COMP_GB_COMP_SHIFT 8 +#define CCDC_BLK_COMP_GR_COMP_SHIFT 0 +#define CCDC_BLK_COMP_R_COMP_SHIFT 8 +#define CCDC_LATCH_ON_VSYNC_DISABLE (1 << 15) +#define CCDC_LATCH_ON_VSYNC_ENABLE (0 << 15) +#define CCDC_FPC_ENABLE (1 << 15) +#define CCDC_FPC_FPC_NUM_MASK 0x7FFF +#define CCDC_DATA_PACK_ENABLE (1 << 11) +#define CCDC_FMT_HORZ_FMTLNH_MASK 0x1FFF +#define CCDC_FMT_HORZ_FMTSPH_MASK 0x1FFF +#define CCDC_FMT_HORZ_FMTSPH_SHIFT 16 +#define CCDC_FMT_VERT_FMTLNV_MASK 0x1FFF +#define CCDC_FMT_VERT_FMTSLV_MASK 0x1FFF +#define CCDC_FMT_VERT_FMTSLV_SHIFT 16 +#define CCDC_VP_OUT_VERT_NUM_MASK 0x3FFF +#define CCDC_VP_OUT_VERT_NUM_SHIFT 17 +#define CCDC_VP_OUT_HORZ_NUM_MASK 0x1FFF +#define CCDC_VP_OUT_HORZ_NUM_SHIFT 4 +#define CCDC_VP_OUT_HORZ_ST_MASK 0xF + +#define CCDC_CSC_COEF_INTEG_MASK 7 +#define CCDC_CSC_COEF_DECIMAL_MASK 0x1f +#define CCDC_CSC_COEF_INTEG_SHIFT 5 +#define CCDC_CSCM_MSB_SHIFT 8 +#define CCDC_CSC_ENABLE 1 +#define CCDC_CSC_DEC_MAX 32 + +#define CCDC_MFILT1_SHIFT 10 +#define CCDC_MFILT2_SHIFT 8 +#define CCDC_MED_FILT_THRESH 0x3FFF +#define CCDC_LPF_MASK 1 +#define CCDC_LPF_SHIFT 14 +#define CCDC_OFFSET_MASK 0x3FF +#define CCDC_DATASFT_MASK 7 +#define CCDC_DATASFT_SHIFT 8 + +#define CCDC_DF_ENABLE 1 + +#define CCDC_FMTPLEN_P0_MASK 0xF +#define CCDC_FMTPLEN_P1_MASK 0xF +#define CCDC_FMTPLEN_P2_MASK 7 +#define CCDC_FMTPLEN_P3_MASK 7 +#define CCDC_FMTPLEN_P0_SHIFT 0 +#define CCDC_FMTPLEN_P1_SHIFT 4 +#define CCDC_FMTPLEN_P2_SHIFT 8 +#define CCDC_FMTPLEN_P3_SHIFT 12 + +#define CCDC_FMTSPH_MASK 0x1FFF +#define CCDC_FMTLNH_MASK 0x1FFF +#define CCDC_FMTSLV_MASK 0x1FFF +#define CCDC_FMTLNV_MASK 0x7FFF +#define CCDC_FMTRLEN_MASK 0x1FFF +#define CCDC_FMTHCNT_MASK 0x1FFF + +#define CCDC_ADP_INIT_MASK 0x1FFF +#define CCDC_ADP_LINE_SHIFT 13 +#define CCDC_ADP_LINE_MASK 3 +#define CCDC_FMTPGN_APTR_MASK 7 + +#define CCDC_DFCCTL_GDFCEN_MASK 1 +#define CCDC_DFCCTL_VDFCEN_MASK 1 +#define CCDC_DFCCTL_VDFC_DISABLE (0 << 4) +#define CCDC_DFCCTL_VDFCEN_SHIFT 4 +#define CCDC_DFCCTL_VDFCSL_MASK 3 +#define CCDC_DFCCTL_VDFCSL_SHIFT 5 +#define CCDC_DFCCTL_VDFCUDA_MASK 1 +#define CCDC_DFCCTL_VDFCUDA_SHIFT 7 +#define CCDC_DFCCTL_VDFLSFT_MASK 3 +#define CCDC_DFCCTL_VDFLSFT_SHIFT 8 +#define CCDC_DFCMEMCTL_DFCMARST_MASK 1 +#define CCDC_DFCMEMCTL_DFCMARST_SHIFT 2 +#define CCDC_DFCMEMCTL_DFCMWR_MASK 1 +#define CCDC_DFCMEMCTL_DFCMWR_SHIFT 0 +#define CCDC_DFCMEMCTL_INC_ADDR (0 << 2) + +#define CCDC_LSCCFG_GFTSF_MASK 7 +#define CCDC_LSCCFG_GFTSF_SHIFT 1 +#define CCDC_LSCCFG_GFTINV_MASK 0xf +#define CCDC_LSCCFG_GFTINV_SHIFT 4 +#define CCDC_LSC_GFTABLE_SEL_MASK 3 +#define CCDC_LSC_GFTABLE_EPEL_SHIFT 8 +#define CCDC_LSC_GFTABLE_OPEL_SHIFT 10 +#define CCDC_LSC_GFTABLE_EPOL_SHIFT 12 +#define CCDC_LSC_GFTABLE_OPOL_SHIFT 14 +#define CCDC_LSC_GFMODE_MASK 3 +#define CCDC_LSC_GFMODE_SHIFT 4 +#define CCDC_LSC_DISABLE 0 +#define CCDC_LSC_ENABLE 1 +#define CCDC_LSC_TABLE1_SLC 0 +#define CCDC_LSC_TABLE2_SLC 1 +#define CCDC_LSC_TABLE3_SLC 2 +#define CCDC_LSC_MEMADDR_RESET (1 << 2) +#define CCDC_LSC_MEMADDR_INCR (0 << 2) +#define CCDC_LSC_FRAC_MASK_T1 0xFF +#define CCDC_LSC_INT_MASK 3 +#define CCDC_LSC_FRAC_MASK 0x3FFF +#define CCDC_LSC_CENTRE_MASK 0x3FFF +#define CCDC_LSC_COEF_MASK 0xff +#define CCDC_LSC_COEFL_SHIFT 0 +#define CCDC_LSC_COEFU_SHIFT 8 +#define CCDC_GAIN_MASK 0x7FF +#define CCDC_SYNCEN_VDHDEN_MASK (1 << 0) +#define CCDC_SYNCEN_WEN_MASK (1 << 1) +#define CCDC_SYNCEN_WEN_SHIFT 1 + +/* Power on Defaults in hardware */ +#define MODESET_DEFAULT 0x200 +#define CULH_DEFAULT 0xFFFF +#define CULV_DEFAULT 0xFF +#define GAIN_DEFAULT 256 +#define OUTCLIP_DEFAULT 0x3FFF +#define LSCCFG2_DEFAULT 0xE + +#endif diff --git a/drivers/staging/media/deprecated/vpfe_capture/dm644x_ccdc.c b/drivers/staging/media/deprecated/vpfe_capture/dm644x_ccdc.c new file mode 100644 index 000000000000..4a93e5ad6415 --- /dev/null +++ b/drivers/staging/media/deprecated/vpfe_capture/dm644x_ccdc.c @@ -0,0 +1,879 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2006-2009 Texas Instruments Inc + * + * CCDC hardware module for DM6446 + * ------------------------------ + * + * This module is for configuring CCD controller of DM6446 VPFE to capture + * Raw yuv or Bayer RGB data from a decoder. CCDC has several modules + * such as Defect Pixel Correction, Color Space Conversion etc to + * pre-process the Raw Bayer RGB data, before writing it to SDRAM. + * This file is named DM644x so that other variants such DM6443 + * may be supported using the same module. + * + * TODO: Test Raw bayer parameter settings and bayer capture + * Split module parameter structure to module specific ioctl structs + * investigate if enum used for user space type definition + * to be replaced by #defines or integer + */ +#include <linux/platform_device.h> +#include <linux/uaccess.h> +#include <linux/videodev2.h> +#include <linux/gfp.h> +#include <linux/err.h> +#include <linux/module.h> + +#include "dm644x_ccdc.h" +#include <media/davinci/vpss.h> + +#include "dm644x_ccdc_regs.h" +#include "ccdc_hw_device.h" + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("CCDC Driver for DM6446"); +MODULE_AUTHOR("Texas Instruments"); + +static struct ccdc_oper_config { + struct device *dev; + /* CCDC interface type */ + enum vpfe_hw_if_type if_type; + /* Raw Bayer configuration */ + struct ccdc_params_raw bayer; + /* YCbCr configuration */ + struct ccdc_params_ycbcr ycbcr; + /* ccdc base address */ + void __iomem *base_addr; +} ccdc_cfg = { + /* Raw configurations */ + .bayer = { + .pix_fmt = CCDC_PIXFMT_RAW, + .frm_fmt = CCDC_FRMFMT_PROGRESSIVE, + .win = CCDC_WIN_VGA, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .config_params = { + .data_sz = CCDC_DATA_10BITS, + }, + }, + .ycbcr = { + .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT, + .frm_fmt = CCDC_FRMFMT_INTERLACED, + .win = CCDC_WIN_PAL, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .bt656_enable = 1, + .pix_order = CCDC_PIXORDER_CBYCRY, + .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED + }, +}; + +#define CCDC_MAX_RAW_YUV_FORMATS 2 + +/* Raw Bayer formats */ +static u32 ccdc_raw_bayer_pix_formats[] = + {V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16}; + +/* Raw YUV formats */ +static u32 ccdc_raw_yuv_pix_formats[] = + {V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV}; + +/* CCDC Save/Restore context */ +static u32 ccdc_ctx[CCDC_REG_END / sizeof(u32)]; + +/* register access routines */ +static inline u32 regr(u32 offset) +{ + return __raw_readl(ccdc_cfg.base_addr + offset); +} + +static inline void regw(u32 val, u32 offset) +{ + __raw_writel(val, ccdc_cfg.base_addr + offset); +} + +static void ccdc_enable(int flag) +{ + regw(flag, CCDC_PCR); +} + +static void ccdc_enable_vport(int flag) +{ + if (flag) + /* enable video port */ + regw(CCDC_ENABLE_VIDEO_PORT, CCDC_FMTCFG); + else + regw(CCDC_DISABLE_VIDEO_PORT, CCDC_FMTCFG); +} + +/* + * ccdc_setwin() + * This function will configure the window size + * to be capture in CCDC reg + */ +static void ccdc_setwin(struct v4l2_rect *image_win, + enum ccdc_frmfmt frm_fmt, + int ppc) +{ + int horz_start, horz_nr_pixels; + int vert_start, vert_nr_lines; + int val = 0, mid_img = 0; + + dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_setwin..."); + /* + * ppc - per pixel count. indicates how many pixels per cell + * output to SDRAM. example, for ycbcr, it is one y and one c, so 2. + * raw capture this is 1 + */ + horz_start = image_win->left << (ppc - 1); + horz_nr_pixels = (image_win->width << (ppc - 1)) - 1; + regw((horz_start << CCDC_HORZ_INFO_SPH_SHIFT) | horz_nr_pixels, + CCDC_HORZ_INFO); + + vert_start = image_win->top; + + if (frm_fmt == CCDC_FRMFMT_INTERLACED) { + vert_nr_lines = (image_win->height >> 1) - 1; + vert_start >>= 1; + /* Since first line doesn't have any data */ + vert_start += 1; + /* configure VDINT0 */ + val = (vert_start << CCDC_VDINT_VDINT0_SHIFT); + regw(val, CCDC_VDINT); + + } else { + /* Since first line doesn't have any data */ + vert_start += 1; + vert_nr_lines = image_win->height - 1; + /* + * configure VDINT0 and VDINT1. VDINT1 will be at half + * of image height + */ + mid_img = vert_start + (image_win->height / 2); + val = (vert_start << CCDC_VDINT_VDINT0_SHIFT) | + (mid_img & CCDC_VDINT_VDINT1_MASK); + regw(val, CCDC_VDINT); + + } + regw((vert_start << CCDC_VERT_START_SLV0_SHIFT) | vert_start, + CCDC_VERT_START); + regw(vert_nr_lines, CCDC_VERT_LINES); + dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_setwin..."); +} + +static void ccdc_readregs(void) +{ + unsigned int val = 0; + + val = regr(CCDC_ALAW); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to ALAW...\n", val); + val = regr(CCDC_CLAMP); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to CLAMP...\n", val); + val = regr(CCDC_DCSUB); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to DCSUB...\n", val); + val = regr(CCDC_BLKCMP); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to BLKCMP...\n", val); + val = regr(CCDC_FPC_ADDR); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FPC_ADDR...\n", val); + val = regr(CCDC_FPC); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FPC...\n", val); + val = regr(CCDC_FMTCFG); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FMTCFG...\n", val); + val = regr(CCDC_COLPTN); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to COLPTN...\n", val); + val = regr(CCDC_FMT_HORZ); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FMT_HORZ...\n", val); + val = regr(CCDC_FMT_VERT); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FMT_VERT...\n", val); + val = regr(CCDC_HSIZE_OFF); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to HSIZE_OFF...\n", val); + val = regr(CCDC_SDOFST); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to SDOFST...\n", val); + val = regr(CCDC_VP_OUT); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to VP_OUT...\n", val); + val = regr(CCDC_SYN_MODE); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to SYN_MODE...\n", val); + val = regr(CCDC_HORZ_INFO); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to HORZ_INFO...\n", val); + val = regr(CCDC_VERT_START); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to VERT_START...\n", val); + val = regr(CCDC_VERT_LINES); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to VERT_LINES...\n", val); +} + +static int ccdc_close(struct device *dev) +{ + return 0; +} + +/* + * ccdc_restore_defaults() + * This function will write defaults to all CCDC registers + */ +static void ccdc_restore_defaults(void) +{ + int i; + + /* disable CCDC */ + ccdc_enable(0); + /* set all registers to default value */ + for (i = 4; i <= 0x94; i += 4) + regw(0, i); + regw(CCDC_NO_CULLING, CCDC_CULLING); + regw(CCDC_GAMMA_BITS_11_2, CCDC_ALAW); +} + +static int ccdc_open(struct device *device) +{ + ccdc_restore_defaults(); + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + ccdc_enable_vport(1); + return 0; +} + +static void ccdc_sbl_reset(void) +{ + vpss_clear_wbl_overflow(VPSS_PCR_CCDC_WBL_O); +} + +/* + * ccdc_config_ycbcr() + * This function will configure CCDC for YCbCr video capture + */ +static void ccdc_config_ycbcr(void) +{ + struct ccdc_params_ycbcr *params = &ccdc_cfg.ycbcr; + u32 syn_mode; + + dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_ycbcr..."); + /* + * first restore the CCDC registers to default values + * This is important since we assume default values to be set in + * a lot of registers that we didn't touch + */ + ccdc_restore_defaults(); + + /* + * configure pixel format, frame format, configure video frame + * format, enable output to SDRAM, enable internal timing generator + * and 8bit pack mode + */ + syn_mode = (((params->pix_fmt & CCDC_SYN_MODE_INPMOD_MASK) << + CCDC_SYN_MODE_INPMOD_SHIFT) | + ((params->frm_fmt & CCDC_SYN_FLDMODE_MASK) << + CCDC_SYN_FLDMODE_SHIFT) | CCDC_VDHDEN_ENABLE | + CCDC_WEN_ENABLE | CCDC_DATA_PACK_ENABLE); + + /* setup BT.656 sync mode */ + if (params->bt656_enable) { + regw(CCDC_REC656IF_BT656_EN, CCDC_REC656IF); + + /* + * configure the FID, VD, HD pin polarity, + * fld,hd pol positive, vd negative, 8-bit data + */ + syn_mode |= CCDC_SYN_MODE_VD_POL_NEGATIVE; + if (ccdc_cfg.if_type == VPFE_BT656_10BIT) + syn_mode |= CCDC_SYN_MODE_10BITS; + else + syn_mode |= CCDC_SYN_MODE_8BITS; + } else { + /* y/c external sync mode */ + syn_mode |= (((params->fid_pol & CCDC_FID_POL_MASK) << + CCDC_FID_POL_SHIFT) | + ((params->hd_pol & CCDC_HD_POL_MASK) << + CCDC_HD_POL_SHIFT) | + ((params->vd_pol & CCDC_VD_POL_MASK) << + CCDC_VD_POL_SHIFT)); + } + regw(syn_mode, CCDC_SYN_MODE); + + /* configure video window */ + ccdc_setwin(¶ms->win, params->frm_fmt, 2); + + /* + * configure the order of y cb cr in SDRAM, and disable latch + * internal register on vsync + */ + if (ccdc_cfg.if_type == VPFE_BT656_10BIT) + regw((params->pix_order << CCDC_CCDCFG_Y8POS_SHIFT) | + CCDC_LATCH_ON_VSYNC_DISABLE | CCDC_CCDCFG_BW656_10BIT, + CCDC_CCDCFG); + else + regw((params->pix_order << CCDC_CCDCFG_Y8POS_SHIFT) | + CCDC_LATCH_ON_VSYNC_DISABLE, CCDC_CCDCFG); + + /* + * configure the horizontal line offset. This should be a + * on 32 byte boundary. So clear LSB 5 bits + */ + regw(((params->win.width * 2 + 31) & ~0x1f), CCDC_HSIZE_OFF); + + /* configure the memory line offset */ + if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) + /* two fields are interleaved in memory */ + regw(CCDC_SDOFST_FIELD_INTERLEAVED, CCDC_SDOFST); + + ccdc_sbl_reset(); + dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_config_ycbcr...\n"); +} + +static void ccdc_config_black_clamp(struct ccdc_black_clamp *bclamp) +{ + u32 val; + + if (!bclamp->enable) { + /* configure DCSub */ + val = (bclamp->dc_sub) & CCDC_BLK_DC_SUB_MASK; + regw(val, CCDC_DCSUB); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to DCSUB...\n", val); + regw(CCDC_CLAMP_DEFAULT_VAL, CCDC_CLAMP); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x0000 to CLAMP...\n"); + return; + } + /* + * Configure gain, Start pixel, No of line to be avg, + * No of pixel/line to be avg, & Enable the Black clamping + */ + val = ((bclamp->sgain & CCDC_BLK_SGAIN_MASK) | + ((bclamp->start_pixel & CCDC_BLK_ST_PXL_MASK) << + CCDC_BLK_ST_PXL_SHIFT) | + ((bclamp->sample_ln & CCDC_BLK_SAMPLE_LINE_MASK) << + CCDC_BLK_SAMPLE_LINE_SHIFT) | + ((bclamp->sample_pixel & CCDC_BLK_SAMPLE_LN_MASK) << + CCDC_BLK_SAMPLE_LN_SHIFT) | CCDC_BLK_CLAMP_ENABLE); + regw(val, CCDC_CLAMP); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to CLAMP...\n", val); + /* If Black clamping is enable then make dcsub 0 */ + regw(CCDC_DCSUB_DEFAULT_VAL, CCDC_DCSUB); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x00000000 to DCSUB...\n"); +} + +static void ccdc_config_black_compense(struct ccdc_black_compensation *bcomp) +{ + u32 val; + + val = ((bcomp->b & CCDC_BLK_COMP_MASK) | + ((bcomp->gb & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_GB_COMP_SHIFT) | + ((bcomp->gr & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_GR_COMP_SHIFT) | + ((bcomp->r & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_R_COMP_SHIFT)); + regw(val, CCDC_BLKCMP); +} + +/* + * ccdc_config_raw() + * This function will configure CCDC for Raw capture mode + */ +static void ccdc_config_raw(void) +{ + struct ccdc_params_raw *params = &ccdc_cfg.bayer; + struct ccdc_config_params_raw *config_params = + &ccdc_cfg.bayer.config_params; + unsigned int syn_mode = 0; + unsigned int val; + + dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_raw..."); + + /* Reset CCDC */ + ccdc_restore_defaults(); + + /* Disable latching function registers on VSYNC */ + regw(CCDC_LATCH_ON_VSYNC_DISABLE, CCDC_CCDCFG); + + /* + * Configure the vertical sync polarity(SYN_MODE.VDPOL), + * horizontal sync polarity (SYN_MODE.HDPOL), frame id polarity + * (SYN_MODE.FLDPOL), frame format(progressive or interlace), + * data size(SYNMODE.DATSIZ), &pixel format (Input mode), output + * SDRAM, enable internal timing generator + */ + syn_mode = + (((params->vd_pol & CCDC_VD_POL_MASK) << CCDC_VD_POL_SHIFT) | + ((params->hd_pol & CCDC_HD_POL_MASK) << CCDC_HD_POL_SHIFT) | + ((params->fid_pol & CCDC_FID_POL_MASK) << CCDC_FID_POL_SHIFT) | + ((params->frm_fmt & CCDC_FRM_FMT_MASK) << CCDC_FRM_FMT_SHIFT) | + ((config_params->data_sz & CCDC_DATA_SZ_MASK) << + CCDC_DATA_SZ_SHIFT) | + ((params->pix_fmt & CCDC_PIX_FMT_MASK) << CCDC_PIX_FMT_SHIFT) | + CCDC_WEN_ENABLE | CCDC_VDHDEN_ENABLE); + + /* Enable and configure aLaw register if needed */ + if (config_params->alaw.enable) { + val = ((config_params->alaw.gamma_wd & + CCDC_ALAW_GAMMA_WD_MASK) | CCDC_ALAW_ENABLE); + regw(val, CCDC_ALAW); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to ALAW...\n", val); + } + + /* Configure video window */ + ccdc_setwin(¶ms->win, params->frm_fmt, CCDC_PPC_RAW); + + /* Configure Black Clamp */ + ccdc_config_black_clamp(&config_params->blk_clamp); + + /* Configure Black level compensation */ + ccdc_config_black_compense(&config_params->blk_comp); + + /* If data size is 8 bit then pack the data */ + if ((config_params->data_sz == CCDC_DATA_8BITS) || + config_params->alaw.enable) + syn_mode |= CCDC_DATA_PACK_ENABLE; + + /* disable video port */ + val = CCDC_DISABLE_VIDEO_PORT; + + if (config_params->data_sz == CCDC_DATA_8BITS) + val |= (CCDC_DATA_10BITS & CCDC_FMTCFG_VPIN_MASK) + << CCDC_FMTCFG_VPIN_SHIFT; + else + val |= (config_params->data_sz & CCDC_FMTCFG_VPIN_MASK) + << CCDC_FMTCFG_VPIN_SHIFT; + /* Write value in FMTCFG */ + regw(val, CCDC_FMTCFG); + + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FMTCFG...\n", val); + /* Configure the color pattern according to mt9t001 sensor */ + regw(CCDC_COLPTN_VAL, CCDC_COLPTN); + + dev_dbg(ccdc_cfg.dev, "\nWriting 0xBB11BB11 to COLPTN...\n"); + /* + * Configure Data formatter(Video port) pixel selection + * (FMT_HORZ, FMT_VERT) + */ + val = ((params->win.left & CCDC_FMT_HORZ_FMTSPH_MASK) << + CCDC_FMT_HORZ_FMTSPH_SHIFT) | + (params->win.width & CCDC_FMT_HORZ_FMTLNH_MASK); + regw(val, CCDC_FMT_HORZ); + + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FMT_HORZ...\n", val); + val = (params->win.top & CCDC_FMT_VERT_FMTSLV_MASK) + << CCDC_FMT_VERT_FMTSLV_SHIFT; + if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) + val |= (params->win.height) & CCDC_FMT_VERT_FMTLNV_MASK; + else + val |= (params->win.height >> 1) & CCDC_FMT_VERT_FMTLNV_MASK; + + dev_dbg(ccdc_cfg.dev, "\nparams->win.height 0x%x ...\n", + params->win.height); + regw(val, CCDC_FMT_VERT); + + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FMT_VERT...\n", val); + + dev_dbg(ccdc_cfg.dev, "\nbelow regw(val, FMT_VERT)..."); + + /* + * Configure Horizontal offset register. If pack 8 is enabled then + * 1 pixel will take 1 byte + */ + if ((config_params->data_sz == CCDC_DATA_8BITS) || + config_params->alaw.enable) + regw((params->win.width + CCDC_32BYTE_ALIGN_VAL) & + CCDC_HSIZE_OFF_MASK, CCDC_HSIZE_OFF); + else + /* else one pixel will take 2 byte */ + regw(((params->win.width * CCDC_TWO_BYTES_PER_PIXEL) + + CCDC_32BYTE_ALIGN_VAL) & CCDC_HSIZE_OFF_MASK, + CCDC_HSIZE_OFF); + + /* Set value for SDOFST */ + if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) { + if (params->image_invert_enable) { + /* For intelace inverse mode */ + regw(CCDC_INTERLACED_IMAGE_INVERT, CCDC_SDOFST); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x4B6D to SDOFST..\n"); + } + + else { + /* For intelace non inverse mode */ + regw(CCDC_INTERLACED_NO_IMAGE_INVERT, CCDC_SDOFST); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x0249 to SDOFST..\n"); + } + } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) { + regw(CCDC_PROGRESSIVE_NO_IMAGE_INVERT, CCDC_SDOFST); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x0000 to SDOFST...\n"); + } + + /* + * Configure video port pixel selection (VPOUT) + * Here -1 is to make the height value less than FMT_VERT.FMTLNV + */ + if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) + val = (((params->win.height - 1) & CCDC_VP_OUT_VERT_NUM_MASK)) + << CCDC_VP_OUT_VERT_NUM_SHIFT; + else + val = + ((((params->win.height >> CCDC_INTERLACED_HEIGHT_SHIFT) - + 1) & CCDC_VP_OUT_VERT_NUM_MASK)) << + CCDC_VP_OUT_VERT_NUM_SHIFT; + + val |= ((((params->win.width))) & CCDC_VP_OUT_HORZ_NUM_MASK) + << CCDC_VP_OUT_HORZ_NUM_SHIFT; + val |= (params->win.left) & CCDC_VP_OUT_HORZ_ST_MASK; + regw(val, CCDC_VP_OUT); + + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to VP_OUT...\n", val); + regw(syn_mode, CCDC_SYN_MODE); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to SYN_MODE...\n", syn_mode); + + ccdc_sbl_reset(); + dev_dbg(ccdc_cfg.dev, "\nend of ccdc_config_raw..."); + ccdc_readregs(); +} + +static int ccdc_configure(void) +{ + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + ccdc_config_raw(); + else + ccdc_config_ycbcr(); + return 0; +} + +static int ccdc_set_buftype(enum ccdc_buftype buf_type) +{ + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + ccdc_cfg.bayer.buf_type = buf_type; + else + ccdc_cfg.ycbcr.buf_type = buf_type; + return 0; +} + +static enum ccdc_buftype ccdc_get_buftype(void) +{ + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + return ccdc_cfg.bayer.buf_type; + return ccdc_cfg.ycbcr.buf_type; +} + +static int ccdc_enum_pix(u32 *pix, int i) +{ + int ret = -EINVAL; + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) { + if (i < ARRAY_SIZE(ccdc_raw_bayer_pix_formats)) { + *pix = ccdc_raw_bayer_pix_formats[i]; + ret = 0; + } + } else { + if (i < ARRAY_SIZE(ccdc_raw_yuv_pix_formats)) { + *pix = ccdc_raw_yuv_pix_formats[i]; + ret = 0; + } + } + return ret; +} + +static int ccdc_set_pixel_format(u32 pixfmt) +{ + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) { + ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; + if (pixfmt == V4L2_PIX_FMT_SBGGR8) + ccdc_cfg.bayer.config_params.alaw.enable = 1; + else if (pixfmt != V4L2_PIX_FMT_SBGGR16) + return -EINVAL; + } else { + if (pixfmt == V4L2_PIX_FMT_YUYV) + ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR; + else if (pixfmt == V4L2_PIX_FMT_UYVY) + ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; + else + return -EINVAL; + } + return 0; +} + +static u32 ccdc_get_pixel_format(void) +{ + struct ccdc_a_law *alaw = &ccdc_cfg.bayer.config_params.alaw; + u32 pixfmt; + + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + if (alaw->enable) + pixfmt = V4L2_PIX_FMT_SBGGR8; + else + pixfmt = V4L2_PIX_FMT_SBGGR16; + else { + if (ccdc_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR) + pixfmt = V4L2_PIX_FMT_YUYV; + else + pixfmt = V4L2_PIX_FMT_UYVY; + } + return pixfmt; +} + +static int ccdc_set_image_window(struct v4l2_rect *win) +{ + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + ccdc_cfg.bayer.win = *win; + else + ccdc_cfg.ycbcr.win = *win; + return 0; +} + +static void ccdc_get_image_window(struct v4l2_rect *win) +{ + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + *win = ccdc_cfg.bayer.win; + else + *win = ccdc_cfg.ycbcr.win; +} + +static unsigned int ccdc_get_line_length(void) +{ + struct ccdc_config_params_raw *config_params = + &ccdc_cfg.bayer.config_params; + unsigned int len; + + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) { + if ((config_params->alaw.enable) || + (config_params->data_sz == CCDC_DATA_8BITS)) + len = ccdc_cfg.bayer.win.width; + else + len = ccdc_cfg.bayer.win.width * 2; + } else + len = ccdc_cfg.ycbcr.win.width * 2; + return ALIGN(len, 32); +} + +static int ccdc_set_frame_format(enum ccdc_frmfmt frm_fmt) +{ + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + ccdc_cfg.bayer.frm_fmt = frm_fmt; + else + ccdc_cfg.ycbcr.frm_fmt = frm_fmt; + return 0; +} + +static enum ccdc_frmfmt ccdc_get_frame_format(void) +{ + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + return ccdc_cfg.bayer.frm_fmt; + else + return ccdc_cfg.ycbcr.frm_fmt; +} + +static int ccdc_getfid(void) +{ + return (regr(CCDC_SYN_MODE) >> 15) & 1; +} + +/* misc operations */ +static inline void ccdc_setfbaddr(unsigned long addr) +{ + regw(addr & 0xffffffe0, CCDC_SDR_ADDR); +} + +static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params) +{ + ccdc_cfg.if_type = params->if_type; + + switch (params->if_type) { + case VPFE_BT656: + case VPFE_YCBCR_SYNC_16: + case VPFE_YCBCR_SYNC_8: + case VPFE_BT656_10BIT: + ccdc_cfg.ycbcr.vd_pol = params->vdpol; + ccdc_cfg.ycbcr.hd_pol = params->hdpol; + break; + default: + /* TODO add support for raw bayer here */ + return -EINVAL; + } + return 0; +} + +static void ccdc_save_context(void) +{ + ccdc_ctx[CCDC_PCR >> 2] = regr(CCDC_PCR); + ccdc_ctx[CCDC_SYN_MODE >> 2] = regr(CCDC_SYN_MODE); + ccdc_ctx[CCDC_HD_VD_WID >> 2] = regr(CCDC_HD_VD_WID); + ccdc_ctx[CCDC_PIX_LINES >> 2] = regr(CCDC_PIX_LINES); + ccdc_ctx[CCDC_HORZ_INFO >> 2] = regr(CCDC_HORZ_INFO); + ccdc_ctx[CCDC_VERT_START >> 2] = regr(CCDC_VERT_START); + ccdc_ctx[CCDC_VERT_LINES >> 2] = regr(CCDC_VERT_LINES); + ccdc_ctx[CCDC_CULLING >> 2] = regr(CCDC_CULLING); + ccdc_ctx[CCDC_HSIZE_OFF >> 2] = regr(CCDC_HSIZE_OFF); + ccdc_ctx[CCDC_SDOFST >> 2] = regr(CCDC_SDOFST); + ccdc_ctx[CCDC_SDR_ADDR >> 2] = regr(CCDC_SDR_ADDR); + ccdc_ctx[CCDC_CLAMP >> 2] = regr(CCDC_CLAMP); + ccdc_ctx[CCDC_DCSUB >> 2] = regr(CCDC_DCSUB); + ccdc_ctx[CCDC_COLPTN >> 2] = regr(CCDC_COLPTN); + ccdc_ctx[CCDC_BLKCMP >> 2] = regr(CCDC_BLKCMP); + ccdc_ctx[CCDC_FPC >> 2] = regr(CCDC_FPC); + ccdc_ctx[CCDC_FPC_ADDR >> 2] = regr(CCDC_FPC_ADDR); + ccdc_ctx[CCDC_VDINT >> 2] = regr(CCDC_VDINT); + ccdc_ctx[CCDC_ALAW >> 2] = regr(CCDC_ALAW); + ccdc_ctx[CCDC_REC656IF >> 2] = regr(CCDC_REC656IF); + ccdc_ctx[CCDC_CCDCFG >> 2] = regr(CCDC_CCDCFG); + ccdc_ctx[CCDC_FMTCFG >> 2] = regr(CCDC_FMTCFG); + ccdc_ctx[CCDC_FMT_HORZ >> 2] = regr(CCDC_FMT_HORZ); + ccdc_ctx[CCDC_FMT_VERT >> 2] = regr(CCDC_FMT_VERT); + ccdc_ctx[CCDC_FMT_ADDR0 >> 2] = regr(CCDC_FMT_ADDR0); + ccdc_ctx[CCDC_FMT_ADDR1 >> 2] = regr(CCDC_FMT_ADDR1); + ccdc_ctx[CCDC_FMT_ADDR2 >> 2] = regr(CCDC_FMT_ADDR2); + ccdc_ctx[CCDC_FMT_ADDR3 >> 2] = regr(CCDC_FMT_ADDR3); + ccdc_ctx[CCDC_FMT_ADDR4 >> 2] = regr(CCDC_FMT_ADDR4); + ccdc_ctx[CCDC_FMT_ADDR5 >> 2] = regr(CCDC_FMT_ADDR5); + ccdc_ctx[CCDC_FMT_ADDR6 >> 2] = regr(CCDC_FMT_ADDR6); + ccdc_ctx[CCDC_FMT_ADDR7 >> 2] = regr(CCDC_FMT_ADDR7); + ccdc_ctx[CCDC_PRGEVEN_0 >> 2] = regr(CCDC_PRGEVEN_0); + ccdc_ctx[CCDC_PRGEVEN_1 >> 2] = regr(CCDC_PRGEVEN_1); + ccdc_ctx[CCDC_PRGODD_0 >> 2] = regr(CCDC_PRGODD_0); + ccdc_ctx[CCDC_PRGODD_1 >> 2] = regr(CCDC_PRGODD_1); + ccdc_ctx[CCDC_VP_OUT >> 2] = regr(CCDC_VP_OUT); +} + +static void ccdc_restore_context(void) +{ + regw(ccdc_ctx[CCDC_SYN_MODE >> 2], CCDC_SYN_MODE); + regw(ccdc_ctx[CCDC_HD_VD_WID >> 2], CCDC_HD_VD_WID); + regw(ccdc_ctx[CCDC_PIX_LINES >> 2], CCDC_PIX_LINES); + regw(ccdc_ctx[CCDC_HORZ_INFO >> 2], CCDC_HORZ_INFO); + regw(ccdc_ctx[CCDC_VERT_START >> 2], CCDC_VERT_START); + regw(ccdc_ctx[CCDC_VERT_LINES >> 2], CCDC_VERT_LINES); + regw(ccdc_ctx[CCDC_CULLING >> 2], CCDC_CULLING); + regw(ccdc_ctx[CCDC_HSIZE_OFF >> 2], CCDC_HSIZE_OFF); + regw(ccdc_ctx[CCDC_SDOFST >> 2], CCDC_SDOFST); + regw(ccdc_ctx[CCDC_SDR_ADDR >> 2], CCDC_SDR_ADDR); + regw(ccdc_ctx[CCDC_CLAMP >> 2], CCDC_CLAMP); + regw(ccdc_ctx[CCDC_DCSUB >> 2], CCDC_DCSUB); + regw(ccdc_ctx[CCDC_COLPTN >> 2], CCDC_COLPTN); + regw(ccdc_ctx[CCDC_BLKCMP >> 2], CCDC_BLKCMP); + regw(ccdc_ctx[CCDC_FPC >> 2], CCDC_FPC); + regw(ccdc_ctx[CCDC_FPC_ADDR >> 2], CCDC_FPC_ADDR); + regw(ccdc_ctx[CCDC_VDINT >> 2], CCDC_VDINT); + regw(ccdc_ctx[CCDC_ALAW >> 2], CCDC_ALAW); + regw(ccdc_ctx[CCDC_REC656IF >> 2], CCDC_REC656IF); + regw(ccdc_ctx[CCDC_CCDCFG >> 2], CCDC_CCDCFG); + regw(ccdc_ctx[CCDC_FMTCFG >> 2], CCDC_FMTCFG); + regw(ccdc_ctx[CCDC_FMT_HORZ >> 2], CCDC_FMT_HORZ); + regw(ccdc_ctx[CCDC_FMT_VERT >> 2], CCDC_FMT_VERT); + regw(ccdc_ctx[CCDC_FMT_ADDR0 >> 2], CCDC_FMT_ADDR0); + regw(ccdc_ctx[CCDC_FMT_ADDR1 >> 2], CCDC_FMT_ADDR1); + regw(ccdc_ctx[CCDC_FMT_ADDR2 >> 2], CCDC_FMT_ADDR2); + regw(ccdc_ctx[CCDC_FMT_ADDR3 >> 2], CCDC_FMT_ADDR3); + regw(ccdc_ctx[CCDC_FMT_ADDR4 >> 2], CCDC_FMT_ADDR4); + regw(ccdc_ctx[CCDC_FMT_ADDR5 >> 2], CCDC_FMT_ADDR5); + regw(ccdc_ctx[CCDC_FMT_ADDR6 >> 2], CCDC_FMT_ADDR6); + regw(ccdc_ctx[CCDC_FMT_ADDR7 >> 2], CCDC_FMT_ADDR7); + regw(ccdc_ctx[CCDC_PRGEVEN_0 >> 2], CCDC_PRGEVEN_0); + regw(ccdc_ctx[CCDC_PRGEVEN_1 >> 2], CCDC_PRGEVEN_1); + regw(ccdc_ctx[CCDC_PRGODD_0 >> 2], CCDC_PRGODD_0); + regw(ccdc_ctx[CCDC_PRGODD_1 >> 2], CCDC_PRGODD_1); + regw(ccdc_ctx[CCDC_VP_OUT >> 2], CCDC_VP_OUT); + regw(ccdc_ctx[CCDC_PCR >> 2], CCDC_PCR); +} +static const struct ccdc_hw_device ccdc_hw_dev = { + .name = "DM6446 CCDC", + .owner = THIS_MODULE, + .hw_ops = { + .open = ccdc_open, + .close = ccdc_close, + .reset = ccdc_sbl_reset, + .enable = ccdc_enable, + .set_hw_if_params = ccdc_set_hw_if_params, + .configure = ccdc_configure, + .set_buftype = ccdc_set_buftype, + .get_buftype = ccdc_get_buftype, + .enum_pix = ccdc_enum_pix, + .set_pixel_format = ccdc_set_pixel_format, + .get_pixel_format = ccdc_get_pixel_format, + .set_frame_format = ccdc_set_frame_format, + .get_frame_format = ccdc_get_frame_format, + .set_image_window = ccdc_set_image_window, + .get_image_window = ccdc_get_image_window, + .get_line_length = ccdc_get_line_length, + .setfbaddr = ccdc_setfbaddr, + .getfid = ccdc_getfid, + }, +}; + +static int dm644x_ccdc_probe(struct platform_device *pdev) +{ + struct resource *res; + int status = 0; + + /* + * first try to register with vpfe. If not correct platform, then we + * don't have to iomap + */ + status = vpfe_register_ccdc_device(&ccdc_hw_dev); + if (status < 0) + return status; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + status = -ENODEV; + goto fail_nores; + } + + res = request_mem_region(res->start, resource_size(res), res->name); + if (!res) { + status = -EBUSY; + goto fail_nores; + } + + ccdc_cfg.base_addr = ioremap(res->start, resource_size(res)); + if (!ccdc_cfg.base_addr) { + status = -ENOMEM; + goto fail_nomem; + } + + ccdc_cfg.dev = &pdev->dev; + printk(KERN_NOTICE "%s is registered with vpfe.\n", ccdc_hw_dev.name); + return 0; +fail_nomem: + release_mem_region(res->start, resource_size(res)); +fail_nores: + vpfe_unregister_ccdc_device(&ccdc_hw_dev); + return status; +} + +static int dm644x_ccdc_remove(struct platform_device *pdev) +{ + struct resource *res; + + iounmap(ccdc_cfg.base_addr); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + vpfe_unregister_ccdc_device(&ccdc_hw_dev); + return 0; +} + +static int dm644x_ccdc_suspend(struct device *dev) +{ + /* Save CCDC context */ + ccdc_save_context(); + /* Disable CCDC */ + ccdc_enable(0); + + return 0; +} + +static int dm644x_ccdc_resume(struct device *dev) +{ + /* Restore CCDC context */ + ccdc_restore_context(); + + return 0; +} + +static const struct dev_pm_ops dm644x_ccdc_pm_ops = { + .suspend = dm644x_ccdc_suspend, + .resume = dm644x_ccdc_resume, +}; + +static struct platform_driver dm644x_ccdc_driver = { + .driver = { + .name = "dm644x_ccdc", + .pm = &dm644x_ccdc_pm_ops, + }, + .remove = dm644x_ccdc_remove, + .probe = dm644x_ccdc_probe, +}; + +module_platform_driver(dm644x_ccdc_driver); diff --git a/drivers/staging/media/deprecated/vpfe_capture/dm644x_ccdc.h b/drivers/staging/media/deprecated/vpfe_capture/dm644x_ccdc.h new file mode 100644 index 000000000000..c20dba3d76d6 --- /dev/null +++ b/drivers/staging/media/deprecated/vpfe_capture/dm644x_ccdc.h @@ -0,0 +1,171 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2006-2009 Texas Instruments Inc + */ +#ifndef _DM644X_CCDC_H +#define _DM644X_CCDC_H +#include <media/davinci/ccdc_types.h> +#include <media/davinci/vpfe_types.h> + +/* enum for No of pixel per line to be avg. in Black Clamping*/ +enum ccdc_sample_length { + CCDC_SAMPLE_1PIXELS, + CCDC_SAMPLE_2PIXELS, + CCDC_SAMPLE_4PIXELS, + CCDC_SAMPLE_8PIXELS, + CCDC_SAMPLE_16PIXELS +}; + +/* enum for No of lines in Black Clamping */ +enum ccdc_sample_line { + CCDC_SAMPLE_1LINES, + CCDC_SAMPLE_2LINES, + CCDC_SAMPLE_4LINES, + CCDC_SAMPLE_8LINES, + CCDC_SAMPLE_16LINES +}; + +/* enum for Alaw gamma width */ +enum ccdc_gamma_width { + CCDC_GAMMA_BITS_15_6, /* use bits 15-6 for gamma */ + CCDC_GAMMA_BITS_14_5, + CCDC_GAMMA_BITS_13_4, + CCDC_GAMMA_BITS_12_3, + CCDC_GAMMA_BITS_11_2, + CCDC_GAMMA_BITS_10_1, + CCDC_GAMMA_BITS_09_0 /* use bits 9-0 for gamma */ +}; + +/* returns the highest bit used for the gamma */ +static inline u8 ccdc_gamma_width_max_bit(enum ccdc_gamma_width width) +{ + return 15 - width; +} + +enum ccdc_data_size { + CCDC_DATA_16BITS, + CCDC_DATA_15BITS, + CCDC_DATA_14BITS, + CCDC_DATA_13BITS, + CCDC_DATA_12BITS, + CCDC_DATA_11BITS, + CCDC_DATA_10BITS, + CCDC_DATA_8BITS +}; + +/* returns the highest bit used for this data size */ +static inline u8 ccdc_data_size_max_bit(enum ccdc_data_size sz) +{ + return sz == CCDC_DATA_8BITS ? 7 : 15 - sz; +} + +/* structure for ALaw */ +struct ccdc_a_law { + /* Enable/disable A-Law */ + unsigned char enable; + /* Gamma Width Input */ + enum ccdc_gamma_width gamma_wd; +}; + +/* structure for Black Clamping */ +struct ccdc_black_clamp { + unsigned char enable; + /* only if bClampEnable is TRUE */ + enum ccdc_sample_length sample_pixel; + /* only if bClampEnable is TRUE */ + enum ccdc_sample_line sample_ln; + /* only if bClampEnable is TRUE */ + unsigned short start_pixel; + /* only if bClampEnable is TRUE */ + unsigned short sgain; + /* only if bClampEnable is FALSE */ + unsigned short dc_sub; +}; + +/* structure for Black Level Compensation */ +struct ccdc_black_compensation { + /* Constant value to subtract from Red component */ + char r; + /* Constant value to subtract from Gr component */ + char gr; + /* Constant value to subtract from Blue component */ + char b; + /* Constant value to subtract from Gb component */ + char gb; +}; + +/* Structure for CCDC configuration parameters for raw capture mode passed + * by application + */ +struct ccdc_config_params_raw { + /* data size value from 8 to 16 bits */ + enum ccdc_data_size data_sz; + /* Structure for Optional A-Law */ + struct ccdc_a_law alaw; + /* Structure for Optical Black Clamp */ + struct ccdc_black_clamp blk_clamp; + /* Structure for Black Compensation */ + struct ccdc_black_compensation blk_comp; +}; + + +#ifdef __KERNEL__ +#include <linux/io.h> +/* Define to enable/disable video port */ +#define FP_NUM_BYTES 4 +/* Define for extra pixel/line and extra lines/frame */ +#define NUM_EXTRAPIXELS 8 +#define NUM_EXTRALINES 8 + +/* settings for commonly used video formats */ +#define CCDC_WIN_PAL {0, 0, 720, 576} +/* ntsc square pixel */ +#define CCDC_WIN_VGA {0, 0, (640 + NUM_EXTRAPIXELS), (480 + NUM_EXTRALINES)} + +/* Structure for CCDC configuration parameters for raw capture mode */ +struct ccdc_params_raw { + /* pixel format */ + enum ccdc_pixfmt pix_fmt; + /* progressive or interlaced frame */ + enum ccdc_frmfmt frm_fmt; + /* video window */ + struct v4l2_rect win; + /* field id polarity */ + enum vpfe_pin_pol fid_pol; + /* vertical sync polarity */ + enum vpfe_pin_pol vd_pol; + /* horizontal sync polarity */ + enum vpfe_pin_pol hd_pol; + /* interleaved or separated fields */ + enum ccdc_buftype buf_type; + /* + * enable to store the image in inverse + * order in memory(bottom to top) + */ + unsigned char image_invert_enable; + /* configurable parameters */ + struct ccdc_config_params_raw config_params; +}; + +struct ccdc_params_ycbcr { + /* pixel format */ + enum ccdc_pixfmt pix_fmt; + /* progressive or interlaced frame */ + enum ccdc_frmfmt frm_fmt; + /* video window */ + struct v4l2_rect win; + /* field id polarity */ + enum vpfe_pin_pol fid_pol; + /* vertical sync polarity */ + enum vpfe_pin_pol vd_pol; + /* horizontal sync polarity */ + enum vpfe_pin_pol hd_pol; + /* enable BT.656 embedded sync mode */ + int bt656_enable; + /* cb:y:cr:y or y:cb:y:cr in memory */ + enum ccdc_pixorder pix_order; + /* interleaved or separated fields */ + enum ccdc_buftype buf_type; +}; +#endif +#endif /* _DM644X_CCDC_H */ diff --git a/drivers/staging/media/deprecated/vpfe_capture/dm644x_ccdc_regs.h b/drivers/staging/media/deprecated/vpfe_capture/dm644x_ccdc_regs.h new file mode 100644 index 000000000000..c4894f6a254e --- /dev/null +++ b/drivers/staging/media/deprecated/vpfe_capture/dm644x_ccdc_regs.h @@ -0,0 +1,140 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2006-2009 Texas Instruments Inc + */ +#ifndef _DM644X_CCDC_REGS_H +#define _DM644X_CCDC_REGS_H + +/**************************************************************************\ +* Register OFFSET Definitions +\**************************************************************************/ +#define CCDC_PID 0x0 +#define CCDC_PCR 0x4 +#define CCDC_SYN_MODE 0x8 +#define CCDC_HD_VD_WID 0xc +#define CCDC_PIX_LINES 0x10 +#define CCDC_HORZ_INFO 0x14 +#define CCDC_VERT_START 0x18 +#define CCDC_VERT_LINES 0x1c +#define CCDC_CULLING 0x20 +#define CCDC_HSIZE_OFF 0x24 +#define CCDC_SDOFST 0x28 +#define CCDC_SDR_ADDR 0x2c +#define CCDC_CLAMP 0x30 +#define CCDC_DCSUB 0x34 +#define CCDC_COLPTN 0x38 +#define CCDC_BLKCMP 0x3c +#define CCDC_FPC 0x40 +#define CCDC_FPC_ADDR 0x44 +#define CCDC_VDINT 0x48 +#define CCDC_ALAW 0x4c +#define CCDC_REC656IF 0x50 +#define CCDC_CCDCFG 0x54 +#define CCDC_FMTCFG 0x58 +#define CCDC_FMT_HORZ 0x5c +#define CCDC_FMT_VERT 0x60 +#define CCDC_FMT_ADDR0 0x64 +#define CCDC_FMT_ADDR1 0x68 +#define CCDC_FMT_ADDR2 0x6c +#define CCDC_FMT_ADDR3 0x70 +#define CCDC_FMT_ADDR4 0x74 +#define CCDC_FMT_ADDR5 0x78 +#define CCDC_FMT_ADDR6 0x7c +#define CCDC_FMT_ADDR7 0x80 +#define CCDC_PRGEVEN_0 0x84 +#define CCDC_PRGEVEN_1 0x88 +#define CCDC_PRGODD_0 0x8c +#define CCDC_PRGODD_1 0x90 +#define CCDC_VP_OUT 0x94 +#define CCDC_REG_END 0x98 + +/*************************************************************** +* Define for various register bit mask and shifts for CCDC +****************************************************************/ +#define CCDC_FID_POL_MASK 1 +#define CCDC_FID_POL_SHIFT 4 +#define CCDC_HD_POL_MASK 1 +#define CCDC_HD_POL_SHIFT 3 +#define CCDC_VD_POL_MASK 1 +#define CCDC_VD_POL_SHIFT 2 +#define CCDC_HSIZE_OFF_MASK 0xffffffe0 +#define CCDC_32BYTE_ALIGN_VAL 31 +#define CCDC_FRM_FMT_MASK 0x1 +#define CCDC_FRM_FMT_SHIFT 7 +#define CCDC_DATA_SZ_MASK 7 +#define CCDC_DATA_SZ_SHIFT 8 +#define CCDC_PIX_FMT_MASK 3 +#define CCDC_PIX_FMT_SHIFT 12 +#define CCDC_VP2SDR_DISABLE 0xFFFBFFFF +#define CCDC_WEN_ENABLE BIT(17) +#define CCDC_SDR2RSZ_DISABLE 0xFFF7FFFF +#define CCDC_VDHDEN_ENABLE BIT(16) +#define CCDC_LPF_ENABLE BIT(14) +#define CCDC_ALAW_ENABLE BIT(3) +#define CCDC_ALAW_GAMMA_WD_MASK 7 +#define CCDC_BLK_CLAMP_ENABLE BIT(31) +#define CCDC_BLK_SGAIN_MASK 0x1F +#define CCDC_BLK_ST_PXL_MASK 0x7FFF +#define CCDC_BLK_ST_PXL_SHIFT 10 +#define CCDC_BLK_SAMPLE_LN_MASK 7 +#define CCDC_BLK_SAMPLE_LN_SHIFT 28 +#define CCDC_BLK_SAMPLE_LINE_MASK 7 +#define CCDC_BLK_SAMPLE_LINE_SHIFT 25 +#define CCDC_BLK_DC_SUB_MASK 0x03FFF +#define CCDC_BLK_COMP_MASK 0xFF +#define CCDC_BLK_COMP_GB_COMP_SHIFT 8 +#define CCDC_BLK_COMP_GR_COMP_SHIFT 16 +#define CCDC_BLK_COMP_R_COMP_SHIFT 24 +#define CCDC_LATCH_ON_VSYNC_DISABLE BIT(15) +#define CCDC_FPC_ENABLE BIT(15) +#define CCDC_FPC_DISABLE 0 +#define CCDC_FPC_FPC_NUM_MASK 0x7FFF +#define CCDC_DATA_PACK_ENABLE BIT(11) +#define CCDC_FMTCFG_VPIN_MASK 7 +#define CCDC_FMTCFG_VPIN_SHIFT 12 +#define CCDC_FMT_HORZ_FMTLNH_MASK 0x1FFF +#define CCDC_FMT_HORZ_FMTSPH_MASK 0x1FFF +#define CCDC_FMT_HORZ_FMTSPH_SHIFT 16 +#define CCDC_FMT_VERT_FMTLNV_MASK 0x1FFF +#define CCDC_FMT_VERT_FMTSLV_MASK 0x1FFF +#define CCDC_FMT_VERT_FMTSLV_SHIFT 16 +#define CCDC_VP_OUT_VERT_NUM_MASK 0x3FFF +#define CCDC_VP_OUT_VERT_NUM_SHIFT 17 +#define CCDC_VP_OUT_HORZ_NUM_MASK 0x1FFF +#define CCDC_VP_OUT_HORZ_NUM_SHIFT 4 +#define CCDC_VP_OUT_HORZ_ST_MASK 0xF +#define CCDC_HORZ_INFO_SPH_SHIFT 16 +#define CCDC_VERT_START_SLV0_SHIFT 16 +#define CCDC_VDINT_VDINT0_SHIFT 16 +#define CCDC_VDINT_VDINT1_MASK 0xFFFF +#define CCDC_PPC_RAW 1 +#define CCDC_DCSUB_DEFAULT_VAL 0 +#define CCDC_CLAMP_DEFAULT_VAL 0 +#define CCDC_ENABLE_VIDEO_PORT 0x8000 +#define CCDC_DISABLE_VIDEO_PORT 0 +#define CCDC_COLPTN_VAL 0xBB11BB11 +#define CCDC_TWO_BYTES_PER_PIXEL 2 +#define CCDC_INTERLACED_IMAGE_INVERT 0x4B6D +#define CCDC_INTERLACED_NO_IMAGE_INVERT 0x0249 +#define CCDC_PROGRESSIVE_IMAGE_INVERT 0x4000 +#define CCDC_PROGRESSIVE_NO_IMAGE_INVERT 0 +#define CCDC_INTERLACED_HEIGHT_SHIFT 1 +#define CCDC_SYN_MODE_INPMOD_SHIFT 12 +#define CCDC_SYN_MODE_INPMOD_MASK 3 +#define CCDC_SYN_MODE_8BITS (7 << 8) +#define CCDC_SYN_MODE_10BITS (6 << 8) +#define CCDC_SYN_MODE_11BITS (5 << 8) +#define CCDC_SYN_MODE_12BITS (4 << 8) +#define CCDC_SYN_MODE_13BITS (3 << 8) +#define CCDC_SYN_MODE_14BITS (2 << 8) +#define CCDC_SYN_MODE_15BITS (1 << 8) +#define CCDC_SYN_MODE_16BITS (0 << 8) +#define CCDC_SYN_FLDMODE_MASK 1 +#define CCDC_SYN_FLDMODE_SHIFT 7 +#define CCDC_REC656IF_BT656_EN 3 +#define CCDC_SYN_MODE_VD_POL_NEGATIVE BIT(2) +#define CCDC_CCDCFG_Y8POS_SHIFT 11 +#define CCDC_CCDCFG_BW656_10BIT BIT(5) +#define CCDC_SDOFST_FIELD_INTERLEAVED 0x249 +#define CCDC_NO_CULLING 0xffff00ff +#endif diff --git a/drivers/staging/media/deprecated/vpfe_capture/isif.c b/drivers/staging/media/deprecated/vpfe_capture/isif.c new file mode 100644 index 000000000000..4059891c2824 --- /dev/null +++ b/drivers/staging/media/deprecated/vpfe_capture/isif.c @@ -0,0 +1,1127 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * Image Sensor Interface (ISIF) driver + * + * This driver is for configuring the ISIF IP available on DM365 or any other + * TI SoCs. This is used for capturing yuv or bayer video or image data + * from a decoder or sensor. This IP is similar to the CCDC IP on DM355 + * and DM6446, but with enhanced or additional ip blocks. The driver + * configures the ISIF upon commands from the vpfe bridge driver through + * ccdc_hw_device interface. + * + * TODO: 1) Raw bayer parameter settings and bayer capture + * 2) Add support for control ioctl + */ +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/uaccess.h> +#include <linux/io.h> +#include <linux/videodev2.h> +#include <linux/err.h> +#include <linux/module.h> + +#include "isif.h" +#include <media/davinci/vpss.h> + +#include "isif_regs.h" +#include "ccdc_hw_device.h" + +/* Defaults for module configuration parameters */ +static const struct isif_config_params_raw isif_config_defaults = { + .linearize = { + .en = 0, + .corr_shft = ISIF_NO_SHIFT, + .scale_fact = {1, 0}, + }, + .df_csc = { + .df_or_csc = 0, + .csc = { + .en = 0, + }, + }, + .dfc = { + .en = 0, + }, + .bclamp = { + .en = 0, + }, + .gain_offset = { + .gain = { + .r_ye = {1, 0}, + .gr_cy = {1, 0}, + .gb_g = {1, 0}, + .b_mg = {1, 0}, + }, + }, + .culling = { + .hcpat_odd = 0xff, + .hcpat_even = 0xff, + .vcpat = 0xff, + }, + .compress = { + .alg = ISIF_ALAW, + }, +}; + +/* ISIF operation configuration */ +static struct isif_oper_config { + struct device *dev; + enum vpfe_hw_if_type if_type; + struct isif_ycbcr_config ycbcr; + struct isif_params_raw bayer; + enum isif_data_pack data_pack; + /* ISIF base address */ + void __iomem *base_addr; + /* ISIF Linear Table 0 */ + void __iomem *linear_tbl0_addr; + /* ISIF Linear Table 1 */ + void __iomem *linear_tbl1_addr; +} isif_cfg = { + .ycbcr = { + .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT, + .frm_fmt = CCDC_FRMFMT_INTERLACED, + .win = ISIF_WIN_NTSC, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .pix_order = CCDC_PIXORDER_CBYCRY, + .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED, + }, + .bayer = { + .pix_fmt = CCDC_PIXFMT_RAW, + .frm_fmt = CCDC_FRMFMT_PROGRESSIVE, + .win = ISIF_WIN_VGA, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .gain = { + .r_ye = {1, 0}, + .gr_cy = {1, 0}, + .gb_g = {1, 0}, + .b_mg = {1, 0}, + }, + .cfa_pat = ISIF_CFA_PAT_MOSAIC, + .data_msb = ISIF_BIT_MSB_11, + .config_params = { + .data_shift = ISIF_NO_SHIFT, + .col_pat_field0 = { + .olop = ISIF_GREEN_BLUE, + .olep = ISIF_BLUE, + .elop = ISIF_RED, + .elep = ISIF_GREEN_RED, + }, + .col_pat_field1 = { + .olop = ISIF_GREEN_BLUE, + .olep = ISIF_BLUE, + .elop = ISIF_RED, + .elep = ISIF_GREEN_RED, + }, + .test_pat_gen = 0, + }, + }, + .data_pack = ISIF_DATA_PACK8, +}; + +/* Raw Bayer formats */ +static const u32 isif_raw_bayer_pix_formats[] = { + V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16}; + +/* Raw YUV formats */ +static const u32 isif_raw_yuv_pix_formats[] = { + V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV}; + +/* register access routines */ +static inline u32 regr(u32 offset) +{ + return __raw_readl(isif_cfg.base_addr + offset); +} + +static inline void regw(u32 val, u32 offset) +{ + __raw_writel(val, isif_cfg.base_addr + offset); +} + +/* reg_modify() - read, modify and write register */ +static inline u32 reg_modify(u32 mask, u32 val, u32 offset) +{ + u32 new_val = (regr(offset) & ~mask) | (val & mask); + + regw(new_val, offset); + return new_val; +} + +static inline void regw_lin_tbl(u32 val, u32 offset, int i) +{ + if (!i) + __raw_writel(val, isif_cfg.linear_tbl0_addr + offset); + else + __raw_writel(val, isif_cfg.linear_tbl1_addr + offset); +} + +static void isif_disable_all_modules(void) +{ + /* disable BC */ + regw(0, CLAMPCFG); + /* disable vdfc */ + regw(0, DFCCTL); + /* disable CSC */ + regw(0, CSCCTL); + /* disable linearization */ + regw(0, LINCFG0); + /* disable other modules here as they are supported */ +} + +static void isif_enable(int en) +{ + if (!en) { + /* Before disable isif, disable all ISIF modules */ + isif_disable_all_modules(); + /* + * wait for next VD. Assume lowest scan rate is 12 Hz. So + * 100 msec delay is good enough + */ + msleep(100); + } + reg_modify(ISIF_SYNCEN_VDHDEN_MASK, en, SYNCEN); +} + +static void isif_enable_output_to_sdram(int en) +{ + reg_modify(ISIF_SYNCEN_WEN_MASK, en << ISIF_SYNCEN_WEN_SHIFT, SYNCEN); +} + +static void isif_config_culling(struct isif_cul *cul) +{ + u32 val; + + /* Horizontal pattern */ + val = (cul->hcpat_even << CULL_PAT_EVEN_LINE_SHIFT) | cul->hcpat_odd; + regw(val, CULH); + + /* vertical pattern */ + regw(cul->vcpat, CULV); + + /* LPF */ + reg_modify(ISIF_LPF_MASK << ISIF_LPF_SHIFT, + cul->en_lpf << ISIF_LPF_SHIFT, MODESET); +} + +static void isif_config_gain_offset(void) +{ + struct isif_gain_offsets_adj *gain_off_p = + &isif_cfg.bayer.config_params.gain_offset; + u32 val; + + val = (!!gain_off_p->gain_sdram_en << GAIN_SDRAM_EN_SHIFT) | + (!!gain_off_p->gain_ipipe_en << GAIN_IPIPE_EN_SHIFT) | + (!!gain_off_p->gain_h3a_en << GAIN_H3A_EN_SHIFT) | + (!!gain_off_p->offset_sdram_en << OFST_SDRAM_EN_SHIFT) | + (!!gain_off_p->offset_ipipe_en << OFST_IPIPE_EN_SHIFT) | + (!!gain_off_p->offset_h3a_en << OFST_H3A_EN_SHIFT); + + reg_modify(GAIN_OFFSET_EN_MASK, val, CGAMMAWD); + + val = (gain_off_p->gain.r_ye.integer << GAIN_INTEGER_SHIFT) | + gain_off_p->gain.r_ye.decimal; + regw(val, CRGAIN); + + val = (gain_off_p->gain.gr_cy.integer << GAIN_INTEGER_SHIFT) | + gain_off_p->gain.gr_cy.decimal; + regw(val, CGRGAIN); + + val = (gain_off_p->gain.gb_g.integer << GAIN_INTEGER_SHIFT) | + gain_off_p->gain.gb_g.decimal; + regw(val, CGBGAIN); + + val = (gain_off_p->gain.b_mg.integer << GAIN_INTEGER_SHIFT) | + gain_off_p->gain.b_mg.decimal; + regw(val, CBGAIN); + + regw(gain_off_p->offset, COFSTA); +} + +static void isif_restore_defaults(void) +{ + enum vpss_ccdc_source_sel source = VPSS_CCDCIN; + + dev_dbg(isif_cfg.dev, "\nstarting isif_restore_defaults..."); + isif_cfg.bayer.config_params = isif_config_defaults; + /* Enable clock to ISIF, IPIPEIF and BL */ + vpss_enable_clock(VPSS_CCDC_CLOCK, 1); + vpss_enable_clock(VPSS_IPIPEIF_CLOCK, 1); + vpss_enable_clock(VPSS_BL_CLOCK, 1); + /* Set default offset and gain */ + isif_config_gain_offset(); + vpss_select_ccdc_source(source); + dev_dbg(isif_cfg.dev, "\nEnd of isif_restore_defaults..."); +} + +static int isif_open(struct device *device) +{ + isif_restore_defaults(); + return 0; +} + +/* This function will configure the window size to be capture in ISIF reg */ +static void isif_setwin(struct v4l2_rect *image_win, + enum ccdc_frmfmt frm_fmt, int ppc) +{ + int horz_start, horz_nr_pixels; + int vert_start, vert_nr_lines; + int mid_img = 0; + + dev_dbg(isif_cfg.dev, "\nStarting isif_setwin..."); + /* + * ppc - per pixel count. indicates how many pixels per cell + * output to SDRAM. example, for ycbcr, it is one y and one c, so 2. + * raw capture this is 1 + */ + horz_start = image_win->left << (ppc - 1); + horz_nr_pixels = ((image_win->width) << (ppc - 1)) - 1; + + /* Writing the horizontal info into the registers */ + regw(horz_start & START_PX_HOR_MASK, SPH); + regw(horz_nr_pixels & NUM_PX_HOR_MASK, LNH); + vert_start = image_win->top; + + if (frm_fmt == CCDC_FRMFMT_INTERLACED) { + vert_nr_lines = (image_win->height >> 1) - 1; + vert_start >>= 1; + /* To account for VD since line 0 doesn't have any data */ + vert_start += 1; + } else { + /* To account for VD since line 0 doesn't have any data */ + vert_start += 1; + vert_nr_lines = image_win->height - 1; + /* configure VDINT0 and VDINT1 */ + mid_img = vert_start + (image_win->height / 2); + regw(mid_img, VDINT1); + } + + regw(0, VDINT0); + regw(vert_start & START_VER_ONE_MASK, SLV0); + regw(vert_start & START_VER_TWO_MASK, SLV1); + regw(vert_nr_lines & NUM_LINES_VER, LNV); +} + +static void isif_config_bclamp(struct isif_black_clamp *bc) +{ + u32 val; + + /* + * DC Offset is always added to image data irrespective of bc enable + * status + */ + regw(bc->dc_offset, CLDCOFST); + + if (bc->en) { + val = bc->bc_mode_color << ISIF_BC_MODE_COLOR_SHIFT; + + /* Enable BC and horizontal clamp calculation parameters */ + val = val | 1 | (bc->horz.mode << ISIF_HORZ_BC_MODE_SHIFT); + + regw(val, CLAMPCFG); + + if (bc->horz.mode != ISIF_HORZ_BC_DISABLE) { + /* + * Window count for calculation + * Base window selection + * pixel limit + * Horizontal size of window + * vertical size of the window + * Horizontal start position of the window + * Vertical start position of the window + */ + val = bc->horz.win_count_calc | + ((!!bc->horz.base_win_sel_calc) << + ISIF_HORZ_BC_WIN_SEL_SHIFT) | + ((!!bc->horz.clamp_pix_limit) << + ISIF_HORZ_BC_PIX_LIMIT_SHIFT) | + (bc->horz.win_h_sz_calc << + ISIF_HORZ_BC_WIN_H_SIZE_SHIFT) | + (bc->horz.win_v_sz_calc << + ISIF_HORZ_BC_WIN_V_SIZE_SHIFT); + regw(val, CLHWIN0); + + regw(bc->horz.win_start_h_calc, CLHWIN1); + regw(bc->horz.win_start_v_calc, CLHWIN2); + } + + /* vertical clamp calculation parameters */ + + /* Reset clamp value sel for previous line */ + val |= + (bc->vert.reset_val_sel << ISIF_VERT_BC_RST_VAL_SEL_SHIFT) | + (bc->vert.line_ave_coef << ISIF_VERT_BC_LINE_AVE_COEF_SHIFT); + regw(val, CLVWIN0); + + /* Optical Black horizontal start position */ + regw(bc->vert.ob_start_h, CLVWIN1); + /* Optical Black vertical start position */ + regw(bc->vert.ob_start_v, CLVWIN2); + /* Optical Black vertical size for calculation */ + regw(bc->vert.ob_v_sz_calc, CLVWIN3); + /* Vertical start position for BC subtraction */ + regw(bc->vert_start_sub, CLSV); + } +} + +static void isif_config_linearization(struct isif_linearize *linearize) +{ + u32 val, i; + + if (!linearize->en) { + regw(0, LINCFG0); + return; + } + + /* shift value for correction & enable linearization (set lsb) */ + val = (linearize->corr_shft << ISIF_LIN_CORRSFT_SHIFT) | 1; + regw(val, LINCFG0); + + /* Scale factor */ + val = ((!!linearize->scale_fact.integer) << + ISIF_LIN_SCALE_FACT_INTEG_SHIFT) | + linearize->scale_fact.decimal; + regw(val, LINCFG1); + + for (i = 0; i < ISIF_LINEAR_TAB_SIZE; i++) { + if (i % 2) + regw_lin_tbl(linearize->table[i], ((i >> 1) << 2), 1); + else + regw_lin_tbl(linearize->table[i], ((i >> 1) << 2), 0); + } +} + +static int isif_config_dfc(struct isif_dfc *vdfc) +{ + /* initialize retries to loop for max ~ 250 usec */ + u32 val, count, retries = loops_per_jiffy / (4000/HZ); + int i; + + if (!vdfc->en) + return 0; + + /* Correction mode */ + val = (vdfc->corr_mode << ISIF_VDFC_CORR_MOD_SHIFT); + + /* Correct whole line or partial */ + if (vdfc->corr_whole_line) + val |= 1 << ISIF_VDFC_CORR_WHOLE_LN_SHIFT; + + /* level shift value */ + val |= vdfc->def_level_shift << ISIF_VDFC_LEVEL_SHFT_SHIFT; + + regw(val, DFCCTL); + + /* Defect saturation level */ + regw(vdfc->def_sat_level, VDFSATLV); + + regw(vdfc->table[0].pos_vert, DFCMEM0); + regw(vdfc->table[0].pos_horz, DFCMEM1); + if (vdfc->corr_mode == ISIF_VDFC_NORMAL || + vdfc->corr_mode == ISIF_VDFC_HORZ_INTERPOL_IF_SAT) { + regw(vdfc->table[0].level_at_pos, DFCMEM2); + regw(vdfc->table[0].level_up_pixels, DFCMEM3); + regw(vdfc->table[0].level_low_pixels, DFCMEM4); + } + + /* set DFCMARST and set DFCMWR */ + val = regr(DFCMEMCTL) | (1 << ISIF_DFCMEMCTL_DFCMARST_SHIFT) | 1; + regw(val, DFCMEMCTL); + + count = retries; + while (count && (regr(DFCMEMCTL) & 0x1)) + count--; + + if (!count) { + dev_dbg(isif_cfg.dev, "defect table write timeout !!!\n"); + return -1; + } + + for (i = 1; i < vdfc->num_vdefects; i++) { + regw(vdfc->table[i].pos_vert, DFCMEM0); + regw(vdfc->table[i].pos_horz, DFCMEM1); + if (vdfc->corr_mode == ISIF_VDFC_NORMAL || + vdfc->corr_mode == ISIF_VDFC_HORZ_INTERPOL_IF_SAT) { + regw(vdfc->table[i].level_at_pos, DFCMEM2); + regw(vdfc->table[i].level_up_pixels, DFCMEM3); + regw(vdfc->table[i].level_low_pixels, DFCMEM4); + } + val = regr(DFCMEMCTL); + /* clear DFCMARST and set DFCMWR */ + val &= ~BIT(ISIF_DFCMEMCTL_DFCMARST_SHIFT); + val |= 1; + regw(val, DFCMEMCTL); + + count = retries; + while (count && (regr(DFCMEMCTL) & 0x1)) + count--; + + if (!count) { + dev_err(isif_cfg.dev, + "defect table write timeout !!!\n"); + return -1; + } + } + if (vdfc->num_vdefects < ISIF_VDFC_TABLE_SIZE) { + /* Extra cycle needed */ + regw(0, DFCMEM0); + regw(0x1FFF, DFCMEM1); + regw(1, DFCMEMCTL); + } + + /* enable VDFC */ + reg_modify((1 << ISIF_VDFC_EN_SHIFT), (1 << ISIF_VDFC_EN_SHIFT), + DFCCTL); + return 0; +} + +static void isif_config_csc(struct isif_df_csc *df_csc) +{ + u32 val1 = 0, val2 = 0, i; + + if (!df_csc->csc.en) { + regw(0, CSCCTL); + return; + } + for (i = 0; i < ISIF_CSC_NUM_COEFF; i++) { + if ((i % 2) == 0) { + /* CSCM - LSB */ + val1 = (df_csc->csc.coeff[i].integer << + ISIF_CSC_COEF_INTEG_SHIFT) | + df_csc->csc.coeff[i].decimal; + } else { + + /* CSCM - MSB */ + val2 = (df_csc->csc.coeff[i].integer << + ISIF_CSC_COEF_INTEG_SHIFT) | + df_csc->csc.coeff[i].decimal; + val2 <<= ISIF_CSCM_MSB_SHIFT; + val2 |= val1; + regw(val2, (CSCM0 + ((i - 1) << 1))); + } + } + + /* program the active area */ + regw(df_csc->start_pix, FMTSPH); + /* + * one extra pixel as required for CSC. Actually number of + * pixel - 1 should be configured in this register. So we + * need to subtract 1 before writing to FMTSPH, but we will + * not do this since csc requires one extra pixel + */ + regw(df_csc->num_pixels, FMTLNH); + regw(df_csc->start_line, FMTSLV); + /* + * one extra line as required for CSC. See reason documented for + * num_pixels + */ + regw(df_csc->num_lines, FMTLNV); + + /* Enable CSC */ + regw(1, CSCCTL); +} + +static int isif_config_raw(void) +{ + struct isif_params_raw *params = &isif_cfg.bayer; + struct isif_config_params_raw *module_params = + &isif_cfg.bayer.config_params; + struct vpss_pg_frame_size frame_size; + struct vpss_sync_pol sync; + u32 val; + + dev_dbg(isif_cfg.dev, "\nStarting isif_config_raw..\n"); + + /* + * Configure CCDCFG register:- + * Set CCD Not to swap input since input is RAW data + * Set FID detection function to Latch at V-Sync + * Set WENLOG - isif valid area + * Set TRGSEL + * Set EXTRG + * Packed to 8 or 16 bits + */ + + val = ISIF_YCINSWP_RAW | ISIF_CCDCFG_FIDMD_LATCH_VSYNC | + ISIF_CCDCFG_WENLOG_AND | ISIF_CCDCFG_TRGSEL_WEN | + ISIF_CCDCFG_EXTRG_DISABLE | isif_cfg.data_pack; + + dev_dbg(isif_cfg.dev, "Writing 0x%x to ...CCDCFG \n", val); + regw(val, CCDCFG); + + /* + * Configure the vertical sync polarity(MODESET.VDPOL) + * Configure the horizontal sync polarity (MODESET.HDPOL) + * Configure frame id polarity (MODESET.FLDPOL) + * Configure data polarity + * Configure External WEN Selection + * Configure frame format(progressive or interlace) + * Configure pixel format (Input mode) + * Configure the data shift + */ + + val = ISIF_VDHDOUT_INPUT | (params->vd_pol << ISIF_VD_POL_SHIFT) | + (params->hd_pol << ISIF_HD_POL_SHIFT) | + (params->fid_pol << ISIF_FID_POL_SHIFT) | + (ISIF_DATAPOL_NORMAL << ISIF_DATAPOL_SHIFT) | + (ISIF_EXWEN_DISABLE << ISIF_EXWEN_SHIFT) | + (params->frm_fmt << ISIF_FRM_FMT_SHIFT) | + (params->pix_fmt << ISIF_INPUT_SHIFT) | + (params->config_params.data_shift << ISIF_DATASFT_SHIFT); + + regw(val, MODESET); + dev_dbg(isif_cfg.dev, "Writing 0x%x to MODESET...\n", val); + + /* + * Configure GAMMAWD register + * CFA pattern setting + */ + val = params->cfa_pat << ISIF_GAMMAWD_CFA_SHIFT; + + /* Gamma msb */ + if (module_params->compress.alg == ISIF_ALAW) + val |= ISIF_ALAW_ENABLE; + + val |= (params->data_msb << ISIF_ALAW_GAMMA_WD_SHIFT); + regw(val, CGAMMAWD); + + /* Configure DPCM compression settings */ + if (module_params->compress.alg == ISIF_DPCM) { + val = BIT(ISIF_DPCM_EN_SHIFT) | + (module_params->compress.pred << + ISIF_DPCM_PREDICTOR_SHIFT); + } + + regw(val, MISC); + + /* Configure Gain & Offset */ + isif_config_gain_offset(); + + /* Configure Color pattern */ + val = (params->config_params.col_pat_field0.olop) | + (params->config_params.col_pat_field0.olep << 2) | + (params->config_params.col_pat_field0.elop << 4) | + (params->config_params.col_pat_field0.elep << 6) | + (params->config_params.col_pat_field1.olop << 8) | + (params->config_params.col_pat_field1.olep << 10) | + (params->config_params.col_pat_field1.elop << 12) | + (params->config_params.col_pat_field1.elep << 14); + regw(val, CCOLP); + dev_dbg(isif_cfg.dev, "Writing %x to CCOLP ...\n", val); + + /* Configure HSIZE register */ + val = (!!params->horz_flip_en) << ISIF_HSIZE_FLIP_SHIFT; + + /* calculate line offset in 32 bytes based on pack value */ + if (isif_cfg.data_pack == ISIF_PACK_8BIT) + val |= ((params->win.width + 31) >> 5); + else if (isif_cfg.data_pack == ISIF_PACK_12BIT) + val |= (((params->win.width + + (params->win.width >> 2)) + 31) >> 5); + else + val |= (((params->win.width * 2) + 31) >> 5); + regw(val, HSIZE); + + /* Configure SDOFST register */ + if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) { + if (params->image_invert_en) { + /* For interlace inverse mode */ + regw(0x4B6D, SDOFST); + dev_dbg(isif_cfg.dev, "Writing 0x4B6D to SDOFST...\n"); + } else { + /* For interlace non inverse mode */ + regw(0x0B6D, SDOFST); + dev_dbg(isif_cfg.dev, "Writing 0x0B6D to SDOFST...\n"); + } + } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) { + if (params->image_invert_en) { + /* For progressive inverse mode */ + regw(0x4000, SDOFST); + dev_dbg(isif_cfg.dev, "Writing 0x4000 to SDOFST...\n"); + } else { + /* For progressive non inverse mode */ + regw(0x0000, SDOFST); + dev_dbg(isif_cfg.dev, "Writing 0x0000 to SDOFST...\n"); + } + } + + /* Configure video window */ + isif_setwin(¶ms->win, params->frm_fmt, 1); + + /* Configure Black Clamp */ + isif_config_bclamp(&module_params->bclamp); + + /* Configure Vertical Defection Pixel Correction */ + if (isif_config_dfc(&module_params->dfc) < 0) + return -EFAULT; + + if (!module_params->df_csc.df_or_csc) + /* Configure Color Space Conversion */ + isif_config_csc(&module_params->df_csc); + + isif_config_linearization(&module_params->linearize); + + /* Configure Culling */ + isif_config_culling(&module_params->culling); + + /* Configure horizontal and vertical offsets(DFC,LSC,Gain) */ + regw(module_params->horz_offset, DATAHOFST); + regw(module_params->vert_offset, DATAVOFST); + + /* Setup test pattern if enabled */ + if (params->config_params.test_pat_gen) { + /* Use the HD/VD pol settings from user */ + sync.ccdpg_hdpol = params->hd_pol; + sync.ccdpg_vdpol = params->vd_pol; + dm365_vpss_set_sync_pol(sync); + frame_size.hlpfr = isif_cfg.bayer.win.width; + frame_size.pplen = isif_cfg.bayer.win.height; + dm365_vpss_set_pg_frame_size(frame_size); + vpss_select_ccdc_source(VPSS_PGLPBK); + } + + dev_dbg(isif_cfg.dev, "\nEnd of isif_config_ycbcr...\n"); + return 0; +} + +static int isif_set_buftype(enum ccdc_buftype buf_type) +{ + if (isif_cfg.if_type == VPFE_RAW_BAYER) + isif_cfg.bayer.buf_type = buf_type; + else + isif_cfg.ycbcr.buf_type = buf_type; + + return 0; + +} +static enum ccdc_buftype isif_get_buftype(void) +{ + if (isif_cfg.if_type == VPFE_RAW_BAYER) + return isif_cfg.bayer.buf_type; + + return isif_cfg.ycbcr.buf_type; +} + +static int isif_enum_pix(u32 *pix, int i) +{ + int ret = -EINVAL; + + if (isif_cfg.if_type == VPFE_RAW_BAYER) { + if (i < ARRAY_SIZE(isif_raw_bayer_pix_formats)) { + *pix = isif_raw_bayer_pix_formats[i]; + ret = 0; + } + } else { + if (i < ARRAY_SIZE(isif_raw_yuv_pix_formats)) { + *pix = isif_raw_yuv_pix_formats[i]; + ret = 0; + } + } + + return ret; +} + +static int isif_set_pixel_format(unsigned int pixfmt) +{ + if (isif_cfg.if_type == VPFE_RAW_BAYER) { + if (pixfmt == V4L2_PIX_FMT_SBGGR8) { + if ((isif_cfg.bayer.config_params.compress.alg != + ISIF_ALAW) && + (isif_cfg.bayer.config_params.compress.alg != + ISIF_DPCM)) { + dev_dbg(isif_cfg.dev, + "Either configure A-Law or DPCM\n"); + return -EINVAL; + } + isif_cfg.data_pack = ISIF_PACK_8BIT; + } else if (pixfmt == V4L2_PIX_FMT_SBGGR16) { + isif_cfg.bayer.config_params.compress.alg = + ISIF_NO_COMPRESSION; + isif_cfg.data_pack = ISIF_PACK_16BIT; + } else + return -EINVAL; + isif_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; + } else { + if (pixfmt == V4L2_PIX_FMT_YUYV) + isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR; + else if (pixfmt == V4L2_PIX_FMT_UYVY) + isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; + else + return -EINVAL; + isif_cfg.data_pack = ISIF_PACK_8BIT; + } + return 0; +} + +static u32 isif_get_pixel_format(void) +{ + u32 pixfmt; + + if (isif_cfg.if_type == VPFE_RAW_BAYER) + if (isif_cfg.bayer.config_params.compress.alg == ISIF_ALAW || + isif_cfg.bayer.config_params.compress.alg == ISIF_DPCM) + pixfmt = V4L2_PIX_FMT_SBGGR8; + else + pixfmt = V4L2_PIX_FMT_SBGGR16; + else { + if (isif_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR) + pixfmt = V4L2_PIX_FMT_YUYV; + else + pixfmt = V4L2_PIX_FMT_UYVY; + } + return pixfmt; +} + +static int isif_set_image_window(struct v4l2_rect *win) +{ + if (isif_cfg.if_type == VPFE_RAW_BAYER) { + isif_cfg.bayer.win.top = win->top; + isif_cfg.bayer.win.left = win->left; + isif_cfg.bayer.win.width = win->width; + isif_cfg.bayer.win.height = win->height; + } else { + isif_cfg.ycbcr.win.top = win->top; + isif_cfg.ycbcr.win.left = win->left; + isif_cfg.ycbcr.win.width = win->width; + isif_cfg.ycbcr.win.height = win->height; + } + return 0; +} + +static void isif_get_image_window(struct v4l2_rect *win) +{ + if (isif_cfg.if_type == VPFE_RAW_BAYER) + *win = isif_cfg.bayer.win; + else + *win = isif_cfg.ycbcr.win; +} + +static unsigned int isif_get_line_length(void) +{ + unsigned int len; + + if (isif_cfg.if_type == VPFE_RAW_BAYER) { + if (isif_cfg.data_pack == ISIF_PACK_8BIT) + len = ((isif_cfg.bayer.win.width)); + else if (isif_cfg.data_pack == ISIF_PACK_12BIT) + len = (((isif_cfg.bayer.win.width * 2) + + (isif_cfg.bayer.win.width >> 2))); + else + len = (((isif_cfg.bayer.win.width * 2))); + } else + len = (((isif_cfg.ycbcr.win.width * 2))); + return ALIGN(len, 32); +} + +static int isif_set_frame_format(enum ccdc_frmfmt frm_fmt) +{ + if (isif_cfg.if_type == VPFE_RAW_BAYER) + isif_cfg.bayer.frm_fmt = frm_fmt; + else + isif_cfg.ycbcr.frm_fmt = frm_fmt; + return 0; +} +static enum ccdc_frmfmt isif_get_frame_format(void) +{ + if (isif_cfg.if_type == VPFE_RAW_BAYER) + return isif_cfg.bayer.frm_fmt; + return isif_cfg.ycbcr.frm_fmt; +} + +static int isif_getfid(void) +{ + return (regr(MODESET) >> 15) & 0x1; +} + +/* misc operations */ +static void isif_setfbaddr(unsigned long addr) +{ + regw((addr >> 21) & 0x07ff, CADU); + regw((addr >> 5) & 0x0ffff, CADL); +} + +static int isif_set_hw_if_params(struct vpfe_hw_if_param *params) +{ + isif_cfg.if_type = params->if_type; + + switch (params->if_type) { + case VPFE_BT656: + case VPFE_BT656_10BIT: + case VPFE_YCBCR_SYNC_8: + isif_cfg.ycbcr.pix_fmt = CCDC_PIXFMT_YCBCR_8BIT; + isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; + break; + case VPFE_BT1120: + case VPFE_YCBCR_SYNC_16: + isif_cfg.ycbcr.pix_fmt = CCDC_PIXFMT_YCBCR_16BIT; + isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; + break; + case VPFE_RAW_BAYER: + isif_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; + break; + default: + dev_dbg(isif_cfg.dev, "Invalid interface type\n"); + return -EINVAL; + } + + return 0; +} + +/* This function will configure ISIF for YCbCr parameters. */ +static int isif_config_ycbcr(void) +{ + struct isif_ycbcr_config *params = &isif_cfg.ycbcr; + u32 modeset = 0, ccdcfg = 0; + + dev_dbg(isif_cfg.dev, "\nStarting isif_config_ycbcr..."); + + /* configure pixel format or input mode */ + modeset = modeset | (params->pix_fmt << ISIF_INPUT_SHIFT) | + (params->frm_fmt << ISIF_FRM_FMT_SHIFT) | + (params->fid_pol << ISIF_FID_POL_SHIFT) | + (params->hd_pol << ISIF_HD_POL_SHIFT) | + (params->vd_pol << ISIF_VD_POL_SHIFT); + + /* pack the data to 8-bit ISIFCFG */ + switch (isif_cfg.if_type) { + case VPFE_BT656: + if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) { + dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + modeset |= (VPFE_PINPOL_NEGATIVE << ISIF_VD_POL_SHIFT); + regw(3, REC656IF); + ccdcfg = ccdcfg | ISIF_DATA_PACK8 | ISIF_YCINSWP_YCBCR; + break; + case VPFE_BT656_10BIT: + if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) { + dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + /* setup BT.656, embedded sync */ + regw(3, REC656IF); + /* enable 10 bit mode in ccdcfg */ + ccdcfg = ccdcfg | ISIF_DATA_PACK8 | ISIF_YCINSWP_YCBCR | + ISIF_BW656_ENABLE; + break; + case VPFE_BT1120: + if (params->pix_fmt != CCDC_PIXFMT_YCBCR_16BIT) { + dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + regw(3, REC656IF); + break; + + case VPFE_YCBCR_SYNC_8: + ccdcfg |= ISIF_DATA_PACK8; + ccdcfg |= ISIF_YCINSWP_YCBCR; + if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) { + dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + break; + case VPFE_YCBCR_SYNC_16: + if (params->pix_fmt != CCDC_PIXFMT_YCBCR_16BIT) { + dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + break; + default: + /* should never come here */ + dev_dbg(isif_cfg.dev, "Invalid interface type\n"); + return -EINVAL; + } + + regw(modeset, MODESET); + + /* Set up pix order */ + ccdcfg |= params->pix_order << ISIF_PIX_ORDER_SHIFT; + + regw(ccdcfg, CCDCFG); + + /* configure video window */ + if ((isif_cfg.if_type == VPFE_BT1120) || + (isif_cfg.if_type == VPFE_YCBCR_SYNC_16)) + isif_setwin(¶ms->win, params->frm_fmt, 1); + else + isif_setwin(¶ms->win, params->frm_fmt, 2); + + /* + * configure the horizontal line offset + * this is done by rounding up width to a multiple of 16 pixels + * and multiply by two to account for y:cb:cr 4:2:2 data + */ + regw(((((params->win.width * 2) + 31) & 0xffffffe0) >> 5), HSIZE); + + /* configure the memory line offset */ + if ((params->frm_fmt == CCDC_FRMFMT_INTERLACED) && + (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED)) + /* two fields are interleaved in memory */ + regw(0x00000249, SDOFST); + + return 0; +} + +static int isif_configure(void) +{ + if (isif_cfg.if_type == VPFE_RAW_BAYER) + return isif_config_raw(); + return isif_config_ycbcr(); +} + +static int isif_close(struct device *device) +{ + /* copy defaults to module params */ + isif_cfg.bayer.config_params = isif_config_defaults; + return 0; +} + +static const struct ccdc_hw_device isif_hw_dev = { + .name = "ISIF", + .owner = THIS_MODULE, + .hw_ops = { + .open = isif_open, + .close = isif_close, + .enable = isif_enable, + .enable_out_to_sdram = isif_enable_output_to_sdram, + .set_hw_if_params = isif_set_hw_if_params, + .configure = isif_configure, + .set_buftype = isif_set_buftype, + .get_buftype = isif_get_buftype, + .enum_pix = isif_enum_pix, + .set_pixel_format = isif_set_pixel_format, + .get_pixel_format = isif_get_pixel_format, + .set_frame_format = isif_set_frame_format, + .get_frame_format = isif_get_frame_format, + .set_image_window = isif_set_image_window, + .get_image_window = isif_get_image_window, + .get_line_length = isif_get_line_length, + .setfbaddr = isif_setfbaddr, + .getfid = isif_getfid, + }, +}; + +static int isif_probe(struct platform_device *pdev) +{ + void (*setup_pinmux)(void); + struct resource *res; + void __iomem *addr; + int status = 0, i; + + /* Platform data holds setup_pinmux function ptr */ + if (!pdev->dev.platform_data) + return -ENODEV; + + /* + * first try to register with vpfe. If not correct platform, then we + * don't have to iomap + */ + status = vpfe_register_ccdc_device(&isif_hw_dev); + if (status < 0) + return status; + + setup_pinmux = pdev->dev.platform_data; + /* + * setup Mux configuration for ccdc which may be different for + * different SoCs using this CCDC + */ + setup_pinmux(); + + i = 0; + /* Get the ISIF base address, linearization table0 and table1 addr. */ + while (i < 3) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (!res) { + status = -ENODEV; + goto fail_nobase_res; + } + res = request_mem_region(res->start, resource_size(res), + res->name); + if (!res) { + status = -EBUSY; + goto fail_nobase_res; + } + addr = ioremap(res->start, resource_size(res)); + if (!addr) { + status = -ENOMEM; + goto fail_base_iomap; + } + switch (i) { + case 0: + /* ISIF base address */ + isif_cfg.base_addr = addr; + break; + case 1: + /* ISIF linear tbl0 address */ + isif_cfg.linear_tbl0_addr = addr; + break; + default: + /* ISIF linear tbl0 address */ + isif_cfg.linear_tbl1_addr = addr; + break; + } + i++; + } + isif_cfg.dev = &pdev->dev; + + printk(KERN_NOTICE "%s is registered with vpfe.\n", + isif_hw_dev.name); + return 0; +fail_base_iomap: + release_mem_region(res->start, resource_size(res)); + i--; +fail_nobase_res: + if (isif_cfg.base_addr) { + iounmap(isif_cfg.base_addr); + isif_cfg.base_addr = NULL; + } + if (isif_cfg.linear_tbl0_addr) { + iounmap(isif_cfg.linear_tbl0_addr); + isif_cfg.linear_tbl0_addr = NULL; + } + + while (i >= 0) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (res) + release_mem_region(res->start, resource_size(res)); + i--; + } + vpfe_unregister_ccdc_device(&isif_hw_dev); + return status; +} + +static int isif_remove(struct platform_device *pdev) +{ + struct resource *res; + int i = 0; + + iounmap(isif_cfg.base_addr); + isif_cfg.base_addr = NULL; + iounmap(isif_cfg.linear_tbl0_addr); + isif_cfg.linear_tbl0_addr = NULL; + iounmap(isif_cfg.linear_tbl1_addr); + isif_cfg.linear_tbl1_addr = NULL; + while (i < 3) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + release_mem_region(res->start, resource_size(res)); + i++; + } + vpfe_unregister_ccdc_device(&isif_hw_dev); + return 0; +} + +static struct platform_driver isif_driver = { + .driver = { + .name = "isif", + }, + .remove = isif_remove, + .probe = isif_probe, +}; + +module_platform_driver(isif_driver); + +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/deprecated/vpfe_capture/isif.h b/drivers/staging/media/deprecated/vpfe_capture/isif.h new file mode 100644 index 000000000000..8369acd26e7e --- /dev/null +++ b/drivers/staging/media/deprecated/vpfe_capture/isif.h @@ -0,0 +1,518 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * isif header file + */ +#ifndef _ISIF_H +#define _ISIF_H + +#include <media/davinci/ccdc_types.h> +#include <media/davinci/vpfe_types.h> + +/* isif float type S8Q8/U8Q8 */ +struct isif_float_8 { + /* 8 bit integer part */ + __u8 integer; + /* 8 bit decimal part */ + __u8 decimal; +}; + +/* isif float type U16Q16/S16Q16 */ +struct isif_float_16 { + /* 16 bit integer part */ + __u16 integer; + /* 16 bit decimal part */ + __u16 decimal; +}; + +/************************************************************************ + * Vertical Defect Correction parameters + ***********************************************************************/ +/* Defect Correction (DFC) table entry */ +struct isif_vdfc_entry { + /* vertical position of defect */ + __u16 pos_vert; + /* horizontal position of defect */ + __u16 pos_horz; + /* + * Defect level of Vertical line defect position. This is subtracted + * from the data at the defect position + */ + __u8 level_at_pos; + /* + * Defect level of the pixels upper than the vertical line defect. + * This is subtracted from the data + */ + __u8 level_up_pixels; + /* + * Defect level of the pixels lower than the vertical line defect. + * This is subtracted from the data + */ + __u8 level_low_pixels; +}; + +#define ISIF_VDFC_TABLE_SIZE 8 +struct isif_dfc { + /* enable vertical defect correction */ + __u8 en; + /* Defect level subtraction. Just fed through if saturating */ +#define ISIF_VDFC_NORMAL 0 + /* + * Defect level subtraction. Horizontal interpolation ((i-2)+(i+2))/2 + * if data saturating + */ +#define ISIF_VDFC_HORZ_INTERPOL_IF_SAT 1 + /* Horizontal interpolation (((i-2)+(i+2))/2) */ +#define ISIF_VDFC_HORZ_INTERPOL 2 + /* one of the vertical defect correction modes above */ + __u8 corr_mode; + /* 0 - whole line corrected, 1 - not pixels upper than the defect */ + __u8 corr_whole_line; +#define ISIF_VDFC_NO_SHIFT 0 +#define ISIF_VDFC_SHIFT_1 1 +#define ISIF_VDFC_SHIFT_2 2 +#define ISIF_VDFC_SHIFT_3 3 +#define ISIF_VDFC_SHIFT_4 4 + /* + * defect level shift value. level_at_pos, level_upper_pos, + * and level_lower_pos can be shifted up by this value. Choose + * one of the values above + */ + __u8 def_level_shift; + /* defect saturation level */ + __u16 def_sat_level; + /* number of vertical defects. Max is ISIF_VDFC_TABLE_SIZE */ + __u16 num_vdefects; + /* VDFC table ptr */ + struct isif_vdfc_entry table[ISIF_VDFC_TABLE_SIZE]; +}; + +struct isif_horz_bclamp { + + /* Horizontal clamp disabled. Only vertical clamp value is subtracted */ +#define ISIF_HORZ_BC_DISABLE 0 + /* + * Horizontal clamp value is calculated and subtracted from image data + * along with vertical clamp value + */ +#define ISIF_HORZ_BC_CLAMP_CALC_ENABLED 1 + /* + * Horizontal clamp value calculated from previous image is subtracted + * from image data along with vertical clamp value. + */ +#define ISIF_HORZ_BC_CLAMP_NOT_UPDATED 2 + /* horizontal clamp mode. One of the values above */ + __u8 mode; + /* + * pixel value limit enable. + * 0 - limit disabled + * 1 - pixel value limited to 1023 + */ + __u8 clamp_pix_limit; + /* Select Most left window for bc calculation */ +#define ISIF_SEL_MOST_LEFT_WIN 0 + /* Select Most right window for bc calculation */ +#define ISIF_SEL_MOST_RIGHT_WIN 1 + /* Select most left or right window for clamp val calculation */ + __u8 base_win_sel_calc; + /* Window count per color for calculation. range 1-32 */ + __u8 win_count_calc; + /* Window start position - horizontal for calculation. 0 - 8191 */ + __u16 win_start_h_calc; + /* Window start position - vertical for calculation 0 - 8191 */ + __u16 win_start_v_calc; +#define ISIF_HORZ_BC_SZ_H_2PIXELS 0 +#define ISIF_HORZ_BC_SZ_H_4PIXELS 1 +#define ISIF_HORZ_BC_SZ_H_8PIXELS 2 +#define ISIF_HORZ_BC_SZ_H_16PIXELS 3 + /* Width of the sample window in pixels for calculation */ + __u8 win_h_sz_calc; +#define ISIF_HORZ_BC_SZ_V_32PIXELS 0 +#define ISIF_HORZ_BC_SZ_V_64PIXELS 1 +#define ISIF_HORZ_BC_SZ_V_128PIXELS 2 +#define ISIF_HORZ_BC_SZ_V_256PIXELS 3 + /* Height of the sample window in pixels for calculation */ + __u8 win_v_sz_calc; +}; + +/************************************************************************ + * Black Clamp parameters + ***********************************************************************/ +struct isif_vert_bclamp { + /* Reset value used is the clamp value calculated */ +#define ISIF_VERT_BC_USE_HORZ_CLAMP_VAL 0 + /* Reset value used is reset_clamp_val configured */ +#define ISIF_VERT_BC_USE_CONFIG_CLAMP_VAL 1 + /* No update, previous image value is used */ +#define ISIF_VERT_BC_NO_UPDATE 2 + /* + * Reset value selector for vertical clamp calculation. Use one of + * the above values + */ + __u8 reset_val_sel; + /* U8Q8. Line average coefficient used in vertical clamp calculation */ + __u8 line_ave_coef; + /* Height of the optical black region for calculation */ + __u16 ob_v_sz_calc; + /* Optical black region start position - horizontal. 0 - 8191 */ + __u16 ob_start_h; + /* Optical black region start position - vertical 0 - 8191 */ + __u16 ob_start_v; +}; + +struct isif_black_clamp { + /* + * This offset value is added irrespective of the clamp enable status. + * S13 + */ + __u16 dc_offset; + /* + * Enable black/digital clamp value to be subtracted from the image data + */ + __u8 en; + /* + * black clamp mode. same/separate clamp for 4 colors + * 0 - disable - same clamp value for all colors + * 1 - clamp value calculated separately for all colors + */ + __u8 bc_mode_color; + /* Vertical start position for bc subtraction */ + __u16 vert_start_sub; + /* Black clamp for horizontal direction */ + struct isif_horz_bclamp horz; + /* Black clamp for vertical direction */ + struct isif_vert_bclamp vert; +}; + +/************************************************************************* +** Color Space Conversion (CSC) +*************************************************************************/ +#define ISIF_CSC_NUM_COEFF 16 +struct isif_color_space_conv { + /* Enable color space conversion */ + __u8 en; + /* + * csc coefficient table. S8Q5, M00 at index 0, M01 at index 1, and + * so forth + */ + struct isif_float_8 coeff[ISIF_CSC_NUM_COEFF]; +}; + + +/************************************************************************* +** Black Compensation parameters +*************************************************************************/ +struct isif_black_comp { + /* Comp for Red */ + __s8 r_comp; + /* Comp for Gr */ + __s8 gr_comp; + /* Comp for Blue */ + __s8 b_comp; + /* Comp for Gb */ + __s8 gb_comp; +}; + +/************************************************************************* +** Gain parameters +*************************************************************************/ +struct isif_gain { + /* Gain for Red or ye */ + struct isif_float_16 r_ye; + /* Gain for Gr or cy */ + struct isif_float_16 gr_cy; + /* Gain for Gb or g */ + struct isif_float_16 gb_g; + /* Gain for Blue or mg */ + struct isif_float_16 b_mg; +}; + +#define ISIF_LINEAR_TAB_SIZE 192 +/************************************************************************* +** Linearization parameters +*************************************************************************/ +struct isif_linearize { + /* Enable or Disable linearization of data */ + __u8 en; + /* Shift value applied */ + __u8 corr_shft; + /* scale factor applied U11Q10 */ + struct isif_float_16 scale_fact; + /* Size of the linear table */ + __u16 table[ISIF_LINEAR_TAB_SIZE]; +}; + +/* Color patterns */ +#define ISIF_RED 0 +#define ISIF_GREEN_RED 1 +#define ISIF_GREEN_BLUE 2 +#define ISIF_BLUE 3 +struct isif_col_pat { + __u8 olop; + __u8 olep; + __u8 elop; + __u8 elep; +}; + +/************************************************************************* +** Data formatter parameters +*************************************************************************/ +struct isif_fmtplen { + /* + * number of program entries for SET0, range 1 - 16 + * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is + * ISIF_COMBINE + */ + __u16 plen0; + /* + * number of program entries for SET1, range 1 - 16 + * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is + * ISIF_COMBINE + */ + __u16 plen1; + /** + * number of program entries for SET2, range 1 - 16 + * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is + * ISIF_COMBINE + */ + __u16 plen2; + /** + * number of program entries for SET3, range 1 - 16 + * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is + * ISIF_COMBINE + */ + __u16 plen3; +}; + +struct isif_fmt_cfg { +#define ISIF_SPLIT 0 +#define ISIF_COMBINE 1 + /* Split or combine or line alternate */ + __u8 fmtmode; + /* enable or disable line alternating mode */ + __u8 ln_alter_en; +#define ISIF_1LINE 0 +#define ISIF_2LINES 1 +#define ISIF_3LINES 2 +#define ISIF_4LINES 3 + /* Split/combine line number */ + __u8 lnum; + /* Address increment Range 1 - 16 */ + __u8 addrinc; +}; + +struct isif_fmt_addr_ptr { + /* Initial address */ + __u32 init_addr; + /* output line number */ +#define ISIF_1STLINE 0 +#define ISIF_2NDLINE 1 +#define ISIF_3RDLINE 2 +#define ISIF_4THLINE 3 + __u8 out_line; +}; + +struct isif_fmtpgm_ap { + /* program address pointer */ + __u8 pgm_aptr; + /* program address increment or decrement */ + __u8 pgmupdt; +}; + +struct isif_data_formatter { + /* Enable/Disable data formatter */ + __u8 en; + /* data formatter configuration */ + struct isif_fmt_cfg cfg; + /* Formatter program entries length */ + struct isif_fmtplen plen; + /* first pixel in a line fed to formatter */ + __u16 fmtrlen; + /* HD interval for output line. Only valid when split line */ + __u16 fmthcnt; + /* formatter address pointers */ + struct isif_fmt_addr_ptr fmtaddr_ptr[16]; + /* program enable/disable */ + __u8 pgm_en[32]; + /* program address pointers */ + struct isif_fmtpgm_ap fmtpgm_ap[32]; +}; + +struct isif_df_csc { + /* Color Space Conversion configuration, 0 - csc, 1 - df */ + __u8 df_or_csc; + /* csc configuration valid if df_or_csc is 0 */ + struct isif_color_space_conv csc; + /* data formatter configuration valid if df_or_csc is 1 */ + struct isif_data_formatter df; + /* start pixel in a line at the input */ + __u32 start_pix; + /* number of pixels in input line */ + __u32 num_pixels; + /* start line at the input */ + __u32 start_line; + /* number of lines at the input */ + __u32 num_lines; +}; + +struct isif_gain_offsets_adj { + /* Gain adjustment per color */ + struct isif_gain gain; + /* Offset adjustment */ + __u16 offset; + /* Enable or Disable Gain adjustment for SDRAM data */ + __u8 gain_sdram_en; + /* Enable or Disable Gain adjustment for IPIPE data */ + __u8 gain_ipipe_en; + /* Enable or Disable Gain adjustment for H3A data */ + __u8 gain_h3a_en; + /* Enable or Disable Gain adjustment for SDRAM data */ + __u8 offset_sdram_en; + /* Enable or Disable Gain adjustment for IPIPE data */ + __u8 offset_ipipe_en; + /* Enable or Disable Gain adjustment for H3A data */ + __u8 offset_h3a_en; +}; + +struct isif_cul { + /* Horizontal Cull pattern for odd lines */ + __u8 hcpat_odd; + /* Horizontal Cull pattern for even lines */ + __u8 hcpat_even; + /* Vertical Cull pattern */ + __u8 vcpat; + /* Enable or disable lpf. Apply when cull is enabled */ + __u8 en_lpf; +}; + +struct isif_compress { +#define ISIF_ALAW 0 +#define ISIF_DPCM 1 +#define ISIF_NO_COMPRESSION 2 + /* Compression Algorithm used */ + __u8 alg; + /* Choose Predictor1 for DPCM compression */ +#define ISIF_DPCM_PRED1 0 + /* Choose Predictor2 for DPCM compression */ +#define ISIF_DPCM_PRED2 1 + /* Predictor for DPCM compression */ + __u8 pred; +}; + +/* all the stuff in this struct will be provided by userland */ +struct isif_config_params_raw { + /* Linearization parameters for image sensor data input */ + struct isif_linearize linearize; + /* Data formatter or CSC */ + struct isif_df_csc df_csc; + /* Defect Pixel Correction (DFC) configuration */ + struct isif_dfc dfc; + /* Black/Digital Clamp configuration */ + struct isif_black_clamp bclamp; + /* Gain, offset adjustments */ + struct isif_gain_offsets_adj gain_offset; + /* Culling */ + struct isif_cul culling; + /* A-Law and DPCM compression options */ + struct isif_compress compress; + /* horizontal offset for Gain/LSC/DFC */ + __u16 horz_offset; + /* vertical offset for Gain/LSC/DFC */ + __u16 vert_offset; + /* color pattern for field 0 */ + struct isif_col_pat col_pat_field0; + /* color pattern for field 1 */ + struct isif_col_pat col_pat_field1; +#define ISIF_NO_SHIFT 0 +#define ISIF_1BIT_SHIFT 1 +#define ISIF_2BIT_SHIFT 2 +#define ISIF_3BIT_SHIFT 3 +#define ISIF_4BIT_SHIFT 4 +#define ISIF_5BIT_SHIFT 5 +#define ISIF_6BIT_SHIFT 6 + /* Data shift applied before storing to SDRAM */ + __u8 data_shift; + /* enable input test pattern generation */ + __u8 test_pat_gen; +}; + +#ifdef __KERNEL__ +struct isif_ycbcr_config { + /* isif pixel format */ + enum ccdc_pixfmt pix_fmt; + /* isif frame format */ + enum ccdc_frmfmt frm_fmt; + /* ISIF crop window */ + struct v4l2_rect win; + /* field polarity */ + enum vpfe_pin_pol fid_pol; + /* interface VD polarity */ + enum vpfe_pin_pol vd_pol; + /* interface HD polarity */ + enum vpfe_pin_pol hd_pol; + /* isif pix order. Only used for ycbcr capture */ + enum ccdc_pixorder pix_order; + /* isif buffer type. Only used for ycbcr capture */ + enum ccdc_buftype buf_type; +}; + +/* MSB of image data connected to sensor port */ +enum isif_data_msb { + ISIF_BIT_MSB_15, + ISIF_BIT_MSB_14, + ISIF_BIT_MSB_13, + ISIF_BIT_MSB_12, + ISIF_BIT_MSB_11, + ISIF_BIT_MSB_10, + ISIF_BIT_MSB_9, + ISIF_BIT_MSB_8, + ISIF_BIT_MSB_7 +}; + +enum isif_cfa_pattern { + ISIF_CFA_PAT_MOSAIC, + ISIF_CFA_PAT_STRIPE +}; + +struct isif_params_raw { + /* isif pixel format */ + enum ccdc_pixfmt pix_fmt; + /* isif frame format */ + enum ccdc_frmfmt frm_fmt; + /* video window */ + struct v4l2_rect win; + /* field polarity */ + enum vpfe_pin_pol fid_pol; + /* interface VD polarity */ + enum vpfe_pin_pol vd_pol; + /* interface HD polarity */ + enum vpfe_pin_pol hd_pol; + /* buffer type. Applicable for interlaced mode */ + enum ccdc_buftype buf_type; + /* Gain values */ + struct isif_gain gain; + /* cfa pattern */ + enum isif_cfa_pattern cfa_pat; + /* Data MSB position */ + enum isif_data_msb data_msb; + /* Enable horizontal flip */ + unsigned char horz_flip_en; + /* Enable image invert vertically */ + unsigned char image_invert_en; + + /* all the userland defined stuff*/ + struct isif_config_params_raw config_params; +}; + +enum isif_data_pack { + ISIF_PACK_16BIT, + ISIF_PACK_12BIT, + ISIF_PACK_8BIT +}; + +#define ISIF_WIN_NTSC {0, 0, 720, 480} +#define ISIF_WIN_VGA {0, 0, 640, 480} + +#endif +#endif diff --git a/drivers/staging/media/deprecated/vpfe_capture/isif_regs.h b/drivers/staging/media/deprecated/vpfe_capture/isif_regs.h new file mode 100644 index 000000000000..d68d38841ae7 --- /dev/null +++ b/drivers/staging/media/deprecated/vpfe_capture/isif_regs.h @@ -0,0 +1,256 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2008-2009 Texas Instruments Inc + */ +#ifndef _ISIF_REGS_H +#define _ISIF_REGS_H + +/* ISIF registers relative offsets */ +#define SYNCEN 0x00 +#define MODESET 0x04 +#define HDW 0x08 +#define VDW 0x0c +#define PPLN 0x10 +#define LPFR 0x14 +#define SPH 0x18 +#define LNH 0x1c +#define SLV0 0x20 +#define SLV1 0x24 +#define LNV 0x28 +#define CULH 0x2c +#define CULV 0x30 +#define HSIZE 0x34 +#define SDOFST 0x38 +#define CADU 0x3c +#define CADL 0x40 +#define LINCFG0 0x44 +#define LINCFG1 0x48 +#define CCOLP 0x4c +#define CRGAIN 0x50 +#define CGRGAIN 0x54 +#define CGBGAIN 0x58 +#define CBGAIN 0x5c +#define COFSTA 0x60 +#define FLSHCFG0 0x64 +#define FLSHCFG1 0x68 +#define FLSHCFG2 0x6c +#define VDINT0 0x70 +#define VDINT1 0x74 +#define VDINT2 0x78 +#define MISC 0x7c +#define CGAMMAWD 0x80 +#define REC656IF 0x84 +#define CCDCFG 0x88 +/***************************************************** +* Defect Correction registers +*****************************************************/ +#define DFCCTL 0x8c +#define VDFSATLV 0x90 +#define DFCMEMCTL 0x94 +#define DFCMEM0 0x98 +#define DFCMEM1 0x9c +#define DFCMEM2 0xa0 +#define DFCMEM3 0xa4 +#define DFCMEM4 0xa8 +/**************************************************** +* Black Clamp registers +****************************************************/ +#define CLAMPCFG 0xac +#define CLDCOFST 0xb0 +#define CLSV 0xb4 +#define CLHWIN0 0xb8 +#define CLHWIN1 0xbc +#define CLHWIN2 0xc0 +#define CLVRV 0xc4 +#define CLVWIN0 0xc8 +#define CLVWIN1 0xcc +#define CLVWIN2 0xd0 +#define CLVWIN3 0xd4 +/**************************************************** +* Lense Shading Correction +****************************************************/ +#define DATAHOFST 0xd8 +#define DATAVOFST 0xdc +#define LSCHVAL 0xe0 +#define LSCVVAL 0xe4 +#define TWODLSCCFG 0xe8 +#define TWODLSCOFST 0xec +#define TWODLSCINI 0xf0 +#define TWODLSCGRBU 0xf4 +#define TWODLSCGRBL 0xf8 +#define TWODLSCGROF 0xfc +#define TWODLSCORBU 0x100 +#define TWODLSCORBL 0x104 +#define TWODLSCOROF 0x108 +#define TWODLSCIRQEN 0x10c +#define TWODLSCIRQST 0x110 +/**************************************************** +* Data formatter +****************************************************/ +#define FMTCFG 0x114 +#define FMTPLEN 0x118 +#define FMTSPH 0x11c +#define FMTLNH 0x120 +#define FMTSLV 0x124 +#define FMTLNV 0x128 +#define FMTRLEN 0x12c +#define FMTHCNT 0x130 +#define FMTAPTR_BASE 0x134 +/* Below macro for addresses FMTAPTR0 - FMTAPTR15 */ +#define FMTAPTR(i) (FMTAPTR_BASE + (i * 4)) +#define FMTPGMVF0 0x174 +#define FMTPGMVF1 0x178 +#define FMTPGMAPU0 0x17c +#define FMTPGMAPU1 0x180 +#define FMTPGMAPS0 0x184 +#define FMTPGMAPS1 0x188 +#define FMTPGMAPS2 0x18c +#define FMTPGMAPS3 0x190 +#define FMTPGMAPS4 0x194 +#define FMTPGMAPS5 0x198 +#define FMTPGMAPS6 0x19c +#define FMTPGMAPS7 0x1a0 +/************************************************ +* Color Space Converter +************************************************/ +#define CSCCTL 0x1a4 +#define CSCM0 0x1a8 +#define CSCM1 0x1ac +#define CSCM2 0x1b0 +#define CSCM3 0x1b4 +#define CSCM4 0x1b8 +#define CSCM5 0x1bc +#define CSCM6 0x1c0 +#define CSCM7 0x1c4 +#define OBWIN0 0x1c8 +#define OBWIN1 0x1cc +#define OBWIN2 0x1d0 +#define OBWIN3 0x1d4 +#define OBVAL0 0x1d8 +#define OBVAL1 0x1dc +#define OBVAL2 0x1e0 +#define OBVAL3 0x1e4 +#define OBVAL4 0x1e8 +#define OBVAL5 0x1ec +#define OBVAL6 0x1f0 +#define OBVAL7 0x1f4 +#define CLKCTL 0x1f8 + +/* Masks & Shifts below */ +#define START_PX_HOR_MASK 0x7FFF +#define NUM_PX_HOR_MASK 0x7FFF +#define START_VER_ONE_MASK 0x7FFF +#define START_VER_TWO_MASK 0x7FFF +#define NUM_LINES_VER 0x7FFF + +/* gain - offset masks */ +#define GAIN_INTEGER_SHIFT 9 +#define OFFSET_MASK 0xFFF +#define GAIN_SDRAM_EN_SHIFT 12 +#define GAIN_IPIPE_EN_SHIFT 13 +#define GAIN_H3A_EN_SHIFT 14 +#define OFST_SDRAM_EN_SHIFT 8 +#define OFST_IPIPE_EN_SHIFT 9 +#define OFST_H3A_EN_SHIFT 10 +#define GAIN_OFFSET_EN_MASK 0x7700 + +/* Culling */ +#define CULL_PAT_EVEN_LINE_SHIFT 8 + +/* CCDCFG register */ +#define ISIF_YCINSWP_RAW (0x00 << 4) +#define ISIF_YCINSWP_YCBCR (0x01 << 4) +#define ISIF_CCDCFG_FIDMD_LATCH_VSYNC (0x00 << 6) +#define ISIF_CCDCFG_WENLOG_AND (0x00 << 8) +#define ISIF_CCDCFG_TRGSEL_WEN (0x00 << 9) +#define ISIF_CCDCFG_EXTRG_DISABLE (0x00 << 10) +#define ISIF_LATCH_ON_VSYNC_DISABLE (0x01 << 15) +#define ISIF_LATCH_ON_VSYNC_ENABLE (0x00 << 15) +#define ISIF_DATA_PACK_MASK 3 +#define ISIF_DATA_PACK16 0 +#define ISIF_DATA_PACK12 1 +#define ISIF_DATA_PACK8 2 +#define ISIF_PIX_ORDER_SHIFT 11 +#define ISIF_BW656_ENABLE (0x01 << 5) + +/* MODESET registers */ +#define ISIF_VDHDOUT_INPUT (0x00 << 0) +#define ISIF_INPUT_SHIFT 12 +#define ISIF_RAW_INPUT_MODE 0 +#define ISIF_FID_POL_SHIFT 4 +#define ISIF_HD_POL_SHIFT 3 +#define ISIF_VD_POL_SHIFT 2 +#define ISIF_DATAPOL_NORMAL 0 +#define ISIF_DATAPOL_SHIFT 6 +#define ISIF_EXWEN_DISABLE 0 +#define ISIF_EXWEN_SHIFT 5 +#define ISIF_FRM_FMT_SHIFT 7 +#define ISIF_DATASFT_SHIFT 8 +#define ISIF_LPF_SHIFT 14 +#define ISIF_LPF_MASK 1 + +/* GAMMAWD registers */ +#define ISIF_ALAW_GAMMA_WD_MASK 0xF +#define ISIF_ALAW_GAMMA_WD_SHIFT 1 +#define ISIF_ALAW_ENABLE 1 +#define ISIF_GAMMAWD_CFA_SHIFT 5 + +/* HSIZE registers */ +#define ISIF_HSIZE_FLIP_MASK 1 +#define ISIF_HSIZE_FLIP_SHIFT 12 + +/* MISC registers */ +#define ISIF_DPCM_EN_SHIFT 12 +#define ISIF_DPCM_PREDICTOR_SHIFT 13 + +/* Black clamp related */ +#define ISIF_BC_MODE_COLOR_SHIFT 4 +#define ISIF_HORZ_BC_MODE_SHIFT 1 +#define ISIF_HORZ_BC_WIN_SEL_SHIFT 5 +#define ISIF_HORZ_BC_PIX_LIMIT_SHIFT 6 +#define ISIF_HORZ_BC_WIN_H_SIZE_SHIFT 8 +#define ISIF_HORZ_BC_WIN_V_SIZE_SHIFT 12 +#define ISIF_VERT_BC_RST_VAL_SEL_SHIFT 4 +#define ISIF_VERT_BC_LINE_AVE_COEF_SHIFT 8 + +/* VDFC registers */ +#define ISIF_VDFC_EN_SHIFT 4 +#define ISIF_VDFC_CORR_MOD_SHIFT 5 +#define ISIF_VDFC_CORR_WHOLE_LN_SHIFT 7 +#define ISIF_VDFC_LEVEL_SHFT_SHIFT 8 +#define ISIF_VDFC_POS_MASK 0x1FFF +#define ISIF_DFCMEMCTL_DFCMARST_SHIFT 2 + +/* CSC registers */ +#define ISIF_CSC_COEF_INTEG_MASK 7 +#define ISIF_CSC_COEF_DECIMAL_MASK 0x1f +#define ISIF_CSC_COEF_INTEG_SHIFT 5 +#define ISIF_CSCM_MSB_SHIFT 8 +#define ISIF_DF_CSC_SPH_MASK 0x1FFF +#define ISIF_DF_CSC_LNH_MASK 0x1FFF +#define ISIF_DF_CSC_SLV_MASK 0x1FFF +#define ISIF_DF_CSC_LNV_MASK 0x1FFF +#define ISIF_DF_NUMLINES 0x7FFF +#define ISIF_DF_NUMPIX 0x1FFF + +/* Offsets for LSC/DFC/Gain */ +#define ISIF_DATA_H_OFFSET_MASK 0x1FFF +#define ISIF_DATA_V_OFFSET_MASK 0x1FFF + +/* Linearization */ +#define ISIF_LIN_CORRSFT_SHIFT 4 +#define ISIF_LIN_SCALE_FACT_INTEG_SHIFT 10 + + +/* Pattern registers */ +#define ISIF_PG_EN (1 << 3) +#define ISIF_SEL_PG_SRC (3 << 4) +#define ISIF_PG_VD_POL_SHIFT 0 +#define ISIF_PG_HD_POL_SHIFT 1 + +/*random other junk*/ +#define ISIF_SYNCEN_VDHDEN_MASK (1 << 0) +#define ISIF_SYNCEN_WEN_MASK (1 << 1) +#define ISIF_SYNCEN_WEN_SHIFT 1 + +#endif diff --git a/drivers/staging/media/deprecated/vpfe_capture/vpfe_capture.c b/drivers/staging/media/deprecated/vpfe_capture/vpfe_capture.c new file mode 100644 index 000000000000..0a2226b321d7 --- /dev/null +++ b/drivers/staging/media/deprecated/vpfe_capture/vpfe_capture.c @@ -0,0 +1,1902 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * Driver name : VPFE Capture driver + * VPFE Capture driver allows applications to capture and stream video + * frames on DaVinci SoCs (DM6446, DM355 etc) from a YUV source such as + * TVP5146 or Raw Bayer RGB image data from an image sensor + * such as Microns' MT9T001, MT9T031 etc. + * + * These SoCs have, in common, a Video Processing Subsystem (VPSS) that + * consists of a Video Processing Front End (VPFE) for capturing + * video/raw image data and Video Processing Back End (VPBE) for displaying + * YUV data through an in-built analog encoder or Digital LCD port. This + * driver is for capture through VPFE. A typical EVM using these SoCs have + * following high level configuration. + * + * decoder(TVP5146/ YUV/ + * MT9T001) --> Raw Bayer RGB ---> MUX -> VPFE (CCDC/ISIF) + * data input | | + * V | + * SDRAM | + * V + * Image Processor + * | + * V + * SDRAM + * The data flow happens from a decoder connected to the VPFE over a + * YUV embedded (BT.656/BT.1120) or separate sync or raw bayer rgb interface + * and to the input of VPFE through an optional MUX (if more inputs are + * to be interfaced on the EVM). The input data is first passed through + * CCDC (CCD Controller, a.k.a Image Sensor Interface, ISIF). The CCDC + * does very little or no processing on YUV data and does pre-process Raw + * Bayer RGB data through modules such as Defect Pixel Correction (DFC) + * Color Space Conversion (CSC), data gain/offset etc. After this, data + * can be written to SDRAM or can be connected to the image processing + * block such as IPIPE (on DM355 only). + * + * Features supported + * - MMAP IO + * - Capture using TVP5146 over BT.656 + * - support for interfacing decoders using sub device model + * - Work with DM355 or DM6446 CCDC to do Raw Bayer RGB/YUV + * data capture to SDRAM. + * TODO list + * - Support multiple REQBUF after open + * - Support for de-allocating buffers through REQBUF + * - Support for Raw Bayer RGB capture + * - Support for chaining Image Processor + * - Support for static allocation of buffers + * - Support for USERPTR IO + * - Support for STREAMON before QBUF + * - Support for control ioctls + */ +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <media/v4l2-common.h> +#include <linux/io.h> +#include <media/davinci/vpfe_capture.h> +#include "ccdc_hw_device.h" + +static int debug; +static u32 numbuffers = 3; +static u32 bufsize = (720 * 576 * 2); + +module_param(numbuffers, uint, S_IRUGO); +module_param(bufsize, uint, S_IRUGO); +module_param(debug, int, 0644); + +MODULE_PARM_DESC(numbuffers, "buffer count (default:3)"); +MODULE_PARM_DESC(bufsize, "buffer size in bytes (default:720 x 576 x 2)"); +MODULE_PARM_DESC(debug, "Debug level 0-1"); + +MODULE_DESCRIPTION("VPFE Video for Linux Capture Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Texas Instruments"); + +/* standard information */ +struct vpfe_standard { + v4l2_std_id std_id; + unsigned int width; + unsigned int height; + struct v4l2_fract pixelaspect; + /* 0 - progressive, 1 - interlaced */ + int frame_format; +}; + +/* ccdc configuration */ +struct ccdc_config { + /* This make sure vpfe is probed and ready to go */ + int vpfe_probed; + /* name of ccdc device */ + char name[32]; +}; + +/* data structures */ +static struct vpfe_config_params config_params = { + .min_numbuffers = 3, + .numbuffers = 3, + .min_bufsize = 720 * 480 * 2, + .device_bufsize = 720 * 576 * 2, +}; + +/* ccdc device registered */ +static const struct ccdc_hw_device *ccdc_dev; +/* lock for accessing ccdc information */ +static DEFINE_MUTEX(ccdc_lock); +/* ccdc configuration */ +static struct ccdc_config *ccdc_cfg; + +static const struct vpfe_standard vpfe_standards[] = { + {V4L2_STD_525_60, 720, 480, {11, 10}, 1}, + {V4L2_STD_625_50, 720, 576, {54, 59}, 1}, +}; + +/* Used when raw Bayer image from ccdc is directly captured to SDRAM */ +static const struct vpfe_pixel_format vpfe_pix_fmts[] = { + { + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .bpp = 1, + }, + { + .pixelformat = V4L2_PIX_FMT_SBGGR16, + .bpp = 2, + }, + { + .pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8, + .bpp = 1, + }, + { + .pixelformat = V4L2_PIX_FMT_UYVY, + .bpp = 2, + }, + { + .pixelformat = V4L2_PIX_FMT_YUYV, + .bpp = 2, + }, + { + .pixelformat = V4L2_PIX_FMT_NV12, + .bpp = 1, + }, +}; + +/* + * vpfe_lookup_pix_format() + * lookup an entry in the vpfe pix format table based on pix_format + */ +static const struct vpfe_pixel_format *vpfe_lookup_pix_format(u32 pix_format) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(vpfe_pix_fmts); i++) { + if (pix_format == vpfe_pix_fmts[i].pixelformat) + return &vpfe_pix_fmts[i]; + } + return NULL; +} + +/* + * vpfe_register_ccdc_device. CCDC module calls this to + * register with vpfe capture + */ +int vpfe_register_ccdc_device(const struct ccdc_hw_device *dev) +{ + int ret = 0; + printk(KERN_NOTICE "vpfe_register_ccdc_device: %s\n", dev->name); + + if (!dev->hw_ops.open || + !dev->hw_ops.enable || + !dev->hw_ops.set_hw_if_params || + !dev->hw_ops.configure || + !dev->hw_ops.set_buftype || + !dev->hw_ops.get_buftype || + !dev->hw_ops.enum_pix || + !dev->hw_ops.set_frame_format || + !dev->hw_ops.get_frame_format || + !dev->hw_ops.get_pixel_format || + !dev->hw_ops.set_pixel_format || + !dev->hw_ops.set_image_window || + !dev->hw_ops.get_image_window || + !dev->hw_ops.get_line_length || + !dev->hw_ops.getfid) + return -EINVAL; + + mutex_lock(&ccdc_lock); + if (!ccdc_cfg) { + /* + * TODO. Will this ever happen? if so, we need to fix it. + * Probably we need to add the request to a linked list and + * walk through it during vpfe probe + */ + printk(KERN_ERR "vpfe capture not initialized\n"); + ret = -EFAULT; + goto unlock; + } + + if (strcmp(dev->name, ccdc_cfg->name)) { + /* ignore this ccdc */ + ret = -EINVAL; + goto unlock; + } + + if (ccdc_dev) { + printk(KERN_ERR "ccdc already registered\n"); + ret = -EINVAL; + goto unlock; + } + + ccdc_dev = dev; +unlock: + mutex_unlock(&ccdc_lock); + return ret; +} +EXPORT_SYMBOL(vpfe_register_ccdc_device); + +/* + * vpfe_unregister_ccdc_device. CCDC module calls this to + * unregister with vpfe capture + */ +void vpfe_unregister_ccdc_device(const struct ccdc_hw_device *dev) +{ + if (!dev) { + printk(KERN_ERR "invalid ccdc device ptr\n"); + return; + } + + printk(KERN_NOTICE "vpfe_unregister_ccdc_device, dev->name = %s\n", + dev->name); + + if (strcmp(dev->name, ccdc_cfg->name)) { + /* ignore this ccdc */ + return; + } + + mutex_lock(&ccdc_lock); + ccdc_dev = NULL; + mutex_unlock(&ccdc_lock); +} +EXPORT_SYMBOL(vpfe_unregister_ccdc_device); + +/* + * vpfe_config_ccdc_image_format() + * For a pix format, configure ccdc to setup the capture + */ +static int vpfe_config_ccdc_image_format(struct vpfe_device *vpfe_dev) +{ + enum ccdc_frmfmt frm_fmt = CCDC_FRMFMT_INTERLACED; + int ret = 0; + + if (ccdc_dev->hw_ops.set_pixel_format( + vpfe_dev->fmt.fmt.pix.pixelformat) < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "couldn't set pix format in ccdc\n"); + return -EINVAL; + } + /* configure the image window */ + ccdc_dev->hw_ops.set_image_window(&vpfe_dev->crop); + + switch (vpfe_dev->fmt.fmt.pix.field) { + case V4L2_FIELD_INTERLACED: + /* do nothing, since it is default */ + ret = ccdc_dev->hw_ops.set_buftype( + CCDC_BUFTYPE_FLD_INTERLEAVED); + break; + case V4L2_FIELD_NONE: + frm_fmt = CCDC_FRMFMT_PROGRESSIVE; + /* buffer type only applicable for interlaced scan */ + break; + case V4L2_FIELD_SEQ_TB: + ret = ccdc_dev->hw_ops.set_buftype( + CCDC_BUFTYPE_FLD_SEPARATED); + break; + default: + return -EINVAL; + } + + /* set the frame format */ + if (!ret) + ret = ccdc_dev->hw_ops.set_frame_format(frm_fmt); + return ret; +} +/* + * vpfe_config_image_format() + * For a given standard, this functions sets up the default + * pix format & crop values in the vpfe device and ccdc. It first + * starts with defaults based values from the standard table. + * It then checks if sub device supports get_fmt and then override the + * values based on that.Sets crop values to match with scan resolution + * starting at 0,0. It calls vpfe_config_ccdc_image_format() set the + * values in ccdc + */ +static int vpfe_config_image_format(struct vpfe_device *vpfe_dev, + v4l2_std_id std_id) +{ + struct vpfe_subdev_info *sdinfo = vpfe_dev->current_subdev; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mbus_fmt = &fmt.format; + struct v4l2_pix_format *pix = &vpfe_dev->fmt.fmt.pix; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(vpfe_standards); i++) { + if (vpfe_standards[i].std_id & std_id) { + vpfe_dev->std_info.active_pixels = + vpfe_standards[i].width; + vpfe_dev->std_info.active_lines = + vpfe_standards[i].height; + vpfe_dev->std_info.frame_format = + vpfe_standards[i].frame_format; + vpfe_dev->std_index = i; + break; + } + } + + if (i == ARRAY_SIZE(vpfe_standards)) { + v4l2_err(&vpfe_dev->v4l2_dev, "standard not supported\n"); + return -EINVAL; + } + + vpfe_dev->crop.top = 0; + vpfe_dev->crop.left = 0; + vpfe_dev->crop.width = vpfe_dev->std_info.active_pixels; + vpfe_dev->crop.height = vpfe_dev->std_info.active_lines; + pix->width = vpfe_dev->crop.width; + pix->height = vpfe_dev->crop.height; + + /* first field and frame format based on standard frame format */ + if (vpfe_dev->std_info.frame_format) { + pix->field = V4L2_FIELD_INTERLACED; + /* assume V4L2_PIX_FMT_UYVY as default */ + pix->pixelformat = V4L2_PIX_FMT_UYVY; + v4l2_fill_mbus_format(mbus_fmt, pix, + MEDIA_BUS_FMT_YUYV10_2X10); + } else { + pix->field = V4L2_FIELD_NONE; + /* assume V4L2_PIX_FMT_SBGGR8 */ + pix->pixelformat = V4L2_PIX_FMT_SBGGR8; + v4l2_fill_mbus_format(mbus_fmt, pix, + MEDIA_BUS_FMT_SBGGR8_1X8); + } + + /* if sub device supports get_fmt, override the defaults */ + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, + sdinfo->grp_id, pad, get_fmt, NULL, &fmt); + + if (ret && ret != -ENOIOCTLCMD) { + v4l2_err(&vpfe_dev->v4l2_dev, + "error in getting get_fmt from sub device\n"); + return ret; + } + v4l2_fill_pix_format(pix, mbus_fmt); + pix->bytesperline = pix->width * 2; + pix->sizeimage = pix->bytesperline * pix->height; + + /* Sets the values in CCDC */ + ret = vpfe_config_ccdc_image_format(vpfe_dev); + if (ret) + return ret; + + /* Update the values of sizeimage and bytesperline */ + pix->bytesperline = ccdc_dev->hw_ops.get_line_length(); + pix->sizeimage = pix->bytesperline * pix->height; + + return 0; +} + +static int vpfe_initialize_device(struct vpfe_device *vpfe_dev) +{ + int ret; + + /* set first input of current subdevice as the current input */ + vpfe_dev->current_input = 0; + + /* set default standard */ + vpfe_dev->std_index = 0; + + /* Configure the default format information */ + ret = vpfe_config_image_format(vpfe_dev, + vpfe_standards[vpfe_dev->std_index].std_id); + if (ret) + return ret; + + /* now open the ccdc device to initialize it */ + mutex_lock(&ccdc_lock); + if (!ccdc_dev) { + v4l2_err(&vpfe_dev->v4l2_dev, "ccdc device not registered\n"); + ret = -ENODEV; + goto unlock; + } + + if (!try_module_get(ccdc_dev->owner)) { + v4l2_err(&vpfe_dev->v4l2_dev, "Couldn't lock ccdc module\n"); + ret = -ENODEV; + goto unlock; + } + ret = ccdc_dev->hw_ops.open(vpfe_dev->pdev); + if (!ret) + vpfe_dev->initialized = 1; + + /* Clear all VPFE/CCDC interrupts */ + if (vpfe_dev->cfg->clr_intr) + vpfe_dev->cfg->clr_intr(-1); + +unlock: + mutex_unlock(&ccdc_lock); + return ret; +} + +/* + * vpfe_open : It creates object of file handle structure and + * stores it in private_data member of filepointer + */ +static int vpfe_open(struct file *file) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct video_device *vdev = video_devdata(file); + struct vpfe_fh *fh; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_open\n"); + + if (!vpfe_dev->cfg->num_subdevs) { + v4l2_err(&vpfe_dev->v4l2_dev, "No decoder registered\n"); + return -ENODEV; + } + + /* Allocate memory for the file handle object */ + fh = kmalloc(sizeof(*fh), GFP_KERNEL); + if (!fh) + return -ENOMEM; + + /* store pointer to fh in private_data member of file */ + file->private_data = fh; + fh->vpfe_dev = vpfe_dev; + v4l2_fh_init(&fh->fh, vdev); + mutex_lock(&vpfe_dev->lock); + /* If decoder is not initialized. initialize it */ + if (!vpfe_dev->initialized) { + if (vpfe_initialize_device(vpfe_dev)) { + mutex_unlock(&vpfe_dev->lock); + v4l2_fh_exit(&fh->fh); + kfree(fh); + return -ENODEV; + } + } + /* Increment device usrs counter */ + vpfe_dev->usrs++; + /* Set io_allowed member to false */ + fh->io_allowed = 0; + v4l2_fh_add(&fh->fh); + mutex_unlock(&vpfe_dev->lock); + return 0; +} + +static void vpfe_schedule_next_buffer(struct vpfe_device *vpfe_dev) +{ + unsigned long addr; + + vpfe_dev->next_frm = list_entry(vpfe_dev->dma_queue.next, + struct videobuf_buffer, queue); + list_del(&vpfe_dev->next_frm->queue); + vpfe_dev->next_frm->state = VIDEOBUF_ACTIVE; + addr = videobuf_to_dma_contig(vpfe_dev->next_frm); + + ccdc_dev->hw_ops.setfbaddr(addr); +} + +static void vpfe_schedule_bottom_field(struct vpfe_device *vpfe_dev) +{ + unsigned long addr; + + addr = videobuf_to_dma_contig(vpfe_dev->cur_frm); + addr += vpfe_dev->field_off; + ccdc_dev->hw_ops.setfbaddr(addr); +} + +static void vpfe_process_buffer_complete(struct vpfe_device *vpfe_dev) +{ + vpfe_dev->cur_frm->ts = ktime_get_ns(); + vpfe_dev->cur_frm->state = VIDEOBUF_DONE; + vpfe_dev->cur_frm->size = vpfe_dev->fmt.fmt.pix.sizeimage; + wake_up_interruptible(&vpfe_dev->cur_frm->done); + vpfe_dev->cur_frm = vpfe_dev->next_frm; +} + +/* ISR for VINT0*/ +static irqreturn_t vpfe_isr(int irq, void *dev_id) +{ + struct vpfe_device *vpfe_dev = dev_id; + enum v4l2_field field; + int fid; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "\nStarting vpfe_isr...\n"); + field = vpfe_dev->fmt.fmt.pix.field; + + /* if streaming not started, don't do anything */ + if (!vpfe_dev->started) + goto clear_intr; + + /* only for 6446 this will be applicable */ + if (ccdc_dev->hw_ops.reset) + ccdc_dev->hw_ops.reset(); + + if (field == V4L2_FIELD_NONE) { + /* handle progressive frame capture */ + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "frame format is progressive...\n"); + if (vpfe_dev->cur_frm != vpfe_dev->next_frm) + vpfe_process_buffer_complete(vpfe_dev); + goto clear_intr; + } + + /* interlaced or TB capture check which field we are in hardware */ + fid = ccdc_dev->hw_ops.getfid(); + + /* switch the software maintained field id */ + vpfe_dev->field_id ^= 1; + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "field id = %x:%x.\n", + fid, vpfe_dev->field_id); + if (fid == vpfe_dev->field_id) { + /* we are in-sync here,continue */ + if (fid == 0) { + /* + * One frame is just being captured. If the next frame + * is available, release the current frame and move on + */ + if (vpfe_dev->cur_frm != vpfe_dev->next_frm) + vpfe_process_buffer_complete(vpfe_dev); + /* + * based on whether the two fields are stored + * interleavely or separately in memory, reconfigure + * the CCDC memory address + */ + if (field == V4L2_FIELD_SEQ_TB) + vpfe_schedule_bottom_field(vpfe_dev); + goto clear_intr; + } + /* + * if one field is just being captured configure + * the next frame get the next frame from the empty + * queue if no frame is available hold on to the + * current buffer + */ + spin_lock(&vpfe_dev->dma_queue_lock); + if (!list_empty(&vpfe_dev->dma_queue) && + vpfe_dev->cur_frm == vpfe_dev->next_frm) + vpfe_schedule_next_buffer(vpfe_dev); + spin_unlock(&vpfe_dev->dma_queue_lock); + } else if (fid == 0) { + /* + * out of sync. Recover from any hardware out-of-sync. + * May loose one frame + */ + vpfe_dev->field_id = fid; + } +clear_intr: + if (vpfe_dev->cfg->clr_intr) + vpfe_dev->cfg->clr_intr(irq); + + return IRQ_HANDLED; +} + +/* vdint1_isr - isr handler for VINT1 interrupt */ +static irqreturn_t vdint1_isr(int irq, void *dev_id) +{ + struct vpfe_device *vpfe_dev = dev_id; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "\nInside vdint1_isr...\n"); + + /* if streaming not started, don't do anything */ + if (!vpfe_dev->started) { + if (vpfe_dev->cfg->clr_intr) + vpfe_dev->cfg->clr_intr(irq); + return IRQ_HANDLED; + } + + spin_lock(&vpfe_dev->dma_queue_lock); + if ((vpfe_dev->fmt.fmt.pix.field == V4L2_FIELD_NONE) && + !list_empty(&vpfe_dev->dma_queue) && + vpfe_dev->cur_frm == vpfe_dev->next_frm) + vpfe_schedule_next_buffer(vpfe_dev); + spin_unlock(&vpfe_dev->dma_queue_lock); + + if (vpfe_dev->cfg->clr_intr) + vpfe_dev->cfg->clr_intr(irq); + + return IRQ_HANDLED; +} + +static void vpfe_detach_irq(struct vpfe_device *vpfe_dev) +{ + enum ccdc_frmfmt frame_format; + + frame_format = ccdc_dev->hw_ops.get_frame_format(); + if (frame_format == CCDC_FRMFMT_PROGRESSIVE) + free_irq(vpfe_dev->ccdc_irq1, vpfe_dev); +} + +static int vpfe_attach_irq(struct vpfe_device *vpfe_dev) +{ + enum ccdc_frmfmt frame_format; + + frame_format = ccdc_dev->hw_ops.get_frame_format(); + if (frame_format == CCDC_FRMFMT_PROGRESSIVE) { + return request_irq(vpfe_dev->ccdc_irq1, vdint1_isr, + 0, "vpfe_capture1", + vpfe_dev); + } + return 0; +} + +/* vpfe_stop_ccdc_capture: stop streaming in ccdc/isif */ +static void vpfe_stop_ccdc_capture(struct vpfe_device *vpfe_dev) +{ + vpfe_dev->started = 0; + ccdc_dev->hw_ops.enable(0); + if (ccdc_dev->hw_ops.enable_out_to_sdram) + ccdc_dev->hw_ops.enable_out_to_sdram(0); +} + +/* + * vpfe_release : This function deletes buffer queue, frees the + * buffers and the vpfe file handle + */ +static int vpfe_release(struct file *file) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_fh *fh = file->private_data; + struct vpfe_subdev_info *sdinfo; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_release\n"); + + /* Get the device lock */ + mutex_lock(&vpfe_dev->lock); + /* if this instance is doing IO */ + if (fh->io_allowed) { + if (vpfe_dev->started) { + sdinfo = vpfe_dev->current_subdev; + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, + sdinfo->grp_id, + video, s_stream, 0); + if (ret && (ret != -ENOIOCTLCMD)) + v4l2_err(&vpfe_dev->v4l2_dev, + "stream off failed in subdev\n"); + vpfe_stop_ccdc_capture(vpfe_dev); + vpfe_detach_irq(vpfe_dev); + videobuf_streamoff(&vpfe_dev->buffer_queue); + } + vpfe_dev->io_usrs = 0; + vpfe_dev->numbuffers = config_params.numbuffers; + videobuf_stop(&vpfe_dev->buffer_queue); + videobuf_mmap_free(&vpfe_dev->buffer_queue); + } + + /* Decrement device usrs counter */ + vpfe_dev->usrs--; + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); + /* If this is the last file handle */ + if (!vpfe_dev->usrs) { + vpfe_dev->initialized = 0; + if (ccdc_dev->hw_ops.close) + ccdc_dev->hw_ops.close(vpfe_dev->pdev); + module_put(ccdc_dev->owner); + } + mutex_unlock(&vpfe_dev->lock); + file->private_data = NULL; + /* Free memory allocated to file handle object */ + kfree(fh); + return 0; +} + +/* + * vpfe_mmap : It is used to map kernel space buffers + * into user spaces + */ +static int vpfe_mmap(struct file *file, struct vm_area_struct *vma) +{ + /* Get the device object and file handle object */ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_mmap\n"); + + return videobuf_mmap_mapper(&vpfe_dev->buffer_queue, vma); +} + +/* + * vpfe_poll: It is used for select/poll system call + */ +static __poll_t vpfe_poll(struct file *file, poll_table *wait) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_poll\n"); + + if (vpfe_dev->started) + return videobuf_poll_stream(file, + &vpfe_dev->buffer_queue, wait); + return 0; +} + +/* vpfe capture driver file operations */ +static const struct v4l2_file_operations vpfe_fops = { + .owner = THIS_MODULE, + .open = vpfe_open, + .release = vpfe_release, + .unlocked_ioctl = video_ioctl2, + .mmap = vpfe_mmap, + .poll = vpfe_poll +}; + +/* + * vpfe_check_format() + * This function adjust the input pixel format as per hardware + * capabilities and update the same in pixfmt. + * Following algorithm used :- + * + * If given pixformat is not in the vpfe list of pix formats or not + * supported by the hardware, current value of pixformat in the device + * is used + * If given field is not supported, then current field is used. If field + * is different from current, then it is matched with that from sub device. + * Minimum height is 2 lines for interlaced or tb field and 1 line for + * progressive. Maximum height is clamped to active active lines of scan + * Minimum width is 32 bytes in memory and width is clamped to active + * pixels of scan. + * bytesperline is a multiple of 32. + */ +static const struct vpfe_pixel_format * + vpfe_check_format(struct vpfe_device *vpfe_dev, + struct v4l2_pix_format *pixfmt) +{ + u32 min_height = 1, min_width = 32, max_width, max_height; + const struct vpfe_pixel_format *vpfe_pix_fmt; + u32 pix; + int temp, found; + + vpfe_pix_fmt = vpfe_lookup_pix_format(pixfmt->pixelformat); + if (!vpfe_pix_fmt) { + /* + * use current pixel format in the vpfe device. We + * will find this pix format in the table + */ + pixfmt->pixelformat = vpfe_dev->fmt.fmt.pix.pixelformat; + vpfe_pix_fmt = vpfe_lookup_pix_format(pixfmt->pixelformat); + } + + /* check if hw supports it */ + temp = 0; + found = 0; + while (ccdc_dev->hw_ops.enum_pix(&pix, temp) >= 0) { + if (vpfe_pix_fmt->pixelformat == pix) { + found = 1; + break; + } + temp++; + } + + if (!found) { + /* use current pixel format */ + pixfmt->pixelformat = vpfe_dev->fmt.fmt.pix.pixelformat; + /* + * Since this is currently used in the vpfe device, we + * will find this pix format in the table + */ + vpfe_pix_fmt = vpfe_lookup_pix_format(pixfmt->pixelformat); + } + + /* check what field format is supported */ + if (pixfmt->field == V4L2_FIELD_ANY) { + /* if field is any, use current value as default */ + pixfmt->field = vpfe_dev->fmt.fmt.pix.field; + } + + /* + * if field is not same as current field in the vpfe device + * try matching the field with the sub device field + */ + if (vpfe_dev->fmt.fmt.pix.field != pixfmt->field) { + /* + * If field value is not in the supported fields, use current + * field used in the device as default + */ + switch (pixfmt->field) { + case V4L2_FIELD_INTERLACED: + case V4L2_FIELD_SEQ_TB: + /* if sub device is supporting progressive, use that */ + if (!vpfe_dev->std_info.frame_format) + pixfmt->field = V4L2_FIELD_NONE; + break; + case V4L2_FIELD_NONE: + if (vpfe_dev->std_info.frame_format) + pixfmt->field = V4L2_FIELD_INTERLACED; + break; + + default: + /* use current field as default */ + pixfmt->field = vpfe_dev->fmt.fmt.pix.field; + break; + } + } + + /* Now adjust image resolutions supported */ + if (pixfmt->field == V4L2_FIELD_INTERLACED || + pixfmt->field == V4L2_FIELD_SEQ_TB) + min_height = 2; + + max_width = vpfe_dev->std_info.active_pixels; + max_height = vpfe_dev->std_info.active_lines; + min_width /= vpfe_pix_fmt->bpp; + + v4l2_info(&vpfe_dev->v4l2_dev, "width = %d, height = %d, bpp = %d\n", + pixfmt->width, pixfmt->height, vpfe_pix_fmt->bpp); + + pixfmt->width = clamp((pixfmt->width), min_width, max_width); + pixfmt->height = clamp((pixfmt->height), min_height, max_height); + + /* If interlaced, adjust height to be a multiple of 2 */ + if (pixfmt->field == V4L2_FIELD_INTERLACED) + pixfmt->height &= (~1); + /* + * recalculate bytesperline and sizeimage since width + * and height might have changed + */ + pixfmt->bytesperline = (((pixfmt->width * vpfe_pix_fmt->bpp) + 31) + & ~31); + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) + pixfmt->sizeimage = + pixfmt->bytesperline * pixfmt->height + + ((pixfmt->bytesperline * pixfmt->height) >> 1); + else + pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height; + + v4l2_info(&vpfe_dev->v4l2_dev, "adjusted width = %d, height = %d, bpp = %d, bytesperline = %d, sizeimage = %d\n", + pixfmt->width, pixfmt->height, vpfe_pix_fmt->bpp, + pixfmt->bytesperline, pixfmt->sizeimage); + return vpfe_pix_fmt; +} + +static int vpfe_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querycap\n"); + + strscpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver)); + strscpy(cap->bus_info, "VPFE", sizeof(cap->bus_info)); + strscpy(cap->card, vpfe_dev->cfg->card_name, sizeof(cap->card)); + return 0; +} + +static int vpfe_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_fmt_vid_cap\n"); + /* Fill in the information about format */ + *fmt = vpfe_dev->fmt; + return 0; +} + +static int vpfe_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + const struct vpfe_pixel_format *pix_fmt; + u32 pix; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_fmt_vid_cap\n"); + + if (ccdc_dev->hw_ops.enum_pix(&pix, fmt->index) < 0) + return -EINVAL; + + /* Fill in the information about format */ + pix_fmt = vpfe_lookup_pix_format(pix); + if (pix_fmt) { + fmt->pixelformat = pix_fmt->pixelformat; + return 0; + } + return -EINVAL; +} + +static int vpfe_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + const struct vpfe_pixel_format *pix_fmts; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_fmt_vid_cap\n"); + + /* If streaming is started, return error */ + if (vpfe_dev->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is started\n"); + return -EBUSY; + } + + /* Check for valid frame format */ + pix_fmts = vpfe_check_format(vpfe_dev, &fmt->fmt.pix); + if (!pix_fmts) + return -EINVAL; + + /* store the pixel format in the device object */ + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + /* First detach any IRQ if currently attached */ + vpfe_detach_irq(vpfe_dev); + vpfe_dev->fmt = *fmt; + /* set image capture parameters in the ccdc */ + ret = vpfe_config_ccdc_image_format(vpfe_dev); + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + const struct vpfe_pixel_format *pix_fmts; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_try_fmt_vid_cap\n"); + + pix_fmts = vpfe_check_format(vpfe_dev, &f->fmt.pix); + if (!pix_fmts) + return -EINVAL; + return 0; +} + +/* + * vpfe_get_subdev_input_index - Get subdev index and subdev input index for a + * given app input index + */ +static int vpfe_get_subdev_input_index(struct vpfe_device *vpfe_dev, + int *subdev_index, + int *subdev_input_index, + int app_input_index) +{ + struct vpfe_config *cfg = vpfe_dev->cfg; + struct vpfe_subdev_info *sdinfo; + int i, j = 0; + + for (i = 0; i < cfg->num_subdevs; i++) { + sdinfo = &cfg->sub_devs[i]; + if (app_input_index < (j + sdinfo->num_inputs)) { + *subdev_index = i; + *subdev_input_index = app_input_index - j; + return 0; + } + j += sdinfo->num_inputs; + } + return -EINVAL; +} + +/* + * vpfe_get_app_input - Get app input index for a given subdev input index + * driver stores the input index of the current sub device and translate it + * when application request the current input + */ +static int vpfe_get_app_input_index(struct vpfe_device *vpfe_dev, + int *app_input_index) +{ + struct vpfe_config *cfg = vpfe_dev->cfg; + struct vpfe_subdev_info *sdinfo; + int i, j = 0; + + for (i = 0; i < cfg->num_subdevs; i++) { + sdinfo = &cfg->sub_devs[i]; + if (!strcmp(sdinfo->name, vpfe_dev->current_subdev->name)) { + if (vpfe_dev->current_input >= sdinfo->num_inputs) + return -1; + *app_input_index = j + vpfe_dev->current_input; + return 0; + } + j += sdinfo->num_inputs; + } + return -EINVAL; +} + +static int vpfe_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + int subdev, index ; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_input\n"); + + if (vpfe_get_subdev_input_index(vpfe_dev, + &subdev, + &index, + inp->index) < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "input information not found for the subdev\n"); + return -EINVAL; + } + sdinfo = &vpfe_dev->cfg->sub_devs[subdev]; + *inp = sdinfo->inputs[index]; + return 0; +} + +static int vpfe_g_input(struct file *file, void *priv, unsigned int *index) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_input\n"); + + return vpfe_get_app_input_index(vpfe_dev, index); +} + + +static int vpfe_s_input(struct file *file, void *priv, unsigned int index) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct v4l2_subdev *sd; + struct vpfe_subdev_info *sdinfo; + int subdev_index, inp_index; + struct vpfe_route *route; + u32 input, output; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_input\n"); + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + /* + * If streaming is started return device busy + * error + */ + if (vpfe_dev->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is on\n"); + ret = -EBUSY; + goto unlock_out; + } + ret = vpfe_get_subdev_input_index(vpfe_dev, + &subdev_index, + &inp_index, + index); + if (ret < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "invalid input index\n"); + goto unlock_out; + } + + sdinfo = &vpfe_dev->cfg->sub_devs[subdev_index]; + sd = vpfe_dev->sd[subdev_index]; + route = &sdinfo->routes[inp_index]; + if (route && sdinfo->can_route) { + input = route->input; + output = route->output; + } else { + input = 0; + output = 0; + } + + if (sd) + ret = v4l2_subdev_call(sd, video, s_routing, input, output, 0); + + if (ret) { + v4l2_err(&vpfe_dev->v4l2_dev, + "vpfe_doioctl:error in setting input in decoder\n"); + ret = -EINVAL; + goto unlock_out; + } + vpfe_dev->current_subdev = sdinfo; + if (sd) + vpfe_dev->v4l2_dev.ctrl_handler = sd->ctrl_handler; + vpfe_dev->current_input = index; + vpfe_dev->std_index = 0; + + /* set the bus/interface parameter for the sub device in ccdc */ + ret = ccdc_dev->hw_ops.set_hw_if_params(&sdinfo->ccdc_if_params); + if (ret) + goto unlock_out; + + /* set the default image parameters in the device */ + ret = vpfe_config_image_format(vpfe_dev, + vpfe_standards[vpfe_dev->std_index].std_id); +unlock_out: + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_querystd(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querystd\n"); + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + sdinfo = vpfe_dev->current_subdev; + if (ret) + return ret; + /* Call querystd function of decoder device */ + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + video, querystd, std_id); + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id std_id) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_std\n"); + + /* Call decoder driver function to set the standard */ + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + sdinfo = vpfe_dev->current_subdev; + /* If streaming is started, return device busy error */ + if (vpfe_dev->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "streaming is started\n"); + ret = -EBUSY; + goto unlock_out; + } + + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + video, s_std, std_id); + if (ret < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "Failed to set standard\n"); + goto unlock_out; + } + ret = vpfe_config_image_format(vpfe_dev, std_id); + +unlock_out: + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_g_std(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_std\n"); + + *std_id = vpfe_standards[vpfe_dev->std_index].std_id; + return 0; +} +/* + * Videobuf operations + */ +static int vpfe_videobuf_setup(struct videobuf_queue *vq, + unsigned int *count, + unsigned int *size) +{ + struct vpfe_fh *fh = vq->priv_data; + struct vpfe_device *vpfe_dev = fh->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_setup\n"); + *size = vpfe_dev->fmt.fmt.pix.sizeimage; + if (vpfe_dev->memory == V4L2_MEMORY_MMAP && + vpfe_dev->fmt.fmt.pix.sizeimage > config_params.device_bufsize) + *size = config_params.device_bufsize; + + if (*count < config_params.min_numbuffers) + *count = config_params.min_numbuffers; + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "count=%d, size=%d\n", *count, *size); + return 0; +} + +static int vpfe_videobuf_prepare(struct videobuf_queue *vq, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct vpfe_fh *fh = vq->priv_data; + struct vpfe_device *vpfe_dev = fh->vpfe_dev; + unsigned long addr; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_prepare\n"); + + /* If buffer is not initialized, initialize it */ + if (VIDEOBUF_NEEDS_INIT == vb->state) { + vb->width = vpfe_dev->fmt.fmt.pix.width; + vb->height = vpfe_dev->fmt.fmt.pix.height; + vb->size = vpfe_dev->fmt.fmt.pix.sizeimage; + vb->field = field; + + ret = videobuf_iolock(vq, vb, NULL); + if (ret < 0) + return ret; + + addr = videobuf_to_dma_contig(vb); + /* Make sure user addresses are aligned to 32 bytes */ + if (!ALIGN(addr, 32)) + return -EINVAL; + + vb->state = VIDEOBUF_PREPARED; + } + return 0; +} + +static void vpfe_videobuf_queue(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + /* Get the file handle object and device object */ + struct vpfe_fh *fh = vq->priv_data; + struct vpfe_device *vpfe_dev = fh->vpfe_dev; + unsigned long flags; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_queue\n"); + + /* add the buffer to the DMA queue */ + spin_lock_irqsave(&vpfe_dev->dma_queue_lock, flags); + list_add_tail(&vb->queue, &vpfe_dev->dma_queue); + spin_unlock_irqrestore(&vpfe_dev->dma_queue_lock, flags); + + /* Change state of the buffer */ + vb->state = VIDEOBUF_QUEUED; +} + +static void vpfe_videobuf_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct vpfe_fh *fh = vq->priv_data; + struct vpfe_device *vpfe_dev = fh->vpfe_dev; + unsigned long flags; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_videobuf_release\n"); + + /* + * We need to flush the buffer from the dma queue since + * they are de-allocated + */ + spin_lock_irqsave(&vpfe_dev->dma_queue_lock, flags); + INIT_LIST_HEAD(&vpfe_dev->dma_queue); + spin_unlock_irqrestore(&vpfe_dev->dma_queue_lock, flags); + videobuf_dma_contig_free(vq, vb); + vb->state = VIDEOBUF_NEEDS_INIT; +} + +static const struct videobuf_queue_ops vpfe_videobuf_qops = { + .buf_setup = vpfe_videobuf_setup, + .buf_prepare = vpfe_videobuf_prepare, + .buf_queue = vpfe_videobuf_queue, + .buf_release = vpfe_videobuf_release, +}; + +/* + * vpfe_reqbufs. currently support REQBUF only once opening + * the device. + */ +static int vpfe_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *req_buf) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_fh *fh = file->private_data; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_reqbufs\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != req_buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buffer type\n"); + return -EINVAL; + } + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + if (vpfe_dev->io_usrs != 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "Only one IO user allowed\n"); + ret = -EBUSY; + goto unlock_out; + } + + vpfe_dev->memory = req_buf->memory; + videobuf_queue_dma_contig_init(&vpfe_dev->buffer_queue, + &vpfe_videobuf_qops, + vpfe_dev->pdev, + &vpfe_dev->irqlock, + req_buf->type, + vpfe_dev->fmt.fmt.pix.field, + sizeof(struct videobuf_buffer), + fh, NULL); + + fh->io_allowed = 1; + vpfe_dev->io_usrs = 1; + INIT_LIST_HEAD(&vpfe_dev->dma_queue); + ret = videobuf_reqbufs(&vpfe_dev->buffer_queue, req_buf); +unlock_out: + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querybuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + if (vpfe_dev->memory != V4L2_MEMORY_MMAP) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid memory\n"); + return -EINVAL; + } + /* Call videobuf_querybuf to get information */ + return videobuf_querybuf(&vpfe_dev->buffer_queue, buf); +} + +static int vpfe_qbuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_fh *fh = file->private_data; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_qbuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != p->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + /* + * If this file handle is not allowed to do IO, + * return error + */ + if (!fh->io_allowed) { + v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + return videobuf_qbuf(&vpfe_dev->buffer_queue, p); +} + +static int vpfe_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_dqbuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + return videobuf_dqbuf(&vpfe_dev->buffer_queue, + buf, file->f_flags & O_NONBLOCK); +} + +/* + * vpfe_calculate_offsets : This function calculates buffers offset + * for top and bottom field + */ +static void vpfe_calculate_offsets(struct vpfe_device *vpfe_dev) +{ + struct v4l2_rect image_win; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_calculate_offsets\n"); + + ccdc_dev->hw_ops.get_image_window(&image_win); + vpfe_dev->field_off = image_win.height * image_win.width; +} + +/* vpfe_start_ccdc_capture: start streaming in ccdc/isif */ +static void vpfe_start_ccdc_capture(struct vpfe_device *vpfe_dev) +{ + ccdc_dev->hw_ops.enable(1); + if (ccdc_dev->hw_ops.enable_out_to_sdram) + ccdc_dev->hw_ops.enable_out_to_sdram(1); + vpfe_dev->started = 1; +} + +/* + * vpfe_streamon. Assume the DMA queue is not empty. + * application is expected to call QBUF before calling + * this ioctl. If not, driver returns error + */ +static int vpfe_streamon(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_fh *fh = file->private_data; + struct vpfe_subdev_info *sdinfo; + unsigned long addr; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamon\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf_type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + /* If file handle is not allowed IO, return error */ + if (!fh->io_allowed) { + v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + + sdinfo = vpfe_dev->current_subdev; + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + video, s_stream, 1); + + if (ret && (ret != -ENOIOCTLCMD)) { + v4l2_err(&vpfe_dev->v4l2_dev, "stream on failed in subdev\n"); + return -EINVAL; + } + + /* If buffer queue is empty, return error */ + if (list_empty(&vpfe_dev->buffer_queue.stream)) { + v4l2_err(&vpfe_dev->v4l2_dev, "buffer queue is empty\n"); + return -EIO; + } + + /* Call videobuf_streamon to start streaming * in videobuf */ + ret = videobuf_streamon(&vpfe_dev->buffer_queue); + if (ret) + return ret; + + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + goto streamoff; + /* Get the next frame from the buffer queue */ + vpfe_dev->next_frm = list_entry(vpfe_dev->dma_queue.next, + struct videobuf_buffer, queue); + vpfe_dev->cur_frm = vpfe_dev->next_frm; + /* Remove buffer from the buffer queue */ + list_del(&vpfe_dev->cur_frm->queue); + /* Mark state of the current frame to active */ + vpfe_dev->cur_frm->state = VIDEOBUF_ACTIVE; + /* Initialize field_id and started member */ + vpfe_dev->field_id = 0; + addr = videobuf_to_dma_contig(vpfe_dev->cur_frm); + + /* Calculate field offset */ + vpfe_calculate_offsets(vpfe_dev); + + if (vpfe_attach_irq(vpfe_dev) < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Error in attaching interrupt handle\n"); + ret = -EFAULT; + goto unlock_out; + } + if (ccdc_dev->hw_ops.configure() < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Error in configuring ccdc\n"); + ret = -EINVAL; + goto unlock_out; + } + ccdc_dev->hw_ops.setfbaddr((unsigned long)(addr)); + vpfe_start_ccdc_capture(vpfe_dev); + mutex_unlock(&vpfe_dev->lock); + return ret; +unlock_out: + mutex_unlock(&vpfe_dev->lock); +streamoff: + videobuf_streamoff(&vpfe_dev->buffer_queue); + return ret; +} + +static int vpfe_streamoff(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_fh *fh = file->private_data; + struct vpfe_subdev_info *sdinfo; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamoff\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf_type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + /* If io is allowed for this file handle, return error */ + if (!fh->io_allowed) { + v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + + /* If streaming is not started, return error */ + if (!vpfe_dev->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "device started\n"); + return -EINVAL; + } + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + vpfe_stop_ccdc_capture(vpfe_dev); + vpfe_detach_irq(vpfe_dev); + + sdinfo = vpfe_dev->current_subdev; + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + video, s_stream, 0); + + if (ret && (ret != -ENOIOCTLCMD)) + v4l2_err(&vpfe_dev->v4l2_dev, "stream off failed in subdev\n"); + ret = videobuf_streamoff(&vpfe_dev->buffer_queue); + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_g_pixelaspect(struct file *file, void *priv, + int type, struct v4l2_fract *f) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_pixelaspect\n"); + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + /* If std_index is invalid, then just return (== 1:1 aspect) */ + if (vpfe_dev->std_index >= ARRAY_SIZE(vpfe_standards)) + return 0; + + *f = vpfe_standards[vpfe_dev->std_index].pixelaspect; + return 0; +} + +static int vpfe_g_selection(struct file *file, void *priv, + struct v4l2_selection *sel) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_selection\n"); + + if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = vpfe_dev->crop; + break; + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.width = vpfe_standards[vpfe_dev->std_index].width; + sel->r.height = vpfe_standards[vpfe_dev->std_index].height; + break; + default: + return -EINVAL; + } + return 0; +} + +static int vpfe_s_selection(struct file *file, void *priv, + struct v4l2_selection *sel) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct v4l2_rect rect = sel->r; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_selection\n"); + + if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + if (vpfe_dev->started) { + /* make sure streaming is not started */ + v4l2_err(&vpfe_dev->v4l2_dev, + "Cannot change crop when streaming is ON\n"); + return -EBUSY; + } + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + if (rect.top < 0 || rect.left < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "doesn't support negative values for top & left\n"); + ret = -EINVAL; + goto unlock_out; + } + + /* adjust the width to 16 pixel boundary */ + rect.width = ((rect.width + 15) & ~0xf); + + /* make sure parameters are valid */ + if ((rect.left + rect.width > + vpfe_dev->std_info.active_pixels) || + (rect.top + rect.height > + vpfe_dev->std_info.active_lines)) { + v4l2_err(&vpfe_dev->v4l2_dev, "Error in S_SELECTION params\n"); + ret = -EINVAL; + goto unlock_out; + } + ccdc_dev->hw_ops.set_image_window(&rect); + vpfe_dev->fmt.fmt.pix.width = rect.width; + vpfe_dev->fmt.fmt.pix.height = rect.height; + vpfe_dev->fmt.fmt.pix.bytesperline = + ccdc_dev->hw_ops.get_line_length(); + vpfe_dev->fmt.fmt.pix.sizeimage = + vpfe_dev->fmt.fmt.pix.bytesperline * + vpfe_dev->fmt.fmt.pix.height; + vpfe_dev->crop = rect; + sel->r = rect; +unlock_out: + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +/* vpfe capture ioctl operations */ +static const struct v4l2_ioctl_ops vpfe_ioctl_ops = { + .vidioc_querycap = vpfe_querycap, + .vidioc_g_fmt_vid_cap = vpfe_g_fmt_vid_cap, + .vidioc_enum_fmt_vid_cap = vpfe_enum_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vpfe_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vpfe_try_fmt_vid_cap, + .vidioc_enum_input = vpfe_enum_input, + .vidioc_g_input = vpfe_g_input, + .vidioc_s_input = vpfe_s_input, + .vidioc_querystd = vpfe_querystd, + .vidioc_s_std = vpfe_s_std, + .vidioc_g_std = vpfe_g_std, + .vidioc_reqbufs = vpfe_reqbufs, + .vidioc_querybuf = vpfe_querybuf, + .vidioc_qbuf = vpfe_qbuf, + .vidioc_dqbuf = vpfe_dqbuf, + .vidioc_streamon = vpfe_streamon, + .vidioc_streamoff = vpfe_streamoff, + .vidioc_g_pixelaspect = vpfe_g_pixelaspect, + .vidioc_g_selection = vpfe_g_selection, + .vidioc_s_selection = vpfe_s_selection, +}; + +static struct vpfe_device *vpfe_initialize(void) +{ + struct vpfe_device *vpfe_dev; + + /* Default number of buffers should be 3 */ + if ((numbuffers > 0) && + (numbuffers < config_params.min_numbuffers)) + numbuffers = config_params.min_numbuffers; + + /* + * Set buffer size to min buffers size if invalid buffer size is + * given + */ + if (bufsize < config_params.min_bufsize) + bufsize = config_params.min_bufsize; + + config_params.numbuffers = numbuffers; + + if (numbuffers) + config_params.device_bufsize = bufsize; + + /* Allocate memory for device objects */ + vpfe_dev = kzalloc(sizeof(*vpfe_dev), GFP_KERNEL); + + return vpfe_dev; +} + +/* + * vpfe_probe : This function creates device entries by register + * itself to the V4L2 driver and initializes fields of each + * device objects + */ +static int vpfe_probe(struct platform_device *pdev) +{ + struct vpfe_subdev_info *sdinfo; + struct vpfe_config *vpfe_cfg; + struct resource *res1; + struct vpfe_device *vpfe_dev; + struct i2c_adapter *i2c_adap; + struct video_device *vfd; + int ret, i, j; + int num_subdevs = 0; + + /* Get the pointer to the device object */ + vpfe_dev = vpfe_initialize(); + + if (!vpfe_dev) { + v4l2_err(pdev->dev.driver, + "Failed to allocate memory for vpfe_dev\n"); + return -ENOMEM; + } + + vpfe_dev->pdev = &pdev->dev; + + if (!pdev->dev.platform_data) { + v4l2_err(pdev->dev.driver, "Unable to get vpfe config\n"); + ret = -ENODEV; + goto probe_free_dev_mem; + } + + vpfe_cfg = pdev->dev.platform_data; + vpfe_dev->cfg = vpfe_cfg; + if (!vpfe_cfg->ccdc || !vpfe_cfg->card_name || !vpfe_cfg->sub_devs) { + v4l2_err(pdev->dev.driver, "null ptr in vpfe_cfg\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + + /* Allocate memory for ccdc configuration */ + ccdc_cfg = kmalloc(sizeof(*ccdc_cfg), GFP_KERNEL); + if (!ccdc_cfg) { + ret = -ENOMEM; + goto probe_free_dev_mem; + } + + mutex_lock(&ccdc_lock); + + strscpy(ccdc_cfg->name, vpfe_cfg->ccdc, sizeof(ccdc_cfg->name)); + /* Get VINT0 irq resource */ + res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res1) { + v4l2_err(pdev->dev.driver, + "Unable to get interrupt for VINT0\n"); + ret = -ENODEV; + goto probe_free_ccdc_cfg_mem; + } + vpfe_dev->ccdc_irq0 = res1->start; + + /* Get VINT1 irq resource */ + res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 1); + if (!res1) { + v4l2_err(pdev->dev.driver, + "Unable to get interrupt for VINT1\n"); + ret = -ENODEV; + goto probe_free_ccdc_cfg_mem; + } + vpfe_dev->ccdc_irq1 = res1->start; + + ret = request_irq(vpfe_dev->ccdc_irq0, vpfe_isr, 0, + "vpfe_capture0", vpfe_dev); + + if (0 != ret) { + v4l2_err(pdev->dev.driver, "Unable to request interrupt\n"); + goto probe_free_ccdc_cfg_mem; + } + + vfd = &vpfe_dev->video_dev; + /* Initialize field of video device */ + vfd->release = video_device_release_empty; + vfd->fops = &vpfe_fops; + vfd->ioctl_ops = &vpfe_ioctl_ops; + vfd->tvnorms = 0; + vfd->v4l2_dev = &vpfe_dev->v4l2_dev; + vfd->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + snprintf(vfd->name, sizeof(vfd->name), + "%s_V%d.%d.%d", + CAPTURE_DRV_NAME, + (VPFE_CAPTURE_VERSION_CODE >> 16) & 0xff, + (VPFE_CAPTURE_VERSION_CODE >> 8) & 0xff, + (VPFE_CAPTURE_VERSION_CODE) & 0xff); + + ret = v4l2_device_register(&pdev->dev, &vpfe_dev->v4l2_dev); + if (ret) { + v4l2_err(pdev->dev.driver, + "Unable to register v4l2 device.\n"); + goto probe_out_release_irq; + } + v4l2_info(&vpfe_dev->v4l2_dev, "v4l2 device registered\n"); + spin_lock_init(&vpfe_dev->irqlock); + spin_lock_init(&vpfe_dev->dma_queue_lock); + mutex_init(&vpfe_dev->lock); + + /* Initialize field of the device objects */ + vpfe_dev->numbuffers = config_params.numbuffers; + + /* register video device */ + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "trying to register vpfe device.\n"); + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "video_dev=%p\n", &vpfe_dev->video_dev); + vpfe_dev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ret = video_register_device(&vpfe_dev->video_dev, + VFL_TYPE_VIDEO, -1); + + if (ret) { + v4l2_err(pdev->dev.driver, + "Unable to register video device.\n"); + goto probe_out_v4l2_unregister; + } + + v4l2_info(&vpfe_dev->v4l2_dev, "video device registered\n"); + /* set the driver data in platform device */ + platform_set_drvdata(pdev, vpfe_dev); + /* set driver private data */ + video_set_drvdata(&vpfe_dev->video_dev, vpfe_dev); + i2c_adap = i2c_get_adapter(vpfe_cfg->i2c_adapter_id); + num_subdevs = vpfe_cfg->num_subdevs; + vpfe_dev->sd = kmalloc_array(num_subdevs, + sizeof(*vpfe_dev->sd), + GFP_KERNEL); + if (!vpfe_dev->sd) { + ret = -ENOMEM; + goto probe_out_video_unregister; + } + + for (i = 0; i < num_subdevs; i++) { + struct v4l2_input *inps; + + sdinfo = &vpfe_cfg->sub_devs[i]; + + /* Load up the subdevice */ + vpfe_dev->sd[i] = + v4l2_i2c_new_subdev_board(&vpfe_dev->v4l2_dev, + i2c_adap, + &sdinfo->board_info, + NULL); + if (vpfe_dev->sd[i]) { + v4l2_info(&vpfe_dev->v4l2_dev, + "v4l2 sub device %s registered\n", + sdinfo->name); + vpfe_dev->sd[i]->grp_id = sdinfo->grp_id; + /* update tvnorms from the sub devices */ + for (j = 0; j < sdinfo->num_inputs; j++) { + inps = &sdinfo->inputs[j]; + vfd->tvnorms |= inps->std; + } + } else { + v4l2_info(&vpfe_dev->v4l2_dev, + "v4l2 sub device %s register fails\n", + sdinfo->name); + ret = -ENXIO; + goto probe_sd_out; + } + } + + /* set first sub device as current one */ + vpfe_dev->current_subdev = &vpfe_cfg->sub_devs[0]; + vpfe_dev->v4l2_dev.ctrl_handler = vpfe_dev->sd[0]->ctrl_handler; + + /* We have at least one sub device to work with */ + mutex_unlock(&ccdc_lock); + return 0; + +probe_sd_out: + kfree(vpfe_dev->sd); +probe_out_video_unregister: + video_unregister_device(&vpfe_dev->video_dev); +probe_out_v4l2_unregister: + v4l2_device_unregister(&vpfe_dev->v4l2_dev); +probe_out_release_irq: + free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); +probe_free_ccdc_cfg_mem: + kfree(ccdc_cfg); + mutex_unlock(&ccdc_lock); +probe_free_dev_mem: + kfree(vpfe_dev); + return ret; +} + +/* + * vpfe_remove : It un-register device from V4L2 driver + */ +static int vpfe_remove(struct platform_device *pdev) +{ + struct vpfe_device *vpfe_dev = platform_get_drvdata(pdev); + + v4l2_info(pdev->dev.driver, "vpfe_remove\n"); + + free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); + kfree(vpfe_dev->sd); + v4l2_device_unregister(&vpfe_dev->v4l2_dev); + video_unregister_device(&vpfe_dev->video_dev); + kfree(vpfe_dev); + kfree(ccdc_cfg); + return 0; +} + +static int vpfe_suspend(struct device *dev) +{ + return 0; +} + +static int vpfe_resume(struct device *dev) +{ + return 0; +} + +static const struct dev_pm_ops vpfe_dev_pm_ops = { + .suspend = vpfe_suspend, + .resume = vpfe_resume, +}; + +static struct platform_driver vpfe_driver = { + .driver = { + .name = CAPTURE_DRV_NAME, + .pm = &vpfe_dev_pm_ops, + }, + .probe = vpfe_probe, + .remove = vpfe_remove, +}; + +module_platform_driver(vpfe_driver); diff --git a/drivers/staging/media/deprecated/zr364xx/Kconfig b/drivers/staging/media/deprecated/zr364xx/Kconfig new file mode 100644 index 000000000000..ea29c9d8dca2 --- /dev/null +++ b/drivers/staging/media/deprecated/zr364xx/Kconfig @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-only +config USB_ZR364XX + tristate "USB ZR364XX Camera support (DEPRECATED)" + depends on USB && VIDEO_DEV + select VIDEOBUF_GEN + select VIDEOBUF_VMALLOC + help + Say Y here if you want to connect this type of camera to your + computer's USB port. + See <file:Documentation/admin-guide/media/zr364xx.rst> for more info + and list of supported cameras. + + This driver is deprecated and is scheduled for removal by + the beginning of 2023. See the TODO file for more information. + + To compile this driver as a module, choose M here: the + module will be called zr364xx. + diff --git a/drivers/staging/media/deprecated/zr364xx/Makefile b/drivers/staging/media/deprecated/zr364xx/Makefile new file mode 100644 index 000000000000..edab017d499c --- /dev/null +++ b/drivers/staging/media/deprecated/zr364xx/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_USB_ZR364XX) += zr364xx.o + diff --git a/drivers/staging/media/deprecated/zr364xx/TODO b/drivers/staging/media/deprecated/zr364xx/TODO new file mode 100644 index 000000000000..ecb30a429689 --- /dev/null +++ b/drivers/staging/media/deprecated/zr364xx/TODO @@ -0,0 +1,7 @@ +This is one of the few drivers still not using the vb2 +framework, so this driver is now deprecated with the intent of +removing it altogether by the beginning of 2023. + +In order to keep this driver it has to be converted to vb2. +If someone is interested in doing this work, then contact the +linux-media mailinglist (https://linuxtv.org/lists.php). diff --git a/drivers/staging/media/deprecated/zr364xx/zr364xx.c b/drivers/staging/media/deprecated/zr364xx/zr364xx.c new file mode 100644 index 000000000000..538a330046ec --- /dev/null +++ b/drivers/staging/media/deprecated/zr364xx/zr364xx.c @@ -0,0 +1,1635 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Zoran 364xx based USB webcam module version 0.73 + * + * Allows you to use your USB webcam with V4L2 applications + * This is still in heavy development ! + * + * Copyright (C) 2004 Antoine Jacquet <royale@zerezo.com> + * http://royale.zerezo.com/zr364xx/ + * + * Heavily inspired by usb-skeleton.c, vicam.c, cpia.c and spca50x.c drivers + * V4L2 version inspired by meye.c driver + * + * Some video buffer code by Lamarque based on s2255drv.c and vivi.c drivers. + */ + + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/usb.h> +#include <linux/vmalloc.h> +#include <linux/slab.h> +#include <linux/highmem.h> +#include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-event.h> +#include <media/videobuf-vmalloc.h> + + +/* Version Information */ +#define DRIVER_VERSION "0.7.4" +#define DRIVER_AUTHOR "Antoine Jacquet, http://royale.zerezo.com/" +#define DRIVER_DESC "Zoran 364xx" + + +/* Camera */ +#define FRAMES 1 +#define MAX_FRAME_SIZE 200000 +#define BUFFER_SIZE 0x1000 +#define CTRL_TIMEOUT 500 + +#define ZR364XX_DEF_BUFS 4 +#define ZR364XX_READ_IDLE 0 +#define ZR364XX_READ_FRAME 1 + +/* Debug macro */ +#define DBG(fmt, args...) \ + do { \ + if (debug) { \ + printk(KERN_INFO KBUILD_MODNAME " " fmt, ##args); \ + } \ + } while (0) + +/*#define FULL_DEBUG 1*/ +#ifdef FULL_DEBUG +#define _DBG DBG +#else +#define _DBG(fmt, args...) +#endif + +/* Init methods, need to find nicer names for these + * the exact names of the chipsets would be the best if someone finds it */ +#define METHOD0 0 +#define METHOD1 1 +#define METHOD2 2 +#define METHOD3 3 + + +/* Module parameters */ +static int debug; +static int mode; + + +/* Module parameters interface */ +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level"); +module_param(mode, int, 0644); +MODULE_PARM_DESC(mode, "0 = 320x240, 1 = 160x120, 2 = 640x480"); + + +/* Devices supported by this driver + * .driver_info contains the init method used by the camera */ +static const struct usb_device_id device_table[] = { + {USB_DEVICE(0x08ca, 0x0109), .driver_info = METHOD0 }, + {USB_DEVICE(0x041e, 0x4024), .driver_info = METHOD0 }, + {USB_DEVICE(0x0d64, 0x0108), .driver_info = METHOD0 }, + {USB_DEVICE(0x0546, 0x3187), .driver_info = METHOD0 }, + {USB_DEVICE(0x0d64, 0x3108), .driver_info = METHOD0 }, + {USB_DEVICE(0x0595, 0x4343), .driver_info = METHOD0 }, + {USB_DEVICE(0x0bb0, 0x500d), .driver_info = METHOD0 }, + {USB_DEVICE(0x0feb, 0x2004), .driver_info = METHOD0 }, + {USB_DEVICE(0x055f, 0xb500), .driver_info = METHOD0 }, + {USB_DEVICE(0x08ca, 0x2062), .driver_info = METHOD2 }, + {USB_DEVICE(0x052b, 0x1a18), .driver_info = METHOD1 }, + {USB_DEVICE(0x04c8, 0x0729), .driver_info = METHOD0 }, + {USB_DEVICE(0x04f2, 0xa208), .driver_info = METHOD0 }, + {USB_DEVICE(0x0784, 0x0040), .driver_info = METHOD1 }, + {USB_DEVICE(0x06d6, 0x0034), .driver_info = METHOD0 }, + {USB_DEVICE(0x0a17, 0x0062), .driver_info = METHOD2 }, + {USB_DEVICE(0x06d6, 0x003b), .driver_info = METHOD0 }, + {USB_DEVICE(0x0a17, 0x004e), .driver_info = METHOD2 }, + {USB_DEVICE(0x041e, 0x405d), .driver_info = METHOD2 }, + {USB_DEVICE(0x08ca, 0x2102), .driver_info = METHOD3 }, + {USB_DEVICE(0x06d6, 0x003d), .driver_info = METHOD0 }, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +/* frame structure */ +struct zr364xx_framei { + unsigned long ulState; /* ulState:ZR364XX_READ_IDLE, + ZR364XX_READ_FRAME */ + void *lpvbits; /* image data */ + unsigned long cur_size; /* current data copied to it */ +}; + +/* image buffer structure */ +struct zr364xx_bufferi { + unsigned long dwFrames; /* number of frames in buffer */ + struct zr364xx_framei frame[FRAMES]; /* array of FRAME structures */ +}; + +struct zr364xx_dmaqueue { + struct list_head active; + struct zr364xx_camera *cam; +}; + +struct zr364xx_pipeinfo { + u32 transfer_size; + u8 *transfer_buffer; + u32 state; + void *stream_urb; + void *cam; /* back pointer to zr364xx_camera struct */ + u32 err_count; + u32 idx; +}; + +struct zr364xx_fmt { + u32 fourcc; + int depth; +}; + +/* image formats. */ +static const struct zr364xx_fmt formats[] = { + { + .fourcc = V4L2_PIX_FMT_JPEG, + .depth = 24 + } +}; + +/* Camera stuff */ +struct zr364xx_camera { + struct usb_device *udev; /* save off the usb device pointer */ + struct usb_interface *interface;/* the interface for this device */ + struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler ctrl_handler; + struct video_device vdev; /* v4l video device */ + struct v4l2_fh *owner; /* owns the streaming */ + int nb; + struct zr364xx_bufferi buffer; + int skip; + int width; + int height; + int method; + struct mutex lock; + + spinlock_t slock; + struct zr364xx_dmaqueue vidq; + int last_frame; + int cur_frame; + unsigned long frame_count; + int b_acquire; + struct zr364xx_pipeinfo pipe[1]; + + u8 read_endpoint; + + const struct zr364xx_fmt *fmt; + struct videobuf_queue vb_vidq; + bool was_streaming; +}; + +/* buffer for one video frame */ +struct zr364xx_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + const struct zr364xx_fmt *fmt; +}; + +/* function used to send initialisation commands to the camera */ +static int send_control_msg(struct usb_device *udev, u8 request, u16 value, + u16 index, unsigned char *cp, u16 size) +{ + int status; + + unsigned char *transfer_buffer = kmemdup(cp, size, GFP_KERNEL); + if (!transfer_buffer) + return -ENOMEM; + + status = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + request, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, value, index, + transfer_buffer, size, CTRL_TIMEOUT); + + kfree(transfer_buffer); + return status; +} + + +/* Control messages sent to the camera to initialize it + * and launch the capture */ +typedef struct { + unsigned int value; + unsigned int size; + unsigned char *bytes; +} message; + +/* method 0 */ +static unsigned char m0d1[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char m0d2[] = { 0, 0, 0, 0, 0, 0 }; +static unsigned char m0d3[] = { 0, 0 }; +static message m0[] = { + {0x1f30, 0, NULL}, + {0xd000, 0, NULL}, + {0x3370, sizeof(m0d1), m0d1}, + {0x2000, 0, NULL}, + {0x2f0f, 0, NULL}, + {0x2610, sizeof(m0d2), m0d2}, + {0xe107, 0, NULL}, + {0x2502, 0, NULL}, + {0x1f70, 0, NULL}, + {0xd000, 0, NULL}, + {0x9a01, sizeof(m0d3), m0d3}, + {-1, -1, NULL} +}; + +/* method 1 */ +static unsigned char m1d1[] = { 0xff, 0xff }; +static unsigned char m1d2[] = { 0x00, 0x00 }; +static message m1[] = { + {0x1f30, 0, NULL}, + {0xd000, 0, NULL}, + {0xf000, 0, NULL}, + {0x2000, 0, NULL}, + {0x2f0f, 0, NULL}, + {0x2650, 0, NULL}, + {0xe107, 0, NULL}, + {0x2502, sizeof(m1d1), m1d1}, + {0x1f70, 0, NULL}, + {0xd000, 0, NULL}, + {0xd000, 0, NULL}, + {0xd000, 0, NULL}, + {0x9a01, sizeof(m1d2), m1d2}, + {-1, -1, NULL} +}; + +/* method 2 */ +static unsigned char m2d1[] = { 0xff, 0xff }; +static message m2[] = { + {0x1f30, 0, NULL}, + {0xf000, 0, NULL}, + {0x2000, 0, NULL}, + {0x2f0f, 0, NULL}, + {0x2650, 0, NULL}, + {0xe107, 0, NULL}, + {0x2502, sizeof(m2d1), m2d1}, + {0x1f70, 0, NULL}, + {-1, -1, NULL} +}; + +/* init table */ +static message *init[4] = { m0, m1, m2, m2 }; + + +/* JPEG static data in header (Huffman table, etc) */ +static unsigned char header1[] = { + 0xFF, 0xD8, + /* + 0xFF, 0xE0, 0x00, 0x10, 'J', 'F', 'I', 'F', + 0x00, 0x01, 0x01, 0x00, 0x33, 0x8A, 0x00, 0x00, 0x33, 0x88, + */ + 0xFF, 0xDB, 0x00, 0x84 +}; +static unsigned char header2[] = { + 0xFF, 0xC4, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, + 0xFF, 0xC4, 0x00, 0xB5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, + 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01, + 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, + 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, + 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, + 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, + 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, + 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, + 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, + 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, + 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, + 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, + 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, + 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, + 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xC4, 0x00, 0x1F, + 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5, + 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, + 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, + 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, + 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, + 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, + 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, + 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, + 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, + 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, + 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, + 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, + 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, + 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0xFA, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0xF0, 0x01, + 0x40, 0x03, 0x01, 0x21, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, + 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, + 0x00, 0x3F, 0x00 +}; +static unsigned char header3; + +/* ------------------------------------------------------------------ + Videobuf operations + ------------------------------------------------------------------*/ + +static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct zr364xx_camera *cam = vq->priv_data; + + *size = cam->width * cam->height * (cam->fmt->depth >> 3); + + if (*count == 0) + *count = ZR364XX_DEF_BUFS; + + if (*size * *count > ZR364XX_DEF_BUFS * 1024 * 1024) + *count = (ZR364XX_DEF_BUFS * 1024 * 1024) / *size; + + return 0; +} + +static void free_buffer(struct videobuf_queue *vq, struct zr364xx_buffer *buf) +{ + _DBG("%s\n", __func__); + + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct zr364xx_camera *cam = vq->priv_data; + struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer, + vb); + int rc; + + DBG("%s, field=%d\n", __func__, field); + if (!cam->fmt) + return -EINVAL; + + buf->vb.size = cam->width * cam->height * (cam->fmt->depth >> 3); + + if (buf->vb.baddr != 0 && buf->vb.bsize < buf->vb.size) { + DBG("invalid buffer prepare\n"); + return -EINVAL; + } + + buf->fmt = cam->fmt; + buf->vb.width = cam->width; + buf->vb.height = cam->height; + buf->vb.field = field; + + if (buf->vb.state == VIDEOBUF_NEEDS_INIT) { + rc = videobuf_iolock(vq, &buf->vb, NULL); + if (rc < 0) + goto fail; + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; +fail: + free_buffer(vq, buf); + return rc; +} + +static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer, + vb); + struct zr364xx_camera *cam = vq->priv_data; + + _DBG("%s\n", __func__); + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &cam->vidq.active); +} + +static void buffer_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer, + vb); + + _DBG("%s\n", __func__); + free_buffer(vq, buf); +} + +static const struct videobuf_queue_ops zr364xx_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +/********************/ +/* V4L2 integration */ +/********************/ +static int zr364xx_vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type); + +static ssize_t zr364xx_read(struct file *file, char __user *buf, size_t count, + loff_t * ppos) +{ + struct zr364xx_camera *cam = video_drvdata(file); + int err = 0; + + _DBG("%s\n", __func__); + + if (!buf) + return -EINVAL; + + if (!count) + return -EINVAL; + + if (mutex_lock_interruptible(&cam->lock)) + return -ERESTARTSYS; + + err = zr364xx_vidioc_streamon(file, file->private_data, + V4L2_BUF_TYPE_VIDEO_CAPTURE); + if (err == 0) { + DBG("%s: reading %d bytes at pos %d.\n", __func__, + (int) count, (int) *ppos); + + /* NoMan Sux ! */ + err = videobuf_read_one(&cam->vb_vidq, buf, count, ppos, + file->f_flags & O_NONBLOCK); + } + mutex_unlock(&cam->lock); + return err; +} + +/* video buffer vmalloc implementation based partly on VIVI driver which is + * Copyright (c) 2006 by + * Mauro Carvalho Chehab <mchehab--a.t--infradead.org> + * Ted Walther <ted--a.t--enumera.com> + * John Sokol <sokol--a.t--videotechnology.com> + * http://v4l.videotechnology.com/ + * + */ +static void zr364xx_fillbuff(struct zr364xx_camera *cam, + struct zr364xx_buffer *buf, + int jpgsize) +{ + int pos = 0; + const char *tmpbuf; + char *vbuf = videobuf_to_vmalloc(&buf->vb); + unsigned long last_frame; + + if (!vbuf) + return; + + last_frame = cam->last_frame; + if (last_frame != -1) { + tmpbuf = (const char *)cam->buffer.frame[last_frame].lpvbits; + switch (buf->fmt->fourcc) { + case V4L2_PIX_FMT_JPEG: + buf->vb.size = jpgsize; + memcpy(vbuf, tmpbuf, buf->vb.size); + break; + default: + printk(KERN_DEBUG KBUILD_MODNAME ": unknown format?\n"); + } + cam->last_frame = -1; + } else { + printk(KERN_ERR KBUILD_MODNAME ": =======no frame\n"); + return; + } + DBG("%s: Buffer %p size= %d\n", __func__, vbuf, pos); + /* tell v4l buffer was filled */ + + buf->vb.field_count = cam->frame_count * 2; + buf->vb.ts = ktime_get_ns(); + buf->vb.state = VIDEOBUF_DONE; +} + +static int zr364xx_got_frame(struct zr364xx_camera *cam, int jpgsize) +{ + struct zr364xx_dmaqueue *dma_q = &cam->vidq; + struct zr364xx_buffer *buf; + unsigned long flags = 0; + int rc = 0; + + DBG("wakeup: %p\n", &dma_q); + spin_lock_irqsave(&cam->slock, flags); + + if (list_empty(&dma_q->active)) { + DBG("No active queue to serve\n"); + rc = -1; + goto unlock; + } + buf = list_entry(dma_q->active.next, + struct zr364xx_buffer, vb.queue); + + if (!waitqueue_active(&buf->vb.done)) { + /* no one active */ + rc = -1; + goto unlock; + } + list_del(&buf->vb.queue); + buf->vb.ts = ktime_get_ns(); + DBG("[%p/%d] wakeup\n", buf, buf->vb.i); + zr364xx_fillbuff(cam, buf, jpgsize); + wake_up(&buf->vb.done); + DBG("wakeup [buf/i] [%p/%d]\n", buf, buf->vb.i); +unlock: + spin_unlock_irqrestore(&cam->slock, flags); + return rc; +} + +/* this function moves the usb stream read pipe data + * into the system buffers. + * returns 0 on success, EAGAIN if more data to process (call this + * function again). + */ +static int zr364xx_read_video_callback(struct zr364xx_camera *cam, + struct zr364xx_pipeinfo *pipe_info, + struct urb *purb) +{ + unsigned char *pdest; + unsigned char *psrc; + s32 idx = cam->cur_frame; + struct zr364xx_framei *frm = &cam->buffer.frame[idx]; + int i = 0; + unsigned char *ptr = NULL; + + _DBG("buffer to user\n"); + + /* swap bytes if camera needs it */ + if (cam->method == METHOD0) { + u16 *buf = (u16 *)pipe_info->transfer_buffer; + for (i = 0; i < purb->actual_length/2; i++) + swab16s(buf + i); + } + + /* search done. now find out if should be acquiring */ + if (!cam->b_acquire) { + /* we found a frame, but this channel is turned off */ + frm->ulState = ZR364XX_READ_IDLE; + return -EINVAL; + } + + psrc = (u8 *)pipe_info->transfer_buffer; + ptr = pdest = frm->lpvbits; + + if (frm->ulState == ZR364XX_READ_IDLE) { + if (purb->actual_length < 128) { + /* header incomplete */ + dev_info(&cam->udev->dev, + "%s: buffer (%d bytes) too small to hold jpeg header. Discarding.\n", + __func__, purb->actual_length); + return -EINVAL; + } + + frm->ulState = ZR364XX_READ_FRAME; + frm->cur_size = 0; + + _DBG("jpeg header, "); + memcpy(ptr, header1, sizeof(header1)); + ptr += sizeof(header1); + header3 = 0; + memcpy(ptr, &header3, 1); + ptr++; + memcpy(ptr, psrc, 64); + ptr += 64; + header3 = 1; + memcpy(ptr, &header3, 1); + ptr++; + memcpy(ptr, psrc + 64, 64); + ptr += 64; + memcpy(ptr, header2, sizeof(header2)); + ptr += sizeof(header2); + memcpy(ptr, psrc + 128, + purb->actual_length - 128); + ptr += purb->actual_length - 128; + _DBG("header : %d %d %d %d %d %d %d %d %d\n", + psrc[0], psrc[1], psrc[2], + psrc[3], psrc[4], psrc[5], + psrc[6], psrc[7], psrc[8]); + frm->cur_size = ptr - pdest; + } else { + if (frm->cur_size + purb->actual_length > MAX_FRAME_SIZE) { + dev_info(&cam->udev->dev, + "%s: buffer (%d bytes) too small to hold frame data. Discarding frame data.\n", + __func__, MAX_FRAME_SIZE); + } else { + pdest += frm->cur_size; + memcpy(pdest, psrc, purb->actual_length); + frm->cur_size += purb->actual_length; + } + } + /*_DBG("cur_size %lu urb size %d\n", frm->cur_size, + purb->actual_length);*/ + + if (purb->actual_length < pipe_info->transfer_size) { + _DBG("****************Buffer[%d]full*************\n", idx); + cam->last_frame = cam->cur_frame; + cam->cur_frame++; + /* end of system frame ring buffer, start at zero */ + if (cam->cur_frame == cam->buffer.dwFrames) + cam->cur_frame = 0; + + /* frame ready */ + /* go back to find the JPEG EOI marker */ + ptr = pdest = frm->lpvbits; + ptr += frm->cur_size - 2; + while (ptr > pdest) { + if (*ptr == 0xFF && *(ptr + 1) == 0xD9 + && *(ptr + 2) == 0xFF) + break; + ptr--; + } + if (ptr == pdest) + DBG("No EOI marker\n"); + + /* Sometimes there is junk data in the middle of the picture, + * we want to skip this bogus frames */ + while (ptr > pdest) { + if (*ptr == 0xFF && *(ptr + 1) == 0xFF + && *(ptr + 2) == 0xFF) + break; + ptr--; + } + if (ptr != pdest) { + DBG("Bogus frame ? %d\n", ++(cam->nb)); + } else if (cam->b_acquire) { + /* we skip the 2 first frames which are usually buggy */ + if (cam->skip) + cam->skip--; + else { + _DBG("jpeg(%lu): %d %d %d %d %d %d %d %d\n", + frm->cur_size, + pdest[0], pdest[1], pdest[2], pdest[3], + pdest[4], pdest[5], pdest[6], pdest[7]); + + zr364xx_got_frame(cam, frm->cur_size); + } + } + cam->frame_count++; + frm->ulState = ZR364XX_READ_IDLE; + frm->cur_size = 0; + } + /* done successfully */ + return 0; +} + +static int zr364xx_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct zr364xx_camera *cam = video_drvdata(file); + + strscpy(cap->driver, DRIVER_DESC, sizeof(cap->driver)); + if (cam->udev->product) + strscpy(cap->card, cam->udev->product, sizeof(cap->card)); + strscpy(cap->bus_info, dev_name(&cam->udev->dev), + sizeof(cap->bus_info)); + return 0; +} + +static int zr364xx_vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + if (i->index != 0) + return -EINVAL; + strscpy(i->name, DRIVER_DESC " Camera", sizeof(i->name)); + i->type = V4L2_INPUT_TYPE_CAMERA; + return 0; +} + +static int zr364xx_vidioc_g_input(struct file *file, void *priv, + unsigned int *i) +{ + *i = 0; + return 0; +} + +static int zr364xx_vidioc_s_input(struct file *file, void *priv, + unsigned int i) +{ + if (i != 0) + return -EINVAL; + return 0; +} + +static int zr364xx_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct zr364xx_camera *cam = + container_of(ctrl->handler, struct zr364xx_camera, ctrl_handler); + int temp; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + /* hardware brightness */ + send_control_msg(cam->udev, 1, 0x2001, 0, NULL, 0); + temp = (0x60 << 8) + 127 - ctrl->val; + send_control_msg(cam->udev, 1, temp, 0, NULL, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int zr364xx_vidioc_enum_fmt_vid_cap(struct file *file, + void *priv, struct v4l2_fmtdesc *f) +{ + if (f->index > 0) + return -EINVAL; + f->pixelformat = formats[0].fourcc; + return 0; +} + +static char *decode_fourcc(__u32 pixelformat, char *buf) +{ + buf[0] = pixelformat & 0xff; + buf[1] = (pixelformat >> 8) & 0xff; + buf[2] = (pixelformat >> 16) & 0xff; + buf[3] = (pixelformat >> 24) & 0xff; + buf[4] = '\0'; + return buf; +} + +static int zr364xx_vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct zr364xx_camera *cam = video_drvdata(file); + char pixelformat_name[5]; + + if (!cam) + return -ENODEV; + + if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) { + DBG("%s: unsupported pixelformat V4L2_PIX_FMT_%s\n", __func__, + decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name)); + return -EINVAL; + } + + if (!(f->fmt.pix.width == 160 && f->fmt.pix.height == 120) && + !(f->fmt.pix.width == 640 && f->fmt.pix.height == 480)) { + f->fmt.pix.width = 320; + f->fmt.pix.height = 240; + } + + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; + DBG("%s: V4L2_PIX_FMT_%s (%d) ok!\n", __func__, + decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name), + f->fmt.pix.field); + return 0; +} + +static int zr364xx_vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct zr364xx_camera *cam; + + if (!file) + return -ENODEV; + cam = video_drvdata(file); + + f->fmt.pix.pixelformat = formats[0].fourcc; + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.width = cam->width; + f->fmt.pix.height = cam->height; + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; + return 0; +} + +static int zr364xx_vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct zr364xx_camera *cam = video_drvdata(file); + struct videobuf_queue *q = &cam->vb_vidq; + char pixelformat_name[5]; + int ret = zr364xx_vidioc_try_fmt_vid_cap(file, cam, f); + int i; + + if (ret < 0) + return ret; + + mutex_lock(&q->vb_lock); + + if (videobuf_queue_is_busy(&cam->vb_vidq)) { + DBG("%s queue busy\n", __func__); + ret = -EBUSY; + goto out; + } + + if (cam->owner) { + DBG("%s can't change format after started\n", __func__); + ret = -EBUSY; + goto out; + } + + cam->width = f->fmt.pix.width; + cam->height = f->fmt.pix.height; + DBG("%s: %dx%d mode selected\n", __func__, + cam->width, cam->height); + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; + cam->vb_vidq.field = f->fmt.pix.field; + + if (f->fmt.pix.width == 160 && f->fmt.pix.height == 120) + mode = 1; + else if (f->fmt.pix.width == 640 && f->fmt.pix.height == 480) + mode = 2; + else + mode = 0; + + m0d1[0] = mode; + m1[2].value = 0xf000 + mode; + m2[1].value = 0xf000 + mode; + + /* special case for METHOD3, the modes are different */ + if (cam->method == METHOD3) { + switch (mode) { + case 1: + m2[1].value = 0xf000 + 4; + break; + case 2: + m2[1].value = 0xf000 + 0; + break; + default: + m2[1].value = 0xf000 + 1; + break; + } + } + + header2[437] = cam->height / 256; + header2[438] = cam->height % 256; + header2[439] = cam->width / 256; + header2[440] = cam->width % 256; + + for (i = 0; init[cam->method][i].size != -1; i++) { + ret = + send_control_msg(cam->udev, 1, init[cam->method][i].value, + 0, init[cam->method][i].bytes, + init[cam->method][i].size); + if (ret < 0) { + dev_err(&cam->udev->dev, + "error during resolution change sequence: %d\n", i); + goto out; + } + } + + /* Added some delay here, since opening/closing the camera quickly, + * like Ekiga does during its startup, can crash the webcam + */ + mdelay(100); + cam->skip = 2; + ret = 0; + +out: + mutex_unlock(&q->vb_lock); + + DBG("%s: V4L2_PIX_FMT_%s (%d) ok!\n", __func__, + decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name), + f->fmt.pix.field); + return ret; +} + +static int zr364xx_vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + struct zr364xx_camera *cam = video_drvdata(file); + + if (cam->owner && cam->owner != priv) + return -EBUSY; + return videobuf_reqbufs(&cam->vb_vidq, p); +} + +static int zr364xx_vidioc_querybuf(struct file *file, + void *priv, + struct v4l2_buffer *p) +{ + int rc; + struct zr364xx_camera *cam = video_drvdata(file); + rc = videobuf_querybuf(&cam->vb_vidq, p); + return rc; +} + +static int zr364xx_vidioc_qbuf(struct file *file, + void *priv, + struct v4l2_buffer *p) +{ + int rc; + struct zr364xx_camera *cam = video_drvdata(file); + _DBG("%s\n", __func__); + if (cam->owner && cam->owner != priv) + return -EBUSY; + rc = videobuf_qbuf(&cam->vb_vidq, p); + return rc; +} + +static int zr364xx_vidioc_dqbuf(struct file *file, + void *priv, + struct v4l2_buffer *p) +{ + int rc; + struct zr364xx_camera *cam = video_drvdata(file); + _DBG("%s\n", __func__); + if (cam->owner && cam->owner != priv) + return -EBUSY; + rc = videobuf_dqbuf(&cam->vb_vidq, p, file->f_flags & O_NONBLOCK); + return rc; +} + +static void read_pipe_completion(struct urb *purb) +{ + struct zr364xx_pipeinfo *pipe_info; + struct zr364xx_camera *cam; + int pipe; + + pipe_info = purb->context; + _DBG("%s %p, status %d\n", __func__, purb, purb->status); + if (!pipe_info) { + printk(KERN_ERR KBUILD_MODNAME ": no context!\n"); + return; + } + + cam = pipe_info->cam; + if (!cam) { + printk(KERN_ERR KBUILD_MODNAME ": no context!\n"); + return; + } + + /* if shutting down, do not resubmit, exit immediately */ + if (purb->status == -ESHUTDOWN) { + DBG("%s, err shutdown\n", __func__); + pipe_info->err_count++; + return; + } + + if (pipe_info->state == 0) { + DBG("exiting USB pipe\n"); + return; + } + + if (purb->actual_length > pipe_info->transfer_size) { + dev_err(&cam->udev->dev, "wrong number of bytes\n"); + return; + } + + if (purb->status == 0) + zr364xx_read_video_callback(cam, pipe_info, purb); + else { + pipe_info->err_count++; + DBG("%s: failed URB %d\n", __func__, purb->status); + } + + pipe = usb_rcvbulkpipe(cam->udev, cam->read_endpoint); + + /* reuse urb */ + usb_fill_bulk_urb(pipe_info->stream_urb, cam->udev, + pipe, + pipe_info->transfer_buffer, + pipe_info->transfer_size, + read_pipe_completion, pipe_info); + + if (pipe_info->state != 0) { + purb->status = usb_submit_urb(pipe_info->stream_urb, + GFP_ATOMIC); + + if (purb->status) + dev_err(&cam->udev->dev, + "error submitting urb (error=%i)\n", + purb->status); + } else + DBG("read pipe complete state 0\n"); +} + +static int zr364xx_start_readpipe(struct zr364xx_camera *cam) +{ + int pipe; + int retval; + struct zr364xx_pipeinfo *pipe_info = cam->pipe; + pipe = usb_rcvbulkpipe(cam->udev, cam->read_endpoint); + DBG("%s: start pipe IN x%x\n", __func__, cam->read_endpoint); + + pipe_info->state = 1; + pipe_info->err_count = 0; + pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!pipe_info->stream_urb) + return -ENOMEM; + /* transfer buffer allocated in board_init */ + usb_fill_bulk_urb(pipe_info->stream_urb, cam->udev, + pipe, + pipe_info->transfer_buffer, + pipe_info->transfer_size, + read_pipe_completion, pipe_info); + + DBG("submitting URB %p\n", pipe_info->stream_urb); + retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL); + if (retval) { + usb_free_urb(pipe_info->stream_urb); + printk(KERN_ERR KBUILD_MODNAME ": start read pipe failed\n"); + return retval; + } + + return 0; +} + +static void zr364xx_stop_readpipe(struct zr364xx_camera *cam) +{ + struct zr364xx_pipeinfo *pipe_info; + + if (!cam) { + printk(KERN_ERR KBUILD_MODNAME ": invalid device\n"); + return; + } + DBG("stop read pipe\n"); + pipe_info = cam->pipe; + if (pipe_info) { + if (pipe_info->state != 0) + pipe_info->state = 0; + + if (pipe_info->stream_urb) { + /* cancel urb */ + usb_kill_urb(pipe_info->stream_urb); + usb_free_urb(pipe_info->stream_urb); + pipe_info->stream_urb = NULL; + } + } + return; +} + +/* starts acquisition process */ +static int zr364xx_start_acquire(struct zr364xx_camera *cam) +{ + int j; + + DBG("start acquire\n"); + + cam->last_frame = -1; + cam->cur_frame = 0; + for (j = 0; j < FRAMES; j++) { + cam->buffer.frame[j].ulState = ZR364XX_READ_IDLE; + cam->buffer.frame[j].cur_size = 0; + } + cam->b_acquire = 1; + return 0; +} + +static inline int zr364xx_stop_acquire(struct zr364xx_camera *cam) +{ + cam->b_acquire = 0; + return 0; +} + +static int zr364xx_prepare(struct zr364xx_camera *cam) +{ + int res; + int i, j; + + for (i = 0; init[cam->method][i].size != -1; i++) { + res = send_control_msg(cam->udev, 1, init[cam->method][i].value, + 0, init[cam->method][i].bytes, + init[cam->method][i].size); + if (res < 0) { + dev_err(&cam->udev->dev, + "error during open sequence: %d\n", i); + return res; + } + } + + cam->skip = 2; + cam->last_frame = -1; + cam->cur_frame = 0; + cam->frame_count = 0; + for (j = 0; j < FRAMES; j++) { + cam->buffer.frame[j].ulState = ZR364XX_READ_IDLE; + cam->buffer.frame[j].cur_size = 0; + } + v4l2_ctrl_handler_setup(&cam->ctrl_handler); + return 0; +} + +static int zr364xx_vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct zr364xx_camera *cam = video_drvdata(file); + int res; + + DBG("%s\n", __func__); + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (cam->owner && cam->owner != priv) + return -EBUSY; + + res = zr364xx_prepare(cam); + if (res) + return res; + res = videobuf_streamon(&cam->vb_vidq); + if (res == 0) { + zr364xx_start_acquire(cam); + cam->owner = file->private_data; + } + return res; +} + +static int zr364xx_vidioc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct zr364xx_camera *cam = video_drvdata(file); + + DBG("%s\n", __func__); + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (cam->owner && cam->owner != priv) + return -EBUSY; + zr364xx_stop_acquire(cam); + return videobuf_streamoff(&cam->vb_vidq); +} + + +/* open the camera */ +static int zr364xx_open(struct file *file) +{ + struct zr364xx_camera *cam = video_drvdata(file); + int err; + + DBG("%s\n", __func__); + + if (mutex_lock_interruptible(&cam->lock)) + return -ERESTARTSYS; + + err = v4l2_fh_open(file); + if (err) + goto out; + + /* Added some delay here, since opening/closing the camera quickly, + * like Ekiga does during its startup, can crash the webcam + */ + mdelay(100); + err = 0; + +out: + mutex_unlock(&cam->lock); + DBG("%s: %d\n", __func__, err); + return err; +} + +static void zr364xx_board_uninit(struct zr364xx_camera *cam) +{ + unsigned long i; + + zr364xx_stop_readpipe(cam); + + /* release sys buffers */ + for (i = 0; i < FRAMES; i++) { + if (cam->buffer.frame[i].lpvbits) { + DBG("vfree %p\n", cam->buffer.frame[i].lpvbits); + vfree(cam->buffer.frame[i].lpvbits); + } + cam->buffer.frame[i].lpvbits = NULL; + } + + /* release transfer buffer */ + kfree(cam->pipe->transfer_buffer); +} + +static void zr364xx_release(struct v4l2_device *v4l2_dev) +{ + struct zr364xx_camera *cam = + container_of(v4l2_dev, struct zr364xx_camera, v4l2_dev); + + videobuf_mmap_free(&cam->vb_vidq); + v4l2_ctrl_handler_free(&cam->ctrl_handler); + zr364xx_board_uninit(cam); + v4l2_device_unregister(&cam->v4l2_dev); + kfree(cam); +} + +/* release the camera */ +static int zr364xx_close(struct file *file) +{ + struct zr364xx_camera *cam; + struct usb_device *udev; + int i; + + DBG("%s\n", __func__); + cam = video_drvdata(file); + + mutex_lock(&cam->lock); + udev = cam->udev; + + if (file->private_data == cam->owner) { + /* turn off stream */ + if (cam->b_acquire) + zr364xx_stop_acquire(cam); + videobuf_streamoff(&cam->vb_vidq); + + for (i = 0; i < 2; i++) { + send_control_msg(udev, 1, init[cam->method][i].value, + 0, init[cam->method][i].bytes, + init[cam->method][i].size); + } + cam->owner = NULL; + } + + /* Added some delay here, since opening/closing the camera quickly, + * like Ekiga does during its startup, can crash the webcam + */ + mdelay(100); + mutex_unlock(&cam->lock); + return v4l2_fh_release(file); +} + + +static int zr364xx_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct zr364xx_camera *cam = video_drvdata(file); + int ret; + + if (!cam) { + DBG("%s: cam == NULL\n", __func__); + return -ENODEV; + } + DBG("mmap called, vma=%p\n", vma); + + ret = videobuf_mmap_mapper(&cam->vb_vidq, vma); + + DBG("vma start=0x%08lx, size=%ld, ret=%d\n", + (unsigned long)vma->vm_start, + (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, ret); + return ret; +} + +static __poll_t zr364xx_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct zr364xx_camera *cam = video_drvdata(file); + struct videobuf_queue *q = &cam->vb_vidq; + __poll_t res = v4l2_ctrl_poll(file, wait); + + _DBG("%s\n", __func__); + + return res | videobuf_poll_stream(file, q, wait); +} + +static const struct v4l2_ctrl_ops zr364xx_ctrl_ops = { + .s_ctrl = zr364xx_s_ctrl, +}; + +static const struct v4l2_file_operations zr364xx_fops = { + .owner = THIS_MODULE, + .open = zr364xx_open, + .release = zr364xx_close, + .read = zr364xx_read, + .mmap = zr364xx_mmap, + .unlocked_ioctl = video_ioctl2, + .poll = zr364xx_poll, +}; + +static const struct v4l2_ioctl_ops zr364xx_ioctl_ops = { + .vidioc_querycap = zr364xx_vidioc_querycap, + .vidioc_enum_fmt_vid_cap = zr364xx_vidioc_enum_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = zr364xx_vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = zr364xx_vidioc_s_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = zr364xx_vidioc_g_fmt_vid_cap, + .vidioc_enum_input = zr364xx_vidioc_enum_input, + .vidioc_g_input = zr364xx_vidioc_g_input, + .vidioc_s_input = zr364xx_vidioc_s_input, + .vidioc_streamon = zr364xx_vidioc_streamon, + .vidioc_streamoff = zr364xx_vidioc_streamoff, + .vidioc_reqbufs = zr364xx_vidioc_reqbufs, + .vidioc_querybuf = zr364xx_vidioc_querybuf, + .vidioc_qbuf = zr364xx_vidioc_qbuf, + .vidioc_dqbuf = zr364xx_vidioc_dqbuf, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static const struct video_device zr364xx_template = { + .name = DRIVER_DESC, + .fops = &zr364xx_fops, + .ioctl_ops = &zr364xx_ioctl_ops, + .release = video_device_release_empty, + .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING, +}; + + + +/*******************/ +/* USB integration */ +/*******************/ +static int zr364xx_board_init(struct zr364xx_camera *cam) +{ + struct zr364xx_pipeinfo *pipe = cam->pipe; + unsigned long i; + int err; + + DBG("board init: %p\n", cam); + memset(pipe, 0, sizeof(*pipe)); + pipe->cam = cam; + pipe->transfer_size = BUFFER_SIZE; + + pipe->transfer_buffer = kzalloc(pipe->transfer_size, + GFP_KERNEL); + if (!pipe->transfer_buffer) { + DBG("out of memory!\n"); + return -ENOMEM; + } + + cam->b_acquire = 0; + cam->frame_count = 0; + + /*** start create system buffers ***/ + for (i = 0; i < FRAMES; i++) { + /* always allocate maximum size for system buffers */ + cam->buffer.frame[i].lpvbits = vmalloc(MAX_FRAME_SIZE); + + DBG("valloc %p, idx %lu, pdata %p\n", + &cam->buffer.frame[i], i, + cam->buffer.frame[i].lpvbits); + if (!cam->buffer.frame[i].lpvbits) { + printk(KERN_INFO KBUILD_MODNAME ": out of memory. Using less frames\n"); + break; + } + } + + if (i == 0) { + printk(KERN_INFO KBUILD_MODNAME ": out of memory. Aborting\n"); + err = -ENOMEM; + goto err_free; + } else + cam->buffer.dwFrames = i; + + /* make sure internal states are set */ + for (i = 0; i < FRAMES; i++) { + cam->buffer.frame[i].ulState = ZR364XX_READ_IDLE; + cam->buffer.frame[i].cur_size = 0; + } + + cam->cur_frame = 0; + cam->last_frame = -1; + /*** end create system buffers ***/ + + /* start read pipe */ + err = zr364xx_start_readpipe(cam); + if (err) + goto err_free_frames; + + DBG(": board initialized\n"); + return 0; + +err_free_frames: + for (i = 0; i < FRAMES; i++) + vfree(cam->buffer.frame[i].lpvbits); +err_free: + kfree(cam->pipe->transfer_buffer); + cam->pipe->transfer_buffer = NULL; + return err; +} + +static int zr364xx_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct zr364xx_camera *cam = NULL; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + struct v4l2_ctrl_handler *hdl; + int err; + int i; + + DBG("probing...\n"); + + dev_info(&intf->dev, DRIVER_DESC " compatible webcam plugged\n"); + dev_info(&intf->dev, "model %04x:%04x detected\n", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct)); + + cam = kzalloc(sizeof(*cam), GFP_KERNEL); + if (!cam) + return -ENOMEM; + + err = v4l2_device_register(&intf->dev, &cam->v4l2_dev); + if (err < 0) { + dev_err(&udev->dev, "couldn't register v4l2_device\n"); + goto free_cam; + } + hdl = &cam->ctrl_handler; + v4l2_ctrl_handler_init(hdl, 1); + v4l2_ctrl_new_std(hdl, &zr364xx_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 127, 1, 64); + if (hdl->error) { + err = hdl->error; + dev_err(&udev->dev, "couldn't register control\n"); + goto free_hdlr_and_unreg_dev; + } + /* save the init method used by this camera */ + cam->method = id->driver_info; + mutex_init(&cam->lock); + cam->vdev = zr364xx_template; + cam->vdev.lock = &cam->lock; + cam->vdev.v4l2_dev = &cam->v4l2_dev; + cam->vdev.ctrl_handler = &cam->ctrl_handler; + video_set_drvdata(&cam->vdev, cam); + + cam->udev = udev; + + switch (mode) { + case 1: + dev_info(&udev->dev, "160x120 mode selected\n"); + cam->width = 160; + cam->height = 120; + break; + case 2: + dev_info(&udev->dev, "640x480 mode selected\n"); + cam->width = 640; + cam->height = 480; + break; + default: + dev_info(&udev->dev, "320x240 mode selected\n"); + cam->width = 320; + cam->height = 240; + break; + } + + m0d1[0] = mode; + m1[2].value = 0xf000 + mode; + m2[1].value = 0xf000 + mode; + + /* special case for METHOD3, the modes are different */ + if (cam->method == METHOD3) { + switch (mode) { + case 1: + m2[1].value = 0xf000 + 4; + break; + case 2: + m2[1].value = 0xf000 + 0; + break; + default: + m2[1].value = 0xf000 + 1; + break; + } + } + + header2[437] = cam->height / 256; + header2[438] = cam->height % 256; + header2[439] = cam->width / 256; + header2[440] = cam->width % 256; + + cam->nb = 0; + + DBG("dev: %p, udev %p interface %p\n", cam, cam->udev, intf); + + /* set up the endpoint information */ + iface_desc = intf->cur_altsetting; + DBG("num endpoints %d\n", iface_desc->desc.bNumEndpoints); + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + if (!cam->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) { + /* we found the bulk in endpoint */ + cam->read_endpoint = endpoint->bEndpointAddress; + } + } + + if (!cam->read_endpoint) { + err = -ENOMEM; + dev_err(&intf->dev, "Could not find bulk-in endpoint\n"); + goto free_hdlr_and_unreg_dev; + } + + /* v4l */ + INIT_LIST_HEAD(&cam->vidq.active); + cam->vidq.cam = cam; + + usb_set_intfdata(intf, cam); + + /* load zr364xx board specific */ + err = zr364xx_board_init(cam); + if (err) + goto free_hdlr_and_unreg_dev; + err = v4l2_ctrl_handler_setup(hdl); + if (err) + goto board_uninit; + + spin_lock_init(&cam->slock); + + cam->fmt = formats; + + videobuf_queue_vmalloc_init(&cam->vb_vidq, &zr364xx_video_qops, + NULL, &cam->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_NONE, + sizeof(struct zr364xx_buffer), cam, &cam->lock); + + err = video_register_device(&cam->vdev, VFL_TYPE_VIDEO, -1); + if (err) { + dev_err(&udev->dev, "video_register_device failed\n"); + goto board_uninit; + } + cam->v4l2_dev.release = zr364xx_release; + + dev_info(&udev->dev, DRIVER_DESC " controlling device %s\n", + video_device_node_name(&cam->vdev)); + return 0; + +board_uninit: + zr364xx_board_uninit(cam); +free_hdlr_and_unreg_dev: + v4l2_ctrl_handler_free(hdl); + v4l2_device_unregister(&cam->v4l2_dev); +free_cam: + kfree(cam); + return err; +} + + +static void zr364xx_disconnect(struct usb_interface *intf) +{ + struct zr364xx_camera *cam = usb_get_intfdata(intf); + + mutex_lock(&cam->lock); + usb_set_intfdata(intf, NULL); + dev_info(&intf->dev, DRIVER_DESC " webcam unplugged\n"); + video_unregister_device(&cam->vdev); + v4l2_device_disconnect(&cam->v4l2_dev); + + /* stops the read pipe if it is running */ + if (cam->b_acquire) + zr364xx_stop_acquire(cam); + + zr364xx_stop_readpipe(cam); + mutex_unlock(&cam->lock); + v4l2_device_put(&cam->v4l2_dev); +} + + +#ifdef CONFIG_PM +static int zr364xx_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct zr364xx_camera *cam = usb_get_intfdata(intf); + + cam->was_streaming = cam->b_acquire; + if (!cam->was_streaming) + return 0; + zr364xx_stop_acquire(cam); + zr364xx_stop_readpipe(cam); + return 0; +} + +static int zr364xx_resume(struct usb_interface *intf) +{ + struct zr364xx_camera *cam = usb_get_intfdata(intf); + int res; + + if (!cam->was_streaming) + return 0; + + res = zr364xx_start_readpipe(cam); + if (res) + return res; + + res = zr364xx_prepare(cam); + if (res) + goto err_prepare; + + zr364xx_start_acquire(cam); + return 0; + +err_prepare: + zr364xx_stop_readpipe(cam); + return res; +} +#endif + +/**********************/ +/* Module integration */ +/**********************/ + +static struct usb_driver zr364xx_driver = { + .name = "zr364xx", + .probe = zr364xx_probe, + .disconnect = zr364xx_disconnect, +#ifdef CONFIG_PM + .suspend = zr364xx_suspend, + .resume = zr364xx_resume, + .reset_resume = zr364xx_resume, +#endif + .id_table = device_table +}; + +module_usb_driver(zr364xx_driver); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); diff --git a/drivers/staging/media/hantro/Kconfig b/drivers/staging/media/hantro/Kconfig deleted file mode 100644 index 0172a6822ec2..000000000000 --- a/drivers/staging/media/hantro/Kconfig +++ /dev/null @@ -1,50 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -config VIDEO_HANTRO - tristate "Hantro VPU driver" - depends on ARCH_MXC || ARCH_ROCKCHIP || ARCH_AT91 || ARCH_SUNXI || COMPILE_TEST - depends on VIDEO_DEV - select MEDIA_CONTROLLER - select MEDIA_CONTROLLER_REQUEST_API - select VIDEOBUF2_DMA_CONTIG - select VIDEOBUF2_VMALLOC - select V4L2_MEM2MEM_DEV - select V4L2_H264 - select V4L2_VP9 - help - 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_SAMA5D4 - bool "Hantro VDEC SAMA5D4 support" - depends on VIDEO_HANTRO - depends on ARCH_AT91 || COMPILE_TEST - default y - help - Enable support for Microchip SAMA5D4 SoCs. - -config VIDEO_HANTRO_ROCKCHIP - bool "Hantro VPU Rockchip support" - depends on VIDEO_HANTRO - depends on ARCH_ROCKCHIP || COMPILE_TEST - default y - help - Enable support for RK3288, RK3328, and RK3399 SoCs. - -config VIDEO_HANTRO_SUNXI - bool "Hantro VPU Allwinner support" - depends on VIDEO_HANTRO - depends on ARCH_SUNXI || COMPILE_TEST - default y - help - Enable support for H6 SoC. diff --git a/drivers/staging/media/hantro/Makefile b/drivers/staging/media/hantro/Makefile deleted file mode 100644 index ebd5ede7bef7..000000000000 --- a/drivers/staging/media/hantro/Makefile +++ /dev/null @@ -1,38 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -obj-$(CONFIG_VIDEO_HANTRO) += hantro-vpu.o - -hantro-vpu-y += \ - hantro_drv.o \ - hantro_v4l2.o \ - hantro_postproc.o \ - hantro_h1_jpeg_enc.o \ - hantro_g1.o \ - hantro_g1_h264_dec.o \ - hantro_g1_mpeg2_dec.o \ - hantro_g1_vp8_dec.o \ - hantro_g2.o \ - hantro_g2_hevc_dec.o \ - hantro_g2_vp9_dec.o \ - rockchip_vpu2_hw_jpeg_enc.o \ - rockchip_vpu2_hw_h264_dec.o \ - rockchip_vpu2_hw_mpeg2_dec.o \ - rockchip_vpu2_hw_vp8_dec.o \ - hantro_jpeg.o \ - hantro_h264.o \ - hantro_hevc.o \ - hantro_mpeg2.o \ - hantro_vp8.o \ - hantro_vp9.o - -hantro-vpu-$(CONFIG_VIDEO_HANTRO_IMX8M) += \ - imx8m_vpu_hw.o - -hantro-vpu-$(CONFIG_VIDEO_HANTRO_SAMA5D4) += \ - sama5d4_vdec_hw.o - -hantro-vpu-$(CONFIG_VIDEO_HANTRO_ROCKCHIP) += \ - rockchip_vpu_hw.o - -hantro-vpu-$(CONFIG_VIDEO_HANTRO_SUNXI) += \ - sunxi_vpu_hw.o diff --git a/drivers/staging/media/hantro/TODO b/drivers/staging/media/hantro/TODO deleted file mode 100644 index 8483ff482146..000000000000 --- a/drivers/staging/media/hantro/TODO +++ /dev/null @@ -1,2 +0,0 @@ -The V4L controls for the HEVC CODEC are not yet part of the stable uABI, -we are keeping this driver in staging until the HEVC uABI has been merged. diff --git a/drivers/staging/media/hantro/hantro.h b/drivers/staging/media/hantro/hantro.h deleted file mode 100644 index 2989ebc631cc..000000000000 --- a/drivers/staging/media/hantro/hantro.h +++ /dev/null @@ -1,485 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Hantro VPU codec driver - * - * Copyright 2018 Google LLC. - * Tomasz Figa <tfiga@chromium.org> - * - * Based on s5p-mfc driver by Samsung Electronics Co., Ltd. - * Copyright (C) 2011 Samsung Electronics Co., Ltd. - */ - -#ifndef HANTRO_H_ -#define HANTRO_H_ - -#include <linux/platform_device.h> -#include <linux/videodev2.h> -#include <linux/wait.h> -#include <linux/clk.h> -#include <linux/reset.h> - -#include <media/v4l2-ctrls.h> -#include <media/v4l2-device.h> -#include <media/v4l2-ioctl.h> -#include <media/v4l2-mem2mem.h> -#include <media/videobuf2-core.h> -#include <media/videobuf2-dma-contig.h> - -#include "hantro_hw.h" - -struct hantro_ctx; -struct hantro_codec_ops; -struct hantro_postproc_ops; - -#define HANTRO_JPEG_ENCODER BIT(0) -#define HANTRO_ENCODERS 0x0000ffff -#define HANTRO_MPEG2_DECODER BIT(16) -#define HANTRO_VP8_DECODER BIT(17) -#define HANTRO_H264_DECODER BIT(18) -#define HANTRO_HEVC_DECODER BIT(19) -#define HANTRO_VP9_DECODER BIT(20) -#define HANTRO_DECODERS 0xffff0000 - -/** - * struct hantro_irq - irq handler and name - * - * @name: irq name for device tree lookup - * @handler: interrupt handler - */ -struct hantro_irq { - const char *name; - irqreturn_t (*handler)(int irq, void *priv); -}; - -/** - * struct hantro_variant - information about VPU hardware variant - * - * @enc_offset: Offset from VPU base to encoder registers. - * @dec_offset: Offset from VPU base to decoder registers. - * @enc_fmts: Encoder formats. - * @num_enc_fmts: Number of encoder formats. - * @dec_fmts: Decoder formats. - * @num_dec_fmts: Number of decoder formats. - * @postproc_fmts: Post-processor formats. - * @num_postproc_fmts: Number of post-processor formats. - * @postproc_ops: Post-processor ops. - * @codec: Supported codecs - * @codec_ops: Codec ops. - * @init: Initialize hardware, optional. - * @runtime_resume: reenable hardware after power gating, optional. - * @irqs: array of irq names and interrupt handlers - * @num_irqs: number of irqs in the array - * @clk_names: array of clock names - * @num_clocks: number of clocks in the array - * @reg_names: array of register range names - * @num_regs: number of register range names in the array - * @double_buffer: core needs double buffering - * @legacy_regs: core uses legacy register set - * @late_postproc: postproc must be set up at the end of the job - */ -struct hantro_variant { - unsigned int enc_offset; - unsigned int dec_offset; - const struct hantro_fmt *enc_fmts; - unsigned int num_enc_fmts; - const struct hantro_fmt *dec_fmts; - unsigned int num_dec_fmts; - const struct hantro_fmt *postproc_fmts; - unsigned int num_postproc_fmts; - const struct hantro_postproc_ops *postproc_ops; - unsigned int codec; - const struct hantro_codec_ops *codec_ops; - int (*init)(struct hantro_dev *vpu); - int (*runtime_resume)(struct hantro_dev *vpu); - const struct hantro_irq *irqs; - int num_irqs; - const char * const *clk_names; - int num_clocks; - const char * const *reg_names; - int num_regs; - unsigned int double_buffer : 1; - unsigned int legacy_regs : 1; - unsigned int late_postproc : 1; -}; - -/** - * enum hantro_codec_mode - codec operating mode. - * @HANTRO_MODE_NONE: No operating mode. Used for RAW video formats. - * @HANTRO_MODE_JPEG_ENC: JPEG encoder. - * @HANTRO_MODE_H264_DEC: H264 decoder. - * @HANTRO_MODE_MPEG2_DEC: MPEG-2 decoder. - * @HANTRO_MODE_VP8_DEC: VP8 decoder. - * @HANTRO_MODE_HEVC_DEC: HEVC decoder. - * @HANTRO_MODE_VP9_DEC: VP9 decoder. - */ -enum hantro_codec_mode { - HANTRO_MODE_NONE = -1, - HANTRO_MODE_JPEG_ENC, - HANTRO_MODE_H264_DEC, - HANTRO_MODE_MPEG2_DEC, - HANTRO_MODE_VP8_DEC, - HANTRO_MODE_HEVC_DEC, - HANTRO_MODE_VP9_DEC, -}; - -/* - * struct hantro_ctrl - helper type to declare supported controls - * @codec: codec id this control belong to (HANTRO_JPEG_ENCODER, etc.) - * @cfg: control configuration - */ -struct hantro_ctrl { - unsigned int codec; - struct v4l2_ctrl_config cfg; -}; - -/* - * struct hantro_func - Hantro VPU functionality - * - * @id: processing functionality ID (can be - * %MEDIA_ENT_F_PROC_VIDEO_ENCODER or - * %MEDIA_ENT_F_PROC_VIDEO_DECODER) - * @vdev: &struct video_device that exposes the encoder or - * decoder functionality - * @source_pad: &struct media_pad with the source pad. - * @sink: &struct media_entity pointer with the sink entity - * @sink_pad: &struct media_pad with the sink pad. - * @proc: &struct media_entity pointer with the M2M device itself. - * @proc_pads: &struct media_pad with the @proc pads. - * @intf_devnode: &struct media_intf devnode pointer with the interface - * with controls the M2M device. - * - * Contains everything needed to attach the video device to the media device. - */ -struct hantro_func { - unsigned int id; - struct video_device vdev; - struct media_pad source_pad; - struct media_entity sink; - struct media_pad sink_pad; - struct media_entity proc; - struct media_pad proc_pads[2]; - struct media_intf_devnode *intf_devnode; -}; - -static inline struct hantro_func * -hantro_vdev_to_func(struct video_device *vdev) -{ - return container_of(vdev, struct hantro_func, vdev); -} - -/** - * struct hantro_dev - driver data - * @v4l2_dev: V4L2 device to register video devices for. - * @m2m_dev: mem2mem device associated to this device. - * @mdev: media device associated to this device. - * @encoder: encoder functionality. - * @decoder: decoder functionality. - * @pdev: Pointer to VPU platform device. - * @dev: Pointer to device for convenient logging using - * dev_ macros. - * @clocks: Array of clock handles. - * @resets: Array of reset handles. - * @reg_bases: Mapped addresses of VPU registers. - * @enc_base: Mapped address of VPU encoder register for convenience. - * @dec_base: Mapped address of VPU decoder register for convenience. - * @ctrl_base: Mapped address of VPU control block. - * @vpu_mutex: Mutex to synchronize V4L2 calls. - * @irqlock: Spinlock to synchronize access to data structures - * shared with interrupt handlers. - * @variant: Hardware variant-specific parameters. - * @watchdog_work: Delayed work for hardware timeout handling. - */ -struct hantro_dev { - struct v4l2_device v4l2_dev; - struct v4l2_m2m_dev *m2m_dev; - struct media_device mdev; - struct hantro_func *encoder; - struct hantro_func *decoder; - struct platform_device *pdev; - struct device *dev; - struct clk_bulk_data *clocks; - struct reset_control *resets; - void __iomem **reg_bases; - void __iomem *enc_base; - void __iomem *dec_base; - void __iomem *ctrl_base; - - struct mutex vpu_mutex; /* video_device lock */ - spinlock_t irqlock; - const struct hantro_variant *variant; - struct delayed_work watchdog_work; -}; - -/** - * struct hantro_ctx - Context (instance) private data. - * - * @dev: VPU driver data to which the context belongs. - * @fh: V4L2 file handler. - * @is_encoder: Decoder or encoder context? - * - * @sequence_cap: Sequence counter for capture queue - * @sequence_out: Sequence counter for output queue - * - * @vpu_src_fmt: Descriptor of active source format. - * @src_fmt: V4L2 pixel format of active source format. - * @vpu_dst_fmt: Descriptor of active destination format. - * @dst_fmt: V4L2 pixel format of active destination format. - * - * @ctrl_handler: Control handler used to register controls. - * @jpeg_quality: User-specified JPEG compression quality. - * @bit_depth: Bit depth of current frame - * - * @codec_ops: Set of operations related to codec mode. - * @postproc: Post-processing context. - * @h264_dec: H.264-decoding context. - * @jpeg_enc: JPEG-encoding context. - * @mpeg2_dec: MPEG-2-decoding context. - * @vp8_dec: VP8-decoding context. - * @hevc_dec: HEVC-decoding context. - * @vp9_dec: VP9-decoding context. - */ -struct hantro_ctx { - struct hantro_dev *dev; - struct v4l2_fh fh; - bool is_encoder; - - u32 sequence_cap; - u32 sequence_out; - - const struct hantro_fmt *vpu_src_fmt; - struct v4l2_pix_format_mplane src_fmt; - const struct hantro_fmt *vpu_dst_fmt; - struct v4l2_pix_format_mplane dst_fmt; - - struct v4l2_ctrl_handler ctrl_handler; - int jpeg_quality; - int bit_depth; - - const struct hantro_codec_ops *codec_ops; - struct hantro_postproc_ctx postproc; - - /* Specific for particular codec modes. */ - union { - struct hantro_h264_dec_hw_ctx h264_dec; - struct hantro_mpeg2_dec_hw_ctx mpeg2_dec; - struct hantro_vp8_dec_hw_ctx vp8_dec; - struct hantro_hevc_dec_hw_ctx hevc_dec; - struct hantro_vp9_dec_hw_ctx vp9_dec; - }; -}; - -/** - * struct hantro_fmt - information about supported video formats. - * @name: Human readable name of the format. - * @fourcc: FourCC code of the format. See V4L2_PIX_FMT_*. - * @codec_mode: Codec mode related to this format. See - * enum hantro_codec_mode. - * @header_size: Optional header size. Currently used by JPEG encoder. - * @max_depth: Maximum depth, for bitstream formats - * @enc_fmt: Format identifier for encoder registers. - * @frmsize: Supported range of frame sizes (only for bitstream formats). - * @postprocessed: Indicates if this format needs the post-processor. - * @match_depth: Indicates if format bit depth must match video bit depth - */ -struct hantro_fmt { - char *name; - u32 fourcc; - enum hantro_codec_mode codec_mode; - int header_size; - int max_depth; - enum hantro_enc_fmt enc_fmt; - struct v4l2_frmsize_stepwise frmsize; - bool postprocessed; - bool match_depth; -}; - -struct hantro_reg { - u32 base; - u32 shift; - u32 mask; -}; - -struct hantro_postproc_regs { - struct hantro_reg pipeline_en; - struct hantro_reg max_burst; - struct hantro_reg clk_gate; - struct hantro_reg out_swap32; - struct hantro_reg out_endian; - struct hantro_reg out_luma_base; - struct hantro_reg input_width; - struct hantro_reg input_height; - struct hantro_reg output_width; - struct hantro_reg output_height; - struct hantro_reg input_fmt; - struct hantro_reg output_fmt; - struct hantro_reg orig_width; - struct hantro_reg display_width; -}; - -struct hantro_vp9_decoded_buffer_info { - /* Info needed when the decoded frame serves as a reference frame. */ - unsigned short width; - unsigned short height; - u32 bit_depth : 4; -}; - -struct hantro_decoded_buffer { - /* Must be the first field in this struct. */ - struct v4l2_m2m_buffer base; - - union { - struct hantro_vp9_decoded_buffer_info vp9; - }; -}; - -/* Logging helpers */ - -/** - * DOC: hantro_debug: Module parameter to control level of debugging messages. - * - * Level of debugging messages can be controlled by bits of - * module parameter called "debug". Meaning of particular - * bits is as follows: - * - * bit 0 - global information: mode, size, init, release - * bit 1 - each run start/result information - * bit 2 - contents of small controls from userspace - * bit 3 - contents of big controls from userspace - * bit 4 - detail fmt, ctrl, buffer q/dq information - * bit 5 - detail function enter/leave trace information - * bit 6 - register write/read information - */ -extern int hantro_debug; - -#define vpu_debug(level, fmt, args...) \ - do { \ - if (hantro_debug & BIT(level)) \ - pr_info("%s:%d: " fmt, \ - __func__, __LINE__, ##args); \ - } while (0) - -#define vpu_err(fmt, args...) \ - pr_err("%s:%d: " fmt, __func__, __LINE__, ##args) - -/* Structure access helpers. */ -static inline struct hantro_ctx *fh_to_ctx(struct v4l2_fh *fh) -{ - return container_of(fh, struct hantro_ctx, fh); -} - -/* Register accessors. */ -static inline void vepu_write_relaxed(struct hantro_dev *vpu, - u32 val, u32 reg) -{ - vpu_debug(6, "0x%04x = 0x%08x\n", reg / 4, val); - writel_relaxed(val, vpu->enc_base + reg); -} - -static inline void vepu_write(struct hantro_dev *vpu, u32 val, u32 reg) -{ - vpu_debug(6, "0x%04x = 0x%08x\n", reg / 4, val); - writel(val, vpu->enc_base + reg); -} - -static inline u32 vepu_read(struct hantro_dev *vpu, u32 reg) -{ - u32 val = readl(vpu->enc_base + reg); - - vpu_debug(6, "0x%04x = 0x%08x\n", reg / 4, val); - return val; -} - -static inline void vdpu_write_relaxed(struct hantro_dev *vpu, - u32 val, u32 reg) -{ - vpu_debug(6, "0x%04x = 0x%08x\n", reg / 4, val); - writel_relaxed(val, vpu->dec_base + reg); -} - -static inline void vdpu_write(struct hantro_dev *vpu, u32 val, u32 reg) -{ - vpu_debug(6, "0x%04x = 0x%08x\n", reg / 4, val); - writel(val, vpu->dec_base + reg); -} - -static inline void hantro_write_addr(struct hantro_dev *vpu, - unsigned long offset, - dma_addr_t addr) -{ - vdpu_write(vpu, addr & 0xffffffff, offset); -} - -static inline u32 vdpu_read(struct hantro_dev *vpu, u32 reg) -{ - u32 val = readl(vpu->dec_base + reg); - - vpu_debug(6, "0x%04x = 0x%08x\n", reg / 4, val); - return val; -} - -static inline u32 vdpu_read_mask(struct hantro_dev *vpu, - const struct hantro_reg *reg, - u32 val) -{ - u32 v; - - v = vdpu_read(vpu, reg->base); - v &= ~(reg->mask << reg->shift); - v |= ((val & reg->mask) << reg->shift); - return v; -} - -static inline void hantro_reg_write(struct hantro_dev *vpu, - const struct hantro_reg *reg, - u32 val) -{ - vdpu_write_relaxed(vpu, vdpu_read_mask(vpu, reg, val), reg->base); -} - -static inline void hantro_reg_write_s(struct hantro_dev *vpu, - const struct hantro_reg *reg, - u32 val) -{ - vdpu_write(vpu, vdpu_read_mask(vpu, reg, val), reg->base); -} - -void *hantro_get_ctrl(struct hantro_ctx *ctx, u32 id); -dma_addr_t hantro_get_ref(struct hantro_ctx *ctx, u64 ts); - -static inline struct vb2_v4l2_buffer * -hantro_get_src_buf(struct hantro_ctx *ctx) -{ - return v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); -} - -static inline struct vb2_v4l2_buffer * -hantro_get_dst_buf(struct hantro_ctx *ctx) -{ - return v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); -} - -bool hantro_needs_postproc(const struct hantro_ctx *ctx, - const struct hantro_fmt *fmt); - -static inline dma_addr_t -hantro_get_dec_buf_addr(struct hantro_ctx *ctx, struct vb2_buffer *vb) -{ - if (hantro_needs_postproc(ctx, ctx->vpu_dst_fmt)) - return ctx->postproc.dec_q[vb->index].dma; - return vb2_dma_contig_plane_dma_addr(vb, 0); -} - -static inline struct hantro_decoded_buffer * -vb2_to_hantro_decoded_buf(struct vb2_buffer *buf) -{ - return container_of(buf, struct hantro_decoded_buffer, base.vb.vb2_buf); -} - -void hantro_postproc_disable(struct hantro_ctx *ctx); -void hantro_postproc_enable(struct hantro_ctx *ctx); -void hantro_postproc_free(struct hantro_ctx *ctx); -int hantro_postproc_alloc(struct hantro_ctx *ctx); -int hanto_postproc_enum_framesizes(struct hantro_ctx *ctx, - struct v4l2_frmsizeenum *fsize); - -#endif /* HANTRO_H_ */ diff --git a/drivers/staging/media/hantro/hantro_drv.c b/drivers/staging/media/hantro/hantro_drv.c deleted file mode 100644 index 2036f72eeb4a..000000000000 --- a/drivers/staging/media/hantro/hantro_drv.c +++ /dev/null @@ -1,1146 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VPU codec driver - * - * Copyright (C) 2018 Collabora, Ltd. - * Copyright 2018 Google LLC. - * Tomasz Figa <tfiga@chromium.org> - * - * Based on s5p-mfc driver by Samsung Electronics Co., Ltd. - * Copyright (C) 2011 Samsung Electronics Co., Ltd. - */ - -#include <linux/clk.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/platform_device.h> -#include <linux/pm.h> -#include <linux/pm_runtime.h> -#include <linux/slab.h> -#include <linux/videodev2.h> -#include <linux/workqueue.h> -#include <media/v4l2-event.h> -#include <media/v4l2-mem2mem.h> -#include <media/videobuf2-core.h> -#include <media/videobuf2-vmalloc.h> - -#include "hantro_v4l2.h" -#include "hantro.h" -#include "hantro_hw.h" - -#define DRIVER_NAME "hantro-vpu" - -int hantro_debug; -module_param_named(debug, hantro_debug, int, 0644); -MODULE_PARM_DESC(debug, - "Debug level - higher value produces more verbose messages"); - -void *hantro_get_ctrl(struct hantro_ctx *ctx, u32 id) -{ - struct v4l2_ctrl *ctrl; - - ctrl = v4l2_ctrl_find(&ctx->ctrl_handler, id); - return ctrl ? ctrl->p_cur.p : NULL; -} - -dma_addr_t hantro_get_ref(struct hantro_ctx *ctx, u64 ts) -{ - struct vb2_queue *q = v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx); - struct vb2_buffer *buf; - - buf = vb2_find_buffer(q, ts); - if (!buf) - return 0; - return hantro_get_dec_buf_addr(ctx, buf); -} - -static const struct v4l2_event hantro_eos_event = { - .type = V4L2_EVENT_EOS -}; - -static void hantro_job_finish_no_pm(struct hantro_dev *vpu, - struct hantro_ctx *ctx, - enum vb2_buffer_state result) -{ - struct vb2_v4l2_buffer *src, *dst; - - src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); - dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - - if (WARN_ON(!src)) - return; - if (WARN_ON(!dst)) - return; - - src->sequence = ctx->sequence_out++; - dst->sequence = ctx->sequence_cap++; - - if (v4l2_m2m_is_last_draining_src_buf(ctx->fh.m2m_ctx, src)) { - dst->flags |= V4L2_BUF_FLAG_LAST; - v4l2_event_queue_fh(&ctx->fh, &hantro_eos_event); - v4l2_m2m_mark_stopped(ctx->fh.m2m_ctx); - } - - v4l2_m2m_buf_done_and_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx, - result); -} - -static void hantro_job_finish(struct hantro_dev *vpu, - struct hantro_ctx *ctx, - enum vb2_buffer_state result) -{ - pm_runtime_mark_last_busy(vpu->dev); - pm_runtime_put_autosuspend(vpu->dev); - - clk_bulk_disable(vpu->variant->num_clocks, vpu->clocks); - - hantro_job_finish_no_pm(vpu, ctx, result); -} - -void hantro_irq_done(struct hantro_dev *vpu, - enum vb2_buffer_state result) -{ - struct hantro_ctx *ctx = - v4l2_m2m_get_curr_priv(vpu->m2m_dev); - - /* - * If cancel_delayed_work returns false - * the timeout expired. The watchdog is running, - * and will take care of finishing the job. - */ - if (cancel_delayed_work(&vpu->watchdog_work)) { - if (result == VB2_BUF_STATE_DONE && ctx->codec_ops->done) - ctx->codec_ops->done(ctx); - hantro_job_finish(vpu, ctx, result); - } -} - -void hantro_watchdog(struct work_struct *work) -{ - struct hantro_dev *vpu; - struct hantro_ctx *ctx; - - vpu = container_of(to_delayed_work(work), - struct hantro_dev, watchdog_work); - ctx = v4l2_m2m_get_curr_priv(vpu->m2m_dev); - if (ctx) { - vpu_err("frame processing timed out!\n"); - ctx->codec_ops->reset(ctx); - hantro_job_finish(vpu, ctx, VB2_BUF_STATE_ERROR); - } -} - -void hantro_start_prepare_run(struct hantro_ctx *ctx) -{ - struct vb2_v4l2_buffer *src_buf; - - src_buf = hantro_get_src_buf(ctx); - v4l2_ctrl_request_setup(src_buf->vb2_buf.req_obj.req, - &ctx->ctrl_handler); - - if (!ctx->is_encoder && !ctx->dev->variant->late_postproc) { - if (hantro_needs_postproc(ctx, ctx->vpu_dst_fmt)) - hantro_postproc_enable(ctx); - else - hantro_postproc_disable(ctx); - } -} - -void hantro_end_prepare_run(struct hantro_ctx *ctx) -{ - struct vb2_v4l2_buffer *src_buf; - - if (!ctx->is_encoder && ctx->dev->variant->late_postproc) { - if (hantro_needs_postproc(ctx, ctx->vpu_dst_fmt)) - hantro_postproc_enable(ctx); - else - hantro_postproc_disable(ctx); - } - - src_buf = hantro_get_src_buf(ctx); - v4l2_ctrl_request_complete(src_buf->vb2_buf.req_obj.req, - &ctx->ctrl_handler); - - /* Kick the watchdog. */ - schedule_delayed_work(&ctx->dev->watchdog_work, - msecs_to_jiffies(2000)); -} - -static void device_run(void *priv) -{ - struct hantro_ctx *ctx = priv; - struct vb2_v4l2_buffer *src, *dst; - int ret; - - src = hantro_get_src_buf(ctx); - dst = hantro_get_dst_buf(ctx); - - ret = pm_runtime_resume_and_get(ctx->dev->dev); - if (ret < 0) - goto err_cancel_job; - - ret = clk_bulk_enable(ctx->dev->variant->num_clocks, ctx->dev->clocks); - if (ret) - goto err_cancel_job; - - v4l2_m2m_buf_copy_metadata(src, dst, true); - - if (ctx->codec_ops->run(ctx)) - goto err_cancel_job; - - return; - -err_cancel_job: - hantro_job_finish_no_pm(ctx->dev, ctx, VB2_BUF_STATE_ERROR); -} - -static const struct v4l2_m2m_ops vpu_m2m_ops = { - .device_run = device_run, -}; - -static int -queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) -{ - struct hantro_ctx *ctx = priv; - int ret; - - src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - src_vq->io_modes = VB2_MMAP | VB2_DMABUF; - src_vq->drv_priv = ctx; - src_vq->ops = &hantro_queue_ops; - src_vq->mem_ops = &vb2_dma_contig_memops; - - /* - * Driver does mostly sequential access, so sacrifice TLB efficiency - * for faster allocation. Also, no CPU access on the source queue, - * so no kernel mapping needed. - */ - src_vq->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES | - DMA_ATTR_NO_KERNEL_MAPPING; - src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); - src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - src_vq->lock = &ctx->dev->vpu_mutex; - src_vq->dev = ctx->dev->v4l2_dev.dev; - src_vq->supports_requests = true; - - ret = vb2_queue_init(src_vq); - if (ret) - return ret; - - dst_vq->bidirectional = true; - dst_vq->mem_ops = &vb2_dma_contig_memops; - dst_vq->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES; - /* - * The Kernel needs access to the JPEG destination buffer for the - * JPEG encoder to fill in the JPEG headers. - */ - if (!ctx->is_encoder) - dst_vq->dma_attrs |= DMA_ATTR_NO_KERNEL_MAPPING; - - dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; - dst_vq->drv_priv = ctx; - dst_vq->ops = &hantro_queue_ops; - dst_vq->buf_struct_size = sizeof(struct hantro_decoded_buffer); - dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - dst_vq->lock = &ctx->dev->vpu_mutex; - dst_vq->dev = ctx->dev->v4l2_dev.dev; - - return vb2_queue_init(dst_vq); -} - -static int hantro_try_ctrl(struct v4l2_ctrl *ctrl) -{ - if (ctrl->id == V4L2_CID_STATELESS_H264_SPS) { - const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p_h264_sps; - - if (sps->chroma_format_idc > 1) - /* Only 4:0:0 and 4:2:0 are supported */ - return -EINVAL; - if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8) - /* Luma and chroma bit depth mismatch */ - return -EINVAL; - if (sps->bit_depth_luma_minus8 != 0) - /* Only 8-bit is supported */ - return -EINVAL; - } else if (ctrl->id == V4L2_CID_STATELESS_HEVC_SPS) { - const struct v4l2_ctrl_hevc_sps *sps = ctrl->p_new.p_hevc_sps; - - if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8) - /* Luma and chroma bit depth mismatch */ - return -EINVAL; - if (sps->bit_depth_luma_minus8 != 0) - /* Only 8-bit is supported */ - return -EINVAL; - } else if (ctrl->id == V4L2_CID_STATELESS_VP9_FRAME) { - const struct v4l2_ctrl_vp9_frame *dec_params = ctrl->p_new.p_vp9_frame; - - /* We only support profile 0 */ - if (dec_params->profile != 0) - return -EINVAL; - } - return 0; -} - -static int hantro_jpeg_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct hantro_ctx *ctx; - - ctx = container_of(ctrl->handler, - struct hantro_ctx, ctrl_handler); - - vpu_debug(1, "s_ctrl: id = %d, val = %d\n", ctrl->id, ctrl->val); - - switch (ctrl->id) { - case V4L2_CID_JPEG_COMPRESSION_QUALITY: - ctx->jpeg_quality = ctrl->val; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int hantro_vp9_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct hantro_ctx *ctx; - - ctx = container_of(ctrl->handler, - struct hantro_ctx, ctrl_handler); - - switch (ctrl->id) { - case V4L2_CID_STATELESS_VP9_FRAME: - ctx->bit_depth = ctrl->p_new.p_vp9_frame->bit_depth; - break; - default: - return -EINVAL; - } - - return 0; -} - -static const struct v4l2_ctrl_ops hantro_ctrl_ops = { - .try_ctrl = hantro_try_ctrl, -}; - -static const struct v4l2_ctrl_ops hantro_jpeg_ctrl_ops = { - .s_ctrl = hantro_jpeg_s_ctrl, -}; - -static const struct v4l2_ctrl_ops hantro_vp9_ctrl_ops = { - .s_ctrl = hantro_vp9_s_ctrl, -}; - -#define HANTRO_JPEG_ACTIVE_MARKERS (V4L2_JPEG_ACTIVE_MARKER_APP0 | \ - V4L2_JPEG_ACTIVE_MARKER_COM | \ - V4L2_JPEG_ACTIVE_MARKER_DQT | \ - V4L2_JPEG_ACTIVE_MARKER_DHT) - -static const struct hantro_ctrl controls[] = { - { - .codec = HANTRO_JPEG_ENCODER, - .cfg = { - .id = V4L2_CID_JPEG_COMPRESSION_QUALITY, - .min = 5, - .max = 100, - .step = 1, - .def = 50, - .ops = &hantro_jpeg_ctrl_ops, - }, - }, { - .codec = HANTRO_JPEG_ENCODER, - .cfg = { - .id = V4L2_CID_JPEG_ACTIVE_MARKER, - .max = HANTRO_JPEG_ACTIVE_MARKERS, - .def = HANTRO_JPEG_ACTIVE_MARKERS, - /* - * Changing the set of active markers/segments also - * messes up the alignment of the JPEG header, which - * is needed to allow the hardware to write directly - * to the output buffer. Implementing this introduces - * a lot of complexity for little gain, as the markers - * enabled is already the minimum required set. - */ - .flags = V4L2_CTRL_FLAG_READ_ONLY, - }, - }, { - .codec = HANTRO_MPEG2_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_MPEG2_SEQUENCE, - }, - }, { - .codec = HANTRO_MPEG2_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_MPEG2_PICTURE, - }, - }, { - .codec = HANTRO_MPEG2_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_MPEG2_QUANTISATION, - }, - }, { - .codec = HANTRO_VP8_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_VP8_FRAME, - }, - }, { - .codec = HANTRO_H264_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_H264_DECODE_PARAMS, - }, - }, { - .codec = HANTRO_H264_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_H264_SPS, - .ops = &hantro_ctrl_ops, - }, - }, { - .codec = HANTRO_H264_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_H264_PPS, - }, - }, { - .codec = HANTRO_H264_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_H264_SCALING_MATRIX, - }, - }, { - .codec = HANTRO_H264_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_H264_DECODE_MODE, - .min = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, - .def = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, - .max = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, - }, - }, { - .codec = HANTRO_H264_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_H264_START_CODE, - .min = V4L2_STATELESS_H264_START_CODE_ANNEX_B, - .def = V4L2_STATELESS_H264_START_CODE_ANNEX_B, - .max = V4L2_STATELESS_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, - } - }, { - .codec = HANTRO_HEVC_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_HEVC_DECODE_MODE, - .min = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED, - .max = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED, - .def = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED, - }, - }, { - .codec = HANTRO_HEVC_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_HEVC_START_CODE, - .min = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B, - .max = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B, - .def = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B, - }, - }, { - .codec = HANTRO_HEVC_DECODER, - .cfg = { - .id = V4L2_CID_MPEG_VIDEO_HEVC_PROFILE, - .min = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, - .max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10, - .def = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, - }, - }, { - .codec = HANTRO_HEVC_DECODER, - .cfg = { - .id = V4L2_CID_MPEG_VIDEO_HEVC_LEVEL, - .min = V4L2_MPEG_VIDEO_HEVC_LEVEL_1, - .max = V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1, - }, - }, { - .codec = HANTRO_HEVC_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_HEVC_SPS, - .ops = &hantro_ctrl_ops, - }, - }, { - .codec = HANTRO_HEVC_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_HEVC_PPS, - }, - }, { - .codec = HANTRO_HEVC_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_HEVC_DECODE_PARAMS, - }, - }, { - .codec = HANTRO_HEVC_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_HEVC_SCALING_MATRIX, - }, - }, { - .codec = HANTRO_VP9_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_VP9_FRAME, - .ops = &hantro_vp9_ctrl_ops, - }, - }, { - .codec = HANTRO_VP9_DECODER, - .cfg = { - .id = V4L2_CID_STATELESS_VP9_COMPRESSED_HDR, - }, - }, -}; - -static int hantro_ctrls_setup(struct hantro_dev *vpu, - struct hantro_ctx *ctx, - int allowed_codecs) -{ - int i, num_ctrls = ARRAY_SIZE(controls); - - v4l2_ctrl_handler_init(&ctx->ctrl_handler, num_ctrls); - - for (i = 0; i < num_ctrls; i++) { - if (!(allowed_codecs & controls[i].codec)) - continue; - - v4l2_ctrl_new_custom(&ctx->ctrl_handler, - &controls[i].cfg, NULL); - if (ctx->ctrl_handler.error) { - vpu_err("Adding control (%d) failed %d\n", - controls[i].cfg.id, - ctx->ctrl_handler.error); - v4l2_ctrl_handler_free(&ctx->ctrl_handler); - return ctx->ctrl_handler.error; - } - } - return v4l2_ctrl_handler_setup(&ctx->ctrl_handler); -} - -/* - * V4L2 file operations. - */ - -static int hantro_open(struct file *filp) -{ - struct hantro_dev *vpu = video_drvdata(filp); - struct video_device *vdev = video_devdata(filp); - struct hantro_func *func = hantro_vdev_to_func(vdev); - struct hantro_ctx *ctx; - int allowed_codecs, ret; - - /* - * We do not need any extra locking here, because we operate only - * on local data here, except reading few fields from dev, which - * do not change through device's lifetime (which is guaranteed by - * reference on module from open()) and V4L2 internal objects (such - * as vdev and ctx->fh), which have proper locking done in respective - * helper functions used here. - */ - - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; - - ctx->dev = vpu; - if (func->id == MEDIA_ENT_F_PROC_VIDEO_ENCODER) { - allowed_codecs = vpu->variant->codec & HANTRO_ENCODERS; - ctx->is_encoder = true; - } else if (func->id == MEDIA_ENT_F_PROC_VIDEO_DECODER) { - allowed_codecs = vpu->variant->codec & HANTRO_DECODERS; - ctx->is_encoder = false; - } else { - ret = -ENODEV; - goto err_ctx_free; - } - - ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(vpu->m2m_dev, ctx, queue_init); - if (IS_ERR(ctx->fh.m2m_ctx)) { - ret = PTR_ERR(ctx->fh.m2m_ctx); - goto err_ctx_free; - } - - v4l2_fh_init(&ctx->fh, vdev); - filp->private_data = &ctx->fh; - v4l2_fh_add(&ctx->fh); - - hantro_reset_fmts(ctx); - - ret = hantro_ctrls_setup(vpu, ctx, allowed_codecs); - if (ret) { - vpu_err("Failed to set up controls\n"); - goto err_fh_free; - } - ctx->fh.ctrl_handler = &ctx->ctrl_handler; - - return 0; - -err_fh_free: - v4l2_fh_del(&ctx->fh); - v4l2_fh_exit(&ctx->fh); -err_ctx_free: - kfree(ctx); - return ret; -} - -static int hantro_release(struct file *filp) -{ - struct hantro_ctx *ctx = - container_of(filp->private_data, struct hantro_ctx, fh); - - /* - * No need for extra locking because this was the last reference - * to this file. - */ - v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); - v4l2_fh_del(&ctx->fh); - v4l2_fh_exit(&ctx->fh); - v4l2_ctrl_handler_free(&ctx->ctrl_handler); - kfree(ctx); - - return 0; -} - -static const struct v4l2_file_operations hantro_fops = { - .owner = THIS_MODULE, - .open = hantro_open, - .release = hantro_release, - .poll = v4l2_m2m_fop_poll, - .unlocked_ioctl = video_ioctl2, - .mmap = v4l2_m2m_fop_mmap, -}; - -static const struct of_device_id of_hantro_match[] = { -#ifdef CONFIG_VIDEO_HANTRO_ROCKCHIP - { .compatible = "rockchip,px30-vpu", .data = &px30_vpu_variant, }, - { .compatible = "rockchip,rk3036-vpu", .data = &rk3036_vpu_variant, }, - { .compatible = "rockchip,rk3066-vpu", .data = &rk3066_vpu_variant, }, - { .compatible = "rockchip,rk3288-vpu", .data = &rk3288_vpu_variant, }, - { .compatible = "rockchip,rk3328-vpu", .data = &rk3328_vpu_variant, }, - { .compatible = "rockchip,rk3399-vpu", .data = &rk3399_vpu_variant, }, - { .compatible = "rockchip,rk3568-vepu", .data = &rk3568_vepu_variant, }, - { .compatible = "rockchip,rk3568-vpu", .data = &rk3568_vpu_variant, }, -#endif -#ifdef CONFIG_VIDEO_HANTRO_IMX8M - { .compatible = "nxp,imx8mm-vpu-g1", .data = &imx8mm_vpu_g1_variant, }, - { .compatible = "nxp,imx8mq-vpu", .data = &imx8mq_vpu_variant, }, - { .compatible = "nxp,imx8mq-vpu-g1", .data = &imx8mq_vpu_g1_variant }, - { .compatible = "nxp,imx8mq-vpu-g2", .data = &imx8mq_vpu_g2_variant }, -#endif -#ifdef CONFIG_VIDEO_HANTRO_SAMA5D4 - { .compatible = "microchip,sama5d4-vdec", .data = &sama5d4_vdec_variant, }, -#endif -#ifdef CONFIG_VIDEO_HANTRO_SUNXI - { .compatible = "allwinner,sun50i-h6-vpu-g2", .data = &sunxi_vpu_variant, }, -#endif - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, of_hantro_match); - -static int hantro_register_entity(struct media_device *mdev, - struct media_entity *entity, - const char *entity_name, - struct media_pad *pads, int num_pads, - int function, struct video_device *vdev) -{ - char *name; - int ret; - - entity->obj_type = MEDIA_ENTITY_TYPE_BASE; - if (function == MEDIA_ENT_F_IO_V4L) { - entity->info.dev.major = VIDEO_MAJOR; - entity->info.dev.minor = vdev->minor; - } - - name = devm_kasprintf(mdev->dev, GFP_KERNEL, "%s-%s", vdev->name, - entity_name); - if (!name) - return -ENOMEM; - - entity->name = name; - entity->function = function; - - ret = media_entity_pads_init(entity, num_pads, pads); - if (ret) - return ret; - - ret = media_device_register_entity(mdev, entity); - if (ret) - return ret; - - return 0; -} - -static int hantro_attach_func(struct hantro_dev *vpu, - struct hantro_func *func) -{ - struct media_device *mdev = &vpu->mdev; - struct media_link *link; - int ret; - - /* Create the three encoder entities with their pads */ - func->source_pad.flags = MEDIA_PAD_FL_SOURCE; - ret = hantro_register_entity(mdev, &func->vdev.entity, "source", - &func->source_pad, 1, MEDIA_ENT_F_IO_V4L, - &func->vdev); - if (ret) - return ret; - - func->proc_pads[0].flags = MEDIA_PAD_FL_SINK; - func->proc_pads[1].flags = MEDIA_PAD_FL_SOURCE; - ret = hantro_register_entity(mdev, &func->proc, "proc", - func->proc_pads, 2, func->id, - &func->vdev); - if (ret) - goto err_rel_entity0; - - func->sink_pad.flags = MEDIA_PAD_FL_SINK; - ret = hantro_register_entity(mdev, &func->sink, "sink", - &func->sink_pad, 1, MEDIA_ENT_F_IO_V4L, - &func->vdev); - if (ret) - goto err_rel_entity1; - - /* Connect the three entities */ - ret = media_create_pad_link(&func->vdev.entity, 0, &func->proc, 0, - MEDIA_LNK_FL_IMMUTABLE | - MEDIA_LNK_FL_ENABLED); - if (ret) - goto err_rel_entity2; - - ret = media_create_pad_link(&func->proc, 1, &func->sink, 0, - MEDIA_LNK_FL_IMMUTABLE | - MEDIA_LNK_FL_ENABLED); - if (ret) - goto err_rm_links0; - - /* Create video interface */ - func->intf_devnode = media_devnode_create(mdev, MEDIA_INTF_T_V4L_VIDEO, - 0, VIDEO_MAJOR, - func->vdev.minor); - if (!func->intf_devnode) { - ret = -ENOMEM; - goto err_rm_links1; - } - - /* Connect the two DMA engines to the interface */ - link = media_create_intf_link(&func->vdev.entity, - &func->intf_devnode->intf, - MEDIA_LNK_FL_IMMUTABLE | - MEDIA_LNK_FL_ENABLED); - if (!link) { - ret = -ENOMEM; - goto err_rm_devnode; - } - - link = media_create_intf_link(&func->sink, &func->intf_devnode->intf, - MEDIA_LNK_FL_IMMUTABLE | - MEDIA_LNK_FL_ENABLED); - if (!link) { - ret = -ENOMEM; - goto err_rm_devnode; - } - return 0; - -err_rm_devnode: - media_devnode_remove(func->intf_devnode); - -err_rm_links1: - media_entity_remove_links(&func->sink); - -err_rm_links0: - media_entity_remove_links(&func->proc); - media_entity_remove_links(&func->vdev.entity); - -err_rel_entity2: - media_device_unregister_entity(&func->sink); - -err_rel_entity1: - media_device_unregister_entity(&func->proc); - -err_rel_entity0: - media_device_unregister_entity(&func->vdev.entity); - return ret; -} - -static void hantro_detach_func(struct hantro_func *func) -{ - media_devnode_remove(func->intf_devnode); - media_entity_remove_links(&func->sink); - media_entity_remove_links(&func->proc); - media_entity_remove_links(&func->vdev.entity); - media_device_unregister_entity(&func->sink); - media_device_unregister_entity(&func->proc); - media_device_unregister_entity(&func->vdev.entity); -} - -static int hantro_add_func(struct hantro_dev *vpu, unsigned int funcid) -{ - const struct of_device_id *match; - struct hantro_func *func; - struct video_device *vfd; - int ret; - - match = of_match_node(of_hantro_match, vpu->dev->of_node); - func = devm_kzalloc(vpu->dev, sizeof(*func), GFP_KERNEL); - if (!func) { - v4l2_err(&vpu->v4l2_dev, "Failed to allocate video device\n"); - return -ENOMEM; - } - - func->id = funcid; - - vfd = &func->vdev; - vfd->fops = &hantro_fops; - vfd->release = video_device_release_empty; - vfd->lock = &vpu->vpu_mutex; - vfd->v4l2_dev = &vpu->v4l2_dev; - vfd->vfl_dir = VFL_DIR_M2M; - vfd->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE; - vfd->ioctl_ops = &hantro_ioctl_ops; - snprintf(vfd->name, sizeof(vfd->name), "%s-%s", match->compatible, - funcid == MEDIA_ENT_F_PROC_VIDEO_ENCODER ? "enc" : "dec"); - - if (funcid == MEDIA_ENT_F_PROC_VIDEO_ENCODER) { - vpu->encoder = func; - } else { - vpu->decoder = func; - v4l2_disable_ioctl(vfd, VIDIOC_TRY_ENCODER_CMD); - v4l2_disable_ioctl(vfd, VIDIOC_ENCODER_CMD); - } - - video_set_drvdata(vfd, vpu); - - ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); - if (ret) { - v4l2_err(&vpu->v4l2_dev, "Failed to register video device\n"); - return ret; - } - - ret = hantro_attach_func(vpu, func); - if (ret) { - v4l2_err(&vpu->v4l2_dev, - "Failed to attach functionality to the media device\n"); - goto err_unreg_dev; - } - - v4l2_info(&vpu->v4l2_dev, "registered %s as /dev/video%d\n", vfd->name, - vfd->num); - - return 0; - -err_unreg_dev: - video_unregister_device(vfd); - return ret; -} - -static int hantro_add_enc_func(struct hantro_dev *vpu) -{ - if (!vpu->variant->enc_fmts) - return 0; - - return hantro_add_func(vpu, MEDIA_ENT_F_PROC_VIDEO_ENCODER); -} - -static int hantro_add_dec_func(struct hantro_dev *vpu) -{ - if (!vpu->variant->dec_fmts) - return 0; - - return hantro_add_func(vpu, MEDIA_ENT_F_PROC_VIDEO_DECODER); -} - -static void hantro_remove_func(struct hantro_dev *vpu, - unsigned int funcid) -{ - struct hantro_func *func; - - if (funcid == MEDIA_ENT_F_PROC_VIDEO_ENCODER) - func = vpu->encoder; - else - func = vpu->decoder; - - if (!func) - return; - - hantro_detach_func(func); - video_unregister_device(&func->vdev); -} - -static void hantro_remove_enc_func(struct hantro_dev *vpu) -{ - hantro_remove_func(vpu, MEDIA_ENT_F_PROC_VIDEO_ENCODER); -} - -static void hantro_remove_dec_func(struct hantro_dev *vpu) -{ - hantro_remove_func(vpu, MEDIA_ENT_F_PROC_VIDEO_DECODER); -} - -static const struct media_device_ops hantro_m2m_media_ops = { - .req_validate = vb2_request_validate, - .req_queue = v4l2_m2m_request_queue, -}; - -static int hantro_probe(struct platform_device *pdev) -{ - const struct of_device_id *match; - struct hantro_dev *vpu; - struct resource *res; - int num_bases; - int i, ret; - - vpu = devm_kzalloc(&pdev->dev, sizeof(*vpu), GFP_KERNEL); - if (!vpu) - return -ENOMEM; - - vpu->dev = &pdev->dev; - vpu->pdev = pdev; - mutex_init(&vpu->vpu_mutex); - spin_lock_init(&vpu->irqlock); - - match = of_match_node(of_hantro_match, pdev->dev.of_node); - vpu->variant = match->data; - - /* - * Support for nxp,imx8mq-vpu is kept for backwards compatibility - * but it's deprecated. Please update your DTS file to use - * nxp,imx8mq-vpu-g1 or nxp,imx8mq-vpu-g2 instead. - */ - if (of_device_is_compatible(pdev->dev.of_node, "nxp,imx8mq-vpu")) - dev_warn(&pdev->dev, "%s compatible is deprecated\n", - match->compatible); - - INIT_DELAYED_WORK(&vpu->watchdog_work, hantro_watchdog); - - vpu->clocks = devm_kcalloc(&pdev->dev, vpu->variant->num_clocks, - sizeof(*vpu->clocks), GFP_KERNEL); - if (!vpu->clocks) - return -ENOMEM; - - if (vpu->variant->num_clocks > 1) { - for (i = 0; i < vpu->variant->num_clocks; i++) - vpu->clocks[i].id = vpu->variant->clk_names[i]; - - ret = devm_clk_bulk_get(&pdev->dev, vpu->variant->num_clocks, - vpu->clocks); - if (ret) - return ret; - } else { - /* - * If the driver has a single clk, chances are there will be no - * actual name in the DT bindings. - */ - vpu->clocks[0].clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(vpu->clocks[0].clk)) - return PTR_ERR(vpu->clocks[0].clk); - } - - vpu->resets = devm_reset_control_array_get(&pdev->dev, false, true); - if (IS_ERR(vpu->resets)) - return PTR_ERR(vpu->resets); - - num_bases = vpu->variant->num_regs ?: 1; - vpu->reg_bases = devm_kcalloc(&pdev->dev, num_bases, - sizeof(*vpu->reg_bases), GFP_KERNEL); - if (!vpu->reg_bases) - return -ENOMEM; - - for (i = 0; i < num_bases; i++) { - res = vpu->variant->reg_names ? - platform_get_resource_byname(vpu->pdev, IORESOURCE_MEM, - vpu->variant->reg_names[i]) : - platform_get_resource(vpu->pdev, IORESOURCE_MEM, 0); - vpu->reg_bases[i] = devm_ioremap_resource(vpu->dev, res); - if (IS_ERR(vpu->reg_bases[i])) - return PTR_ERR(vpu->reg_bases[i]); - } - vpu->enc_base = vpu->reg_bases[0] + vpu->variant->enc_offset; - vpu->dec_base = vpu->reg_bases[0] + vpu->variant->dec_offset; - - /** - * TODO: Eventually allow taking advantage of full 64-bit address space. - * Until then we assume the MSB portion of buffers' base addresses is - * always 0 due to this masking operation. - */ - ret = dma_set_coherent_mask(vpu->dev, DMA_BIT_MASK(32)); - if (ret) { - dev_err(vpu->dev, "Could not set DMA coherent mask.\n"); - return ret; - } - vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); - - for (i = 0; i < vpu->variant->num_irqs; i++) { - const char *irq_name; - int irq; - - if (!vpu->variant->irqs[i].handler) - continue; - - if (vpu->variant->num_irqs > 1) { - irq_name = vpu->variant->irqs[i].name; - irq = platform_get_irq_byname(vpu->pdev, irq_name); - } else { - /* - * If the driver has a single IRQ, chances are there - * will be no actual name in the DT bindings. - */ - irq_name = "default"; - irq = platform_get_irq(vpu->pdev, 0); - } - if (irq <= 0) - return -ENXIO; - - ret = devm_request_irq(vpu->dev, irq, - vpu->variant->irqs[i].handler, 0, - dev_name(vpu->dev), vpu); - if (ret) { - dev_err(vpu->dev, "Could not request %s IRQ.\n", - irq_name); - return ret; - } - } - - if (vpu->variant->init) { - ret = vpu->variant->init(vpu); - if (ret) { - dev_err(&pdev->dev, "Failed to init VPU hardware\n"); - return ret; - } - } - - pm_runtime_set_autosuspend_delay(vpu->dev, 100); - pm_runtime_use_autosuspend(vpu->dev); - pm_runtime_enable(vpu->dev); - - ret = reset_control_deassert(vpu->resets); - if (ret) { - dev_err(&pdev->dev, "Failed to deassert resets\n"); - goto err_pm_disable; - } - - ret = clk_bulk_prepare(vpu->variant->num_clocks, vpu->clocks); - if (ret) { - dev_err(&pdev->dev, "Failed to prepare clocks\n"); - goto err_rst_assert; - } - - ret = v4l2_device_register(&pdev->dev, &vpu->v4l2_dev); - if (ret) { - dev_err(&pdev->dev, "Failed to register v4l2 device\n"); - goto err_clk_unprepare; - } - platform_set_drvdata(pdev, vpu); - - vpu->m2m_dev = v4l2_m2m_init(&vpu_m2m_ops); - if (IS_ERR(vpu->m2m_dev)) { - v4l2_err(&vpu->v4l2_dev, "Failed to init mem2mem device\n"); - ret = PTR_ERR(vpu->m2m_dev); - goto err_v4l2_unreg; - } - - vpu->mdev.dev = vpu->dev; - strscpy(vpu->mdev.model, DRIVER_NAME, sizeof(vpu->mdev.model)); - strscpy(vpu->mdev.bus_info, "platform: " DRIVER_NAME, - sizeof(vpu->mdev.bus_info)); - media_device_init(&vpu->mdev); - vpu->mdev.ops = &hantro_m2m_media_ops; - vpu->v4l2_dev.mdev = &vpu->mdev; - - ret = hantro_add_enc_func(vpu); - if (ret) { - dev_err(&pdev->dev, "Failed to register encoder\n"); - goto err_m2m_rel; - } - - ret = hantro_add_dec_func(vpu); - if (ret) { - dev_err(&pdev->dev, "Failed to register decoder\n"); - goto err_rm_enc_func; - } - - ret = media_device_register(&vpu->mdev); - if (ret) { - v4l2_err(&vpu->v4l2_dev, "Failed to register mem2mem media device\n"); - goto err_rm_dec_func; - } - - return 0; - -err_rm_dec_func: - hantro_remove_dec_func(vpu); -err_rm_enc_func: - hantro_remove_enc_func(vpu); -err_m2m_rel: - media_device_cleanup(&vpu->mdev); - v4l2_m2m_release(vpu->m2m_dev); -err_v4l2_unreg: - v4l2_device_unregister(&vpu->v4l2_dev); -err_clk_unprepare: - clk_bulk_unprepare(vpu->variant->num_clocks, vpu->clocks); -err_rst_assert: - reset_control_assert(vpu->resets); -err_pm_disable: - pm_runtime_dont_use_autosuspend(vpu->dev); - pm_runtime_disable(vpu->dev); - return ret; -} - -static int hantro_remove(struct platform_device *pdev) -{ - struct hantro_dev *vpu = platform_get_drvdata(pdev); - - v4l2_info(&vpu->v4l2_dev, "Removing %s\n", pdev->name); - - media_device_unregister(&vpu->mdev); - hantro_remove_dec_func(vpu); - hantro_remove_enc_func(vpu); - media_device_cleanup(&vpu->mdev); - v4l2_m2m_release(vpu->m2m_dev); - v4l2_device_unregister(&vpu->v4l2_dev); - clk_bulk_unprepare(vpu->variant->num_clocks, vpu->clocks); - reset_control_assert(vpu->resets); - pm_runtime_dont_use_autosuspend(vpu->dev); - pm_runtime_disable(vpu->dev); - return 0; -} - -#ifdef CONFIG_PM -static int hantro_runtime_resume(struct device *dev) -{ - struct hantro_dev *vpu = dev_get_drvdata(dev); - - if (vpu->variant->runtime_resume) - return vpu->variant->runtime_resume(vpu); - - return 0; -} -#endif - -static const struct dev_pm_ops hantro_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(NULL, hantro_runtime_resume, NULL) -}; - -static struct platform_driver hantro_driver = { - .probe = hantro_probe, - .remove = hantro_remove, - .driver = { - .name = DRIVER_NAME, - .of_match_table = of_match_ptr(of_hantro_match), - .pm = &hantro_pm_ops, - }, -}; -module_platform_driver(hantro_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Alpha Lin <Alpha.Lin@Rock-Chips.com>"); -MODULE_AUTHOR("Tomasz Figa <tfiga@chromium.org>"); -MODULE_AUTHOR("Ezequiel Garcia <ezequiel@collabora.com>"); -MODULE_DESCRIPTION("Hantro VPU codec driver"); diff --git a/drivers/staging/media/hantro/hantro_g1.c b/drivers/staging/media/hantro/hantro_g1.c deleted file mode 100644 index 0ab1cee62218..000000000000 --- a/drivers/staging/media/hantro/hantro_g1.c +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VPU codec driver - * - * Copyright (C) 2018 Rockchip Electronics Co., Ltd. - * Jeffy Chen <jeffy.chen@rock-chips.com> - * Copyright (C) 2019 Pengutronix, Philipp Zabel <kernel@pengutronix.de> - * Copyright (C) 2021 Collabora Ltd, Emil Velikov <emil.velikov@collabora.com> - */ - -#include "hantro.h" -#include "hantro_g1_regs.h" - -irqreturn_t hantro_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, state); - - return IRQ_HANDLED; -} - -void hantro_g1_reset(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - - vdpu_write(vpu, G1_REG_INTERRUPT_DEC_IRQ_DIS, G1_REG_INTERRUPT); - vdpu_write(vpu, G1_REG_CONFIG_DEC_CLK_GATE_E, G1_REG_CONFIG); - vdpu_write(vpu, 1, G1_REG_SOFT_RESET); -} diff --git a/drivers/staging/media/hantro/hantro_g1_h264_dec.c b/drivers/staging/media/hantro/hantro_g1_h264_dec.c deleted file mode 100644 index 9de7f05eff2a..000000000000 --- a/drivers/staging/media/hantro/hantro_g1_h264_dec.c +++ /dev/null @@ -1,284 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Rockchip RK3288 VPU codec driver - * - * Copyright (c) 2014 Rockchip Electronics Co., Ltd. - * Hertz Wong <hertz.wong@rock-chips.com> - * Herman Chen <herman.chen@rock-chips.com> - * - * Copyright (C) 2014 Google, Inc. - * Tomasz Figa <tfiga@chromium.org> - */ - -#include <linux/types.h> -#include <linux/sort.h> - -#include <media/v4l2-mem2mem.h> - -#include "hantro_g1_regs.h" -#include "hantro_hw.h" -#include "hantro_v4l2.h" - -static void set_params(struct hantro_ctx *ctx, struct vb2_v4l2_buffer *src_buf) -{ - const struct hantro_h264_dec_ctrls *ctrls = &ctx->h264_dec.ctrls; - const struct v4l2_ctrl_h264_decode_params *dec_param = ctrls->decode; - const struct v4l2_ctrl_h264_sps *sps = ctrls->sps; - const struct v4l2_ctrl_h264_pps *pps = ctrls->pps; - struct hantro_dev *vpu = ctx->dev; - u32 reg; - - /* Decoder control register 0. */ - reg = G1_REG_DEC_CTRL0_DEC_AXI_AUTO; - if (sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD) - reg |= G1_REG_DEC_CTRL0_SEQ_MBAFF_E; - if (sps->profile_idc > 66) { - reg |= G1_REG_DEC_CTRL0_PICORD_COUNT_E; - if (dec_param->nal_ref_idc) - reg |= G1_REG_DEC_CTRL0_WRITE_MVS_E; - } - - if (!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY) && - (sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD || - dec_param->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC)) - reg |= G1_REG_DEC_CTRL0_PIC_INTERLACE_E; - if (dec_param->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC) - reg |= G1_REG_DEC_CTRL0_PIC_FIELDMODE_E; - if (!(dec_param->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD)) - reg |= G1_REG_DEC_CTRL0_PIC_TOPFIELD_E; - vdpu_write_relaxed(vpu, reg, G1_REG_DEC_CTRL0); - - /* Decoder control register 1. */ - reg = G1_REG_DEC_CTRL1_PIC_MB_WIDTH(MB_WIDTH(ctx->src_fmt.width)) | - G1_REG_DEC_CTRL1_PIC_MB_HEIGHT_P(MB_HEIGHT(ctx->src_fmt.height)) | - G1_REG_DEC_CTRL1_REF_FRAMES(sps->max_num_ref_frames); - vdpu_write_relaxed(vpu, reg, G1_REG_DEC_CTRL1); - - /* Decoder control register 2. */ - reg = G1_REG_DEC_CTRL2_CH_QP_OFFSET(pps->chroma_qp_index_offset) | - G1_REG_DEC_CTRL2_CH_QP_OFFSET2(pps->second_chroma_qp_index_offset); - - if (pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT) - reg |= G1_REG_DEC_CTRL2_TYPE1_QUANT_E; - if (!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY)) - reg |= G1_REG_DEC_CTRL2_FIELDPIC_FLAG_E; - vdpu_write_relaxed(vpu, reg, G1_REG_DEC_CTRL2); - - /* Decoder control register 3. */ - reg = G1_REG_DEC_CTRL3_START_CODE_E | - G1_REG_DEC_CTRL3_INIT_QP(pps->pic_init_qp_minus26 + 26) | - G1_REG_DEC_CTRL3_STREAM_LEN(vb2_get_plane_payload(&src_buf->vb2_buf, 0)); - vdpu_write_relaxed(vpu, reg, G1_REG_DEC_CTRL3); - - /* Decoder control register 4. */ - reg = G1_REG_DEC_CTRL4_FRAMENUM_LEN(sps->log2_max_frame_num_minus4 + 4) | - G1_REG_DEC_CTRL4_FRAMENUM(dec_param->frame_num) | - G1_REG_DEC_CTRL4_WEIGHT_BIPR_IDC(pps->weighted_bipred_idc); - if (pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE) - reg |= G1_REG_DEC_CTRL4_CABAC_E; - if (sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE) - reg |= G1_REG_DEC_CTRL4_DIR_8X8_INFER_E; - if (sps->profile_idc >= 100 && sps->chroma_format_idc == 0) - reg |= G1_REG_DEC_CTRL4_BLACKWHITE_E; - if (pps->flags & V4L2_H264_PPS_FLAG_WEIGHTED_PRED) - reg |= G1_REG_DEC_CTRL4_WEIGHT_PRED_E; - vdpu_write_relaxed(vpu, reg, G1_REG_DEC_CTRL4); - - /* Decoder control register 5. */ - reg = G1_REG_DEC_CTRL5_REFPIC_MK_LEN(dec_param->dec_ref_pic_marking_bit_size) | - G1_REG_DEC_CTRL5_IDR_PIC_ID(dec_param->idr_pic_id); - if (pps->flags & V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED) - reg |= G1_REG_DEC_CTRL5_CONST_INTRA_E; - if (pps->flags & V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT) - reg |= G1_REG_DEC_CTRL5_FILT_CTRL_PRES; - if (pps->flags & V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT) - reg |= G1_REG_DEC_CTRL5_RDPIC_CNT_PRES; - if (pps->flags & V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE) - reg |= G1_REG_DEC_CTRL5_8X8TRANS_FLAG_E; - if (dec_param->flags & V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC) - reg |= G1_REG_DEC_CTRL5_IDR_PIC_E; - vdpu_write_relaxed(vpu, reg, G1_REG_DEC_CTRL5); - - /* Decoder control register 6. */ - reg = G1_REG_DEC_CTRL6_PPS_ID(pps->pic_parameter_set_id) | - G1_REG_DEC_CTRL6_REFIDX0_ACTIVE(pps->num_ref_idx_l0_default_active_minus1 + 1) | - G1_REG_DEC_CTRL6_REFIDX1_ACTIVE(pps->num_ref_idx_l1_default_active_minus1 + 1) | - G1_REG_DEC_CTRL6_POC_LENGTH(dec_param->pic_order_cnt_bit_size); - vdpu_write_relaxed(vpu, reg, G1_REG_DEC_CTRL6); - - /* Error concealment register. */ - vdpu_write_relaxed(vpu, 0, G1_REG_ERR_CONC); - - /* Prediction filter tap register. */ - vdpu_write_relaxed(vpu, - G1_REG_PRED_FLT_PRED_BC_TAP_0_0(1) | - G1_REG_PRED_FLT_PRED_BC_TAP_0_1(-5 & 0x3ff) | - G1_REG_PRED_FLT_PRED_BC_TAP_0_2(20), - G1_REG_PRED_FLT); - - /* Reference picture buffer control register. */ - vdpu_write_relaxed(vpu, 0, G1_REG_REF_BUF_CTRL); - - /* Reference picture buffer control register 2. */ - vdpu_write_relaxed(vpu, G1_REG_REF_BUF_CTRL2_APF_THRESHOLD(8), - G1_REG_REF_BUF_CTRL2); -} - -static void set_ref(struct hantro_ctx *ctx) -{ - const struct v4l2_h264_reference *b0_reflist, *b1_reflist, *p_reflist; - struct hantro_dev *vpu = ctx->dev; - int reg_num; - u32 reg; - int i; - - vdpu_write_relaxed(vpu, ctx->h264_dec.dpb_valid, G1_REG_VALID_REF); - vdpu_write_relaxed(vpu, ctx->h264_dec.dpb_longterm, G1_REG_LT_REF); - - /* - * Set up reference frame picture numbers. - * - * Each G1_REG_REF_PIC(x) register contains numbers of two - * subsequential reference pictures. - */ - for (i = 0; i < HANTRO_H264_DPB_SIZE; i += 2) { - reg = G1_REG_REF_PIC_REFER0_NBR(hantro_h264_get_ref_nbr(ctx, i)) | - G1_REG_REF_PIC_REFER1_NBR(hantro_h264_get_ref_nbr(ctx, i + 1)); - vdpu_write_relaxed(vpu, reg, G1_REG_REF_PIC(i / 2)); - } - - b0_reflist = ctx->h264_dec.reflists.b0; - b1_reflist = ctx->h264_dec.reflists.b1; - p_reflist = ctx->h264_dec.reflists.p; - - /* - * Each G1_REG_BD_REF_PIC(x) register contains three entries - * of each forward and backward picture list. - */ - reg_num = 0; - for (i = 0; i < 15; i += 3) { - reg = G1_REG_BD_REF_PIC_BINIT_RLIST_F0(b0_reflist[i].index) | - G1_REG_BD_REF_PIC_BINIT_RLIST_F1(b0_reflist[i + 1].index) | - G1_REG_BD_REF_PIC_BINIT_RLIST_F2(b0_reflist[i + 2].index) | - G1_REG_BD_REF_PIC_BINIT_RLIST_B0(b1_reflist[i].index) | - G1_REG_BD_REF_PIC_BINIT_RLIST_B1(b1_reflist[i + 1].index) | - G1_REG_BD_REF_PIC_BINIT_RLIST_B2(b1_reflist[i + 2].index); - vdpu_write_relaxed(vpu, reg, G1_REG_BD_REF_PIC(reg_num++)); - } - - /* - * G1_REG_BD_P_REF_PIC register contains last entries (index 15) - * of forward and backward reference picture lists and first 4 entries - * of P forward picture list. - */ - reg = G1_REG_BD_P_REF_PIC_BINIT_RLIST_F15(b0_reflist[15].index) | - G1_REG_BD_P_REF_PIC_BINIT_RLIST_B15(b1_reflist[15].index) | - G1_REG_BD_P_REF_PIC_PINIT_RLIST_F0(p_reflist[0].index) | - G1_REG_BD_P_REF_PIC_PINIT_RLIST_F1(p_reflist[1].index) | - G1_REG_BD_P_REF_PIC_PINIT_RLIST_F2(p_reflist[2].index) | - G1_REG_BD_P_REF_PIC_PINIT_RLIST_F3(p_reflist[3].index); - vdpu_write_relaxed(vpu, reg, G1_REG_BD_P_REF_PIC); - - /* - * Each G1_REG_FWD_PIC(x) register contains six consecutive - * entries of P forward picture list, starting from index 4. - */ - reg_num = 0; - for (i = 4; i < HANTRO_H264_DPB_SIZE; i += 6) { - reg = G1_REG_FWD_PIC_PINIT_RLIST_F0(p_reflist[i].index) | - G1_REG_FWD_PIC_PINIT_RLIST_F1(p_reflist[i + 1].index) | - G1_REG_FWD_PIC_PINIT_RLIST_F2(p_reflist[i + 2].index) | - G1_REG_FWD_PIC_PINIT_RLIST_F3(p_reflist[i + 3].index) | - G1_REG_FWD_PIC_PINIT_RLIST_F4(p_reflist[i + 4].index) | - G1_REG_FWD_PIC_PINIT_RLIST_F5(p_reflist[i + 5].index); - vdpu_write_relaxed(vpu, reg, G1_REG_FWD_PIC(reg_num++)); - } - - /* Set up addresses of DPB buffers. */ - for (i = 0; i < HANTRO_H264_DPB_SIZE; i++) { - dma_addr_t dma_addr = hantro_h264_get_ref_buf(ctx, i); - - vdpu_write_relaxed(vpu, dma_addr, G1_REG_ADDR_REF(i)); - } -} - -static void set_buffers(struct hantro_ctx *ctx, struct vb2_v4l2_buffer *src_buf) -{ - const struct hantro_h264_dec_ctrls *ctrls = &ctx->h264_dec.ctrls; - struct vb2_v4l2_buffer *dst_buf; - struct hantro_dev *vpu = ctx->dev; - dma_addr_t src_dma, dst_dma; - size_t offset = 0; - - /* Source (stream) buffer. */ - src_dma = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); - vdpu_write_relaxed(vpu, src_dma, G1_REG_ADDR_STR); - - /* Destination (decoded frame) buffer. */ - dst_buf = hantro_get_dst_buf(ctx); - dst_dma = hantro_get_dec_buf_addr(ctx, &dst_buf->vb2_buf); - /* Adjust dma addr to start at second line for bottom field */ - if (ctrls->decode->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD) - offset = ALIGN(ctx->src_fmt.width, MB_DIM); - vdpu_write_relaxed(vpu, dst_dma + offset, G1_REG_ADDR_DST); - - /* Higher profiles require DMV buffer appended to reference frames. */ - if (ctrls->sps->profile_idc > 66 && ctrls->decode->nal_ref_idc) { - unsigned int bytes_per_mb = 384; - - /* DMV buffer for monochrome start directly after Y-plane */ - if (ctrls->sps->profile_idc >= 100 && - ctrls->sps->chroma_format_idc == 0) - bytes_per_mb = 256; - offset = bytes_per_mb * MB_WIDTH(ctx->src_fmt.width) * - MB_HEIGHT(ctx->src_fmt.height); - - /* - * DMV buffer is split in two for field encoded frames, - * adjust offset for bottom field - */ - if (ctrls->decode->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD) - offset += 32 * MB_WIDTH(ctx->src_fmt.width) * - MB_HEIGHT(ctx->src_fmt.height); - vdpu_write_relaxed(vpu, dst_dma + offset, G1_REG_ADDR_DIR_MV); - } - - /* Auxiliary buffer prepared in hantro_g1_h264_dec_prepare_table(). */ - vdpu_write_relaxed(vpu, ctx->h264_dec.priv.dma, G1_REG_ADDR_QTABLE); -} - -int hantro_g1_h264_dec_run(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - struct vb2_v4l2_buffer *src_buf; - int ret; - - /* Prepare the H264 decoder context. */ - ret = hantro_h264_dec_prepare_run(ctx); - if (ret) - return ret; - - /* Configure hardware registers. */ - src_buf = hantro_get_src_buf(ctx); - set_params(ctx, src_buf); - set_ref(ctx); - set_buffers(ctx, src_buf); - - hantro_end_prepare_run(ctx); - - /* Start decoding! */ - vdpu_write_relaxed(vpu, - G1_REG_CONFIG_DEC_AXI_RD_ID(0xffu) | - G1_REG_CONFIG_DEC_TIMEOUT_E | - G1_REG_CONFIG_DEC_OUT_ENDIAN | - G1_REG_CONFIG_DEC_STRENDIAN_E | - G1_REG_CONFIG_DEC_MAX_BURST(16) | - G1_REG_CONFIG_DEC_OUTSWAP32_E | - G1_REG_CONFIG_DEC_INSWAP32_E | - G1_REG_CONFIG_DEC_STRSWAP32_E | - G1_REG_CONFIG_DEC_CLK_GATE_E, - G1_REG_CONFIG); - vdpu_write(vpu, G1_REG_INTERRUPT_DEC_E, G1_REG_INTERRUPT); - - return 0; -} diff --git a/drivers/staging/media/hantro/hantro_g1_mpeg2_dec.c b/drivers/staging/media/hantro/hantro_g1_mpeg2_dec.c deleted file mode 100644 index 9aea331e1a3c..000000000000 --- a/drivers/staging/media/hantro/hantro_g1_mpeg2_dec.c +++ /dev/null @@ -1,240 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VPU codec driver - * - * Copyright (C) 2018 Rockchip Electronics Co., Ltd. - */ - -#include <asm/unaligned.h> -#include <linux/bitfield.h> -#include <media/v4l2-mem2mem.h> -#include "hantro.h" -#include "hantro_hw.h" -#include "hantro_g1_regs.h" - -#define G1_SWREG(nr) ((nr) * 4) - -#define G1_REG_RLC_VLC_BASE G1_SWREG(12) -#define G1_REG_DEC_OUT_BASE G1_SWREG(13) -#define G1_REG_REFER0_BASE G1_SWREG(14) -#define G1_REG_REFER1_BASE G1_SWREG(15) -#define G1_REG_REFER2_BASE G1_SWREG(16) -#define G1_REG_REFER3_BASE G1_SWREG(17) -#define G1_REG_QTABLE_BASE G1_SWREG(40) - -#define G1_REG_DEC_AXI_RD_ID(v) (((v) << 24) & GENMASK(31, 24)) -#define G1_REG_DEC_TIMEOUT_E(v) ((v) ? BIT(23) : 0) -#define G1_REG_DEC_STRSWAP32_E(v) ((v) ? BIT(22) : 0) -#define G1_REG_DEC_STRENDIAN_E(v) ((v) ? BIT(21) : 0) -#define G1_REG_DEC_INSWAP32_E(v) ((v) ? BIT(20) : 0) -#define G1_REG_DEC_OUTSWAP32_E(v) ((v) ? BIT(19) : 0) -#define G1_REG_DEC_DATA_DISC_E(v) ((v) ? BIT(18) : 0) -#define G1_REG_DEC_LATENCY(v) (((v) << 11) & GENMASK(16, 11)) -#define G1_REG_DEC_CLK_GATE_E(v) ((v) ? BIT(10) : 0) -#define G1_REG_DEC_IN_ENDIAN(v) ((v) ? BIT(9) : 0) -#define G1_REG_DEC_OUT_ENDIAN(v) ((v) ? BIT(8) : 0) -#define G1_REG_DEC_ADV_PRE_DIS(v) ((v) ? BIT(6) : 0) -#define G1_REG_DEC_SCMD_DIS(v) ((v) ? BIT(5) : 0) -#define G1_REG_DEC_MAX_BURST(v) (((v) << 0) & GENMASK(4, 0)) - -#define G1_REG_DEC_MODE(v) (((v) << 28) & GENMASK(31, 28)) -#define G1_REG_RLC_MODE_E(v) ((v) ? BIT(27) : 0) -#define G1_REG_PIC_INTERLACE_E(v) ((v) ? BIT(23) : 0) -#define G1_REG_PIC_FIELDMODE_E(v) ((v) ? BIT(22) : 0) -#define G1_REG_PIC_B_E(v) ((v) ? BIT(21) : 0) -#define G1_REG_PIC_INTER_E(v) ((v) ? BIT(20) : 0) -#define G1_REG_PIC_TOPFIELD_E(v) ((v) ? BIT(19) : 0) -#define G1_REG_FWD_INTERLACE_E(v) ((v) ? BIT(18) : 0) -#define G1_REG_FILTERING_DIS(v) ((v) ? BIT(14) : 0) -#define G1_REG_WRITE_MVS_E(v) ((v) ? BIT(12) : 0) -#define G1_REG_DEC_AXI_WR_ID(v) (((v) << 0) & GENMASK(7, 0)) - -#define G1_REG_PIC_MB_WIDTH(v) (((v) << 23) & GENMASK(31, 23)) -#define G1_REG_PIC_MB_HEIGHT_P(v) (((v) << 11) & GENMASK(18, 11)) -#define G1_REG_ALT_SCAN_E(v) ((v) ? BIT(6) : 0) -#define G1_REG_TOPFIELDFIRST_E(v) ((v) ? BIT(5) : 0) - -#define G1_REG_STRM_START_BIT(v) (((v) << 26) & GENMASK(31, 26)) -#define G1_REG_QSCALE_TYPE(v) ((v) ? BIT(24) : 0) -#define G1_REG_CON_MV_E(v) ((v) ? BIT(4) : 0) -#define G1_REG_INTRA_DC_PREC(v) (((v) << 2) & GENMASK(3, 2)) -#define G1_REG_INTRA_VLC_TAB(v) ((v) ? BIT(1) : 0) -#define G1_REG_FRAME_PRED_DCT(v) ((v) ? BIT(0) : 0) - -#define G1_REG_INIT_QP(v) (((v) << 25) & GENMASK(30, 25)) -#define G1_REG_STREAM_LEN(v) (((v) << 0) & GENMASK(23, 0)) - -#define G1_REG_ALT_SCAN_FLAG_E(v) ((v) ? BIT(19) : 0) -#define G1_REG_FCODE_FWD_HOR(v) (((v) << 15) & GENMASK(18, 15)) -#define G1_REG_FCODE_FWD_VER(v) (((v) << 11) & GENMASK(14, 11)) -#define G1_REG_FCODE_BWD_HOR(v) (((v) << 7) & GENMASK(10, 7)) -#define G1_REG_FCODE_BWD_VER(v) (((v) << 3) & GENMASK(6, 3)) -#define G1_REG_MV_ACCURACY_FWD(v) ((v) ? BIT(2) : 0) -#define G1_REG_MV_ACCURACY_BWD(v) ((v) ? BIT(1) : 0) - -#define G1_REG_STARTMB_X(v) (((v) << 23) & GENMASK(31, 23)) -#define G1_REG_STARTMB_Y(v) (((v) << 15) & GENMASK(22, 15)) - -#define G1_REG_APF_THRESHOLD(v) (((v) << 0) & GENMASK(13, 0)) - -static void -hantro_g1_mpeg2_dec_set_quantisation(struct hantro_dev *vpu, - struct hantro_ctx *ctx) -{ - struct v4l2_ctrl_mpeg2_quantisation *q; - - q = hantro_get_ctrl(ctx, V4L2_CID_STATELESS_MPEG2_QUANTISATION); - hantro_mpeg2_dec_copy_qtable(ctx->mpeg2_dec.qtable.cpu, q); - vdpu_write_relaxed(vpu, ctx->mpeg2_dec.qtable.dma, G1_REG_QTABLE_BASE); -} - -static void -hantro_g1_mpeg2_dec_set_buffers(struct hantro_dev *vpu, struct hantro_ctx *ctx, - struct vb2_buffer *src_buf, - struct vb2_buffer *dst_buf, - const struct v4l2_ctrl_mpeg2_sequence *seq, - const struct v4l2_ctrl_mpeg2_picture *pic) -{ - dma_addr_t forward_addr = 0, backward_addr = 0; - dma_addr_t current_addr, addr; - - switch (pic->picture_coding_type) { - case V4L2_MPEG2_PIC_CODING_TYPE_B: - backward_addr = hantro_get_ref(ctx, pic->backward_ref_ts); - fallthrough; - case V4L2_MPEG2_PIC_CODING_TYPE_P: - forward_addr = hantro_get_ref(ctx, pic->forward_ref_ts); - } - - /* Source bitstream buffer */ - addr = vb2_dma_contig_plane_dma_addr(src_buf, 0); - vdpu_write_relaxed(vpu, addr, G1_REG_RLC_VLC_BASE); - - /* Destination frame buffer */ - addr = hantro_get_dec_buf_addr(ctx, dst_buf); - current_addr = addr; - - if (pic->picture_structure == V4L2_MPEG2_PIC_BOTTOM_FIELD) - addr += ALIGN(ctx->dst_fmt.width, 16); - vdpu_write_relaxed(vpu, addr, G1_REG_DEC_OUT_BASE); - - if (!forward_addr) - forward_addr = current_addr; - if (!backward_addr) - backward_addr = current_addr; - - /* Set forward ref frame (top/bottom field) */ - if (pic->picture_structure == V4L2_MPEG2_PIC_FRAME || - pic->picture_coding_type == V4L2_MPEG2_PIC_CODING_TYPE_B || - (pic->picture_structure == V4L2_MPEG2_PIC_TOP_FIELD && - pic->flags & V4L2_MPEG2_PIC_FLAG_TOP_FIELD_FIRST) || - (pic->picture_structure == V4L2_MPEG2_PIC_BOTTOM_FIELD && - !(pic->flags & V4L2_MPEG2_PIC_FLAG_TOP_FIELD_FIRST))) { - vdpu_write_relaxed(vpu, forward_addr, G1_REG_REFER0_BASE); - vdpu_write_relaxed(vpu, forward_addr, G1_REG_REFER1_BASE); - } else if (pic->picture_structure == V4L2_MPEG2_PIC_TOP_FIELD) { - vdpu_write_relaxed(vpu, forward_addr, G1_REG_REFER0_BASE); - vdpu_write_relaxed(vpu, current_addr, G1_REG_REFER1_BASE); - } else if (pic->picture_structure == V4L2_MPEG2_PIC_BOTTOM_FIELD) { - vdpu_write_relaxed(vpu, current_addr, G1_REG_REFER0_BASE); - vdpu_write_relaxed(vpu, forward_addr, G1_REG_REFER1_BASE); - } - - /* Set backward ref frame (top/bottom field) */ - vdpu_write_relaxed(vpu, backward_addr, G1_REG_REFER2_BASE); - vdpu_write_relaxed(vpu, backward_addr, G1_REG_REFER3_BASE); -} - -int hantro_g1_mpeg2_dec_run(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - struct vb2_v4l2_buffer *src_buf, *dst_buf; - const struct v4l2_ctrl_mpeg2_sequence *seq; - const struct v4l2_ctrl_mpeg2_picture *pic; - u32 reg; - - src_buf = hantro_get_src_buf(ctx); - dst_buf = hantro_get_dst_buf(ctx); - - /* Apply request controls if any */ - hantro_start_prepare_run(ctx); - - seq = hantro_get_ctrl(ctx, - V4L2_CID_STATELESS_MPEG2_SEQUENCE); - pic = hantro_get_ctrl(ctx, - V4L2_CID_STATELESS_MPEG2_PICTURE); - - reg = G1_REG_DEC_AXI_RD_ID(0) | - G1_REG_DEC_TIMEOUT_E(1) | - G1_REG_DEC_STRSWAP32_E(1) | - G1_REG_DEC_STRENDIAN_E(1) | - G1_REG_DEC_INSWAP32_E(1) | - G1_REG_DEC_OUTSWAP32_E(1) | - G1_REG_DEC_DATA_DISC_E(0) | - G1_REG_DEC_LATENCY(0) | - G1_REG_DEC_CLK_GATE_E(1) | - G1_REG_DEC_IN_ENDIAN(1) | - G1_REG_DEC_OUT_ENDIAN(1) | - G1_REG_DEC_ADV_PRE_DIS(0) | - G1_REG_DEC_SCMD_DIS(0) | - G1_REG_DEC_MAX_BURST(16); - vdpu_write_relaxed(vpu, reg, G1_SWREG(2)); - - reg = G1_REG_DEC_MODE(5) | - G1_REG_RLC_MODE_E(0) | - G1_REG_PIC_INTERLACE_E(!(seq->flags & V4L2_MPEG2_SEQ_FLAG_PROGRESSIVE)) | - G1_REG_PIC_FIELDMODE_E(pic->picture_structure != V4L2_MPEG2_PIC_FRAME) | - G1_REG_PIC_B_E(pic->picture_coding_type == V4L2_MPEG2_PIC_CODING_TYPE_B) | - G1_REG_PIC_INTER_E(pic->picture_coding_type != V4L2_MPEG2_PIC_CODING_TYPE_I) | - G1_REG_PIC_TOPFIELD_E(pic->picture_structure == V4L2_MPEG2_PIC_TOP_FIELD) | - G1_REG_FWD_INTERLACE_E(0) | - G1_REG_FILTERING_DIS(1) | - G1_REG_WRITE_MVS_E(0) | - G1_REG_DEC_AXI_WR_ID(0); - vdpu_write_relaxed(vpu, reg, G1_SWREG(3)); - - reg = G1_REG_PIC_MB_WIDTH(MB_WIDTH(ctx->dst_fmt.width)) | - G1_REG_PIC_MB_HEIGHT_P(MB_HEIGHT(ctx->dst_fmt.height)) | - G1_REG_ALT_SCAN_E(pic->flags & V4L2_MPEG2_PIC_FLAG_ALT_SCAN) | - G1_REG_TOPFIELDFIRST_E(pic->flags & V4L2_MPEG2_PIC_FLAG_TOP_FIELD_FIRST); - vdpu_write_relaxed(vpu, reg, G1_SWREG(4)); - - reg = G1_REG_STRM_START_BIT(0) | - G1_REG_QSCALE_TYPE(pic->flags & V4L2_MPEG2_PIC_FLAG_Q_SCALE_TYPE) | - G1_REG_CON_MV_E(pic->flags & V4L2_MPEG2_PIC_FLAG_CONCEALMENT_MV) | - G1_REG_INTRA_DC_PREC(pic->intra_dc_precision) | - G1_REG_INTRA_VLC_TAB(pic->flags & V4L2_MPEG2_PIC_FLAG_INTRA_VLC) | - G1_REG_FRAME_PRED_DCT(pic->flags & V4L2_MPEG2_PIC_FLAG_FRAME_PRED_DCT); - vdpu_write_relaxed(vpu, reg, G1_SWREG(5)); - - reg = G1_REG_INIT_QP(1) | - G1_REG_STREAM_LEN(vb2_get_plane_payload(&src_buf->vb2_buf, 0)); - vdpu_write_relaxed(vpu, reg, G1_SWREG(6)); - - reg = G1_REG_ALT_SCAN_FLAG_E(pic->flags & V4L2_MPEG2_PIC_FLAG_ALT_SCAN) | - G1_REG_FCODE_FWD_HOR(pic->f_code[0][0]) | - G1_REG_FCODE_FWD_VER(pic->f_code[0][1]) | - G1_REG_FCODE_BWD_HOR(pic->f_code[1][0]) | - G1_REG_FCODE_BWD_VER(pic->f_code[1][1]) | - G1_REG_MV_ACCURACY_FWD(1) | - G1_REG_MV_ACCURACY_BWD(1); - vdpu_write_relaxed(vpu, reg, G1_SWREG(18)); - - reg = G1_REG_STARTMB_X(0) | - G1_REG_STARTMB_Y(0); - vdpu_write_relaxed(vpu, reg, G1_SWREG(48)); - - reg = G1_REG_APF_THRESHOLD(8); - vdpu_write_relaxed(vpu, reg, G1_SWREG(55)); - - hantro_g1_mpeg2_dec_set_quantisation(vpu, ctx); - hantro_g1_mpeg2_dec_set_buffers(vpu, ctx, &src_buf->vb2_buf, - &dst_buf->vb2_buf, - seq, pic); - - hantro_end_prepare_run(ctx); - - vdpu_write(vpu, G1_REG_INTERRUPT_DEC_E, G1_REG_INTERRUPT); - - return 0; -} diff --git a/drivers/staging/media/hantro/hantro_g1_regs.h b/drivers/staging/media/hantro/hantro_g1_regs.h deleted file mode 100644 index c623b3b0be18..000000000000 --- a/drivers/staging/media/hantro/hantro_g1_regs.h +++ /dev/null @@ -1,356 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Hantro VPU codec driver - * - * Copyright 2018 Google LLC. - * Tomasz Figa <tfiga@chromium.org> - */ - -#ifndef HANTRO_G1_REGS_H_ -#define HANTRO_G1_REGS_H_ - -#define G1_SWREG(nr) ((nr) * 4) - -/* Decoder registers. */ -#define G1_REG_INTERRUPT 0x004 -#define G1_REG_INTERRUPT_DEC_PIC_INF BIT(24) -#define G1_REG_INTERRUPT_DEC_TIMEOUT BIT(18) -#define G1_REG_INTERRUPT_DEC_SLICE_INT BIT(17) -#define G1_REG_INTERRUPT_DEC_ERROR_INT BIT(16) -#define G1_REG_INTERRUPT_DEC_ASO_INT BIT(15) -#define G1_REG_INTERRUPT_DEC_BUFFER_INT BIT(14) -#define G1_REG_INTERRUPT_DEC_BUS_INT BIT(13) -#define G1_REG_INTERRUPT_DEC_RDY_INT BIT(12) -#define G1_REG_INTERRUPT_DEC_IRQ BIT(8) -#define G1_REG_INTERRUPT_DEC_IRQ_DIS BIT(4) -#define G1_REG_INTERRUPT_DEC_E BIT(0) -#define G1_REG_CONFIG 0x008 -#define G1_REG_CONFIG_DEC_AXI_RD_ID(x) (((x) & 0xff) << 24) -#define G1_REG_CONFIG_DEC_TIMEOUT_E BIT(23) -#define G1_REG_CONFIG_DEC_STRSWAP32_E BIT(22) -#define G1_REG_CONFIG_DEC_STRENDIAN_E BIT(21) -#define G1_REG_CONFIG_DEC_INSWAP32_E BIT(20) -#define G1_REG_CONFIG_DEC_OUTSWAP32_E BIT(19) -#define G1_REG_CONFIG_DEC_DATA_DISC_E BIT(18) -#define G1_REG_CONFIG_TILED_MODE_MSB BIT(17) -#define G1_REG_CONFIG_DEC_OUT_TILED_E BIT(17) -#define G1_REG_CONFIG_DEC_LATENCY(x) (((x) & 0x3f) << 11) -#define G1_REG_CONFIG_DEC_CLK_GATE_E BIT(10) -#define G1_REG_CONFIG_DEC_IN_ENDIAN BIT(9) -#define G1_REG_CONFIG_DEC_OUT_ENDIAN BIT(8) -#define G1_REG_CONFIG_PRIORITY_MODE(x) (((x) & 0x7) << 5) -#define G1_REG_CONFIG_TILED_MODE_LSB BIT(7) -#define G1_REG_CONFIG_DEC_ADV_PRE_DIS BIT(6) -#define G1_REG_CONFIG_DEC_SCMD_DIS BIT(5) -#define G1_REG_CONFIG_DEC_MAX_BURST(x) (((x) & 0x1f) << 0) -#define G1_REG_DEC_CTRL0 0x00c -#define G1_REG_DEC_CTRL0_DEC_MODE(x) (((x) & 0xf) << 28) -#define G1_REG_DEC_CTRL0_RLC_MODE_E BIT(27) -#define G1_REG_DEC_CTRL0_SKIP_MODE BIT(26) -#define G1_REG_DEC_CTRL0_DIVX3_E BIT(25) -#define G1_REG_DEC_CTRL0_PJPEG_E BIT(24) -#define G1_REG_DEC_CTRL0_PIC_INTERLACE_E BIT(23) -#define G1_REG_DEC_CTRL0_PIC_FIELDMODE_E BIT(22) -#define G1_REG_DEC_CTRL0_PIC_B_E BIT(21) -#define G1_REG_DEC_CTRL0_PIC_INTER_E BIT(20) -#define G1_REG_DEC_CTRL0_PIC_TOPFIELD_E BIT(19) -#define G1_REG_DEC_CTRL0_FWD_INTERLACE_E BIT(18) -#define G1_REG_DEC_CTRL0_SORENSON_E BIT(17) -#define G1_REG_DEC_CTRL0_REF_TOPFIELD_E BIT(16) -#define G1_REG_DEC_CTRL0_DEC_OUT_DIS BIT(15) -#define G1_REG_DEC_CTRL0_FILTERING_DIS BIT(14) -#define G1_REG_DEC_CTRL0_WEBP_E BIT(13) -#define G1_REG_DEC_CTRL0_MVC_E BIT(13) -#define G1_REG_DEC_CTRL0_PIC_FIXED_QUANT BIT(13) -#define G1_REG_DEC_CTRL0_WRITE_MVS_E BIT(12) -#define G1_REG_DEC_CTRL0_REFTOPFIRST_E BIT(11) -#define G1_REG_DEC_CTRL0_SEQ_MBAFF_E BIT(10) -#define G1_REG_DEC_CTRL0_PICORD_COUNT_E BIT(9) -#define G1_REG_DEC_CTRL0_DEC_AHB_HLOCK_E BIT(8) -#define G1_REG_DEC_CTRL0_DEC_AXI_WR_ID(x) (((x) & 0xff) << 0) -/* Setting AXI ID to 0xff to get auto generated ID to avoid possible conflicts */ -#define G1_REG_DEC_CTRL0_DEC_AXI_AUTO G1_REG_DEC_CTRL0_DEC_AXI_WR_ID(0xff) -#define G1_REG_DEC_CTRL1 0x010 -#define G1_REG_DEC_CTRL1_PIC_MB_WIDTH(x) (((x) & 0x1ff) << 23) -#define G1_REG_DEC_CTRL1_MB_WIDTH_OFF(x) (((x) & 0xf) << 19) -#define G1_REG_DEC_CTRL1_PIC_MB_HEIGHT_P(x) (((x) & 0xff) << 11) -#define G1_REG_DEC_CTRL1_MB_HEIGHT_OFF(x) (((x) & 0xf) << 7) -#define G1_REG_DEC_CTRL1_ALT_SCAN_E BIT(6) -#define G1_REG_DEC_CTRL1_TOPFIELDFIRST_E BIT(5) -#define G1_REG_DEC_CTRL1_REF_FRAMES(x) (((x) & 0x1f) << 0) -#define G1_REG_DEC_CTRL1_PIC_MB_W_EXT(x) (((x) & 0x7) << 3) -#define G1_REG_DEC_CTRL1_PIC_MB_H_EXT(x) (((x) & 0x7) << 0) -#define G1_REG_DEC_CTRL1_PIC_REFER_FLAG BIT(0) -#define G1_REG_DEC_CTRL2 0x014 -#define G1_REG_DEC_CTRL2_STRM_START_BIT(x) (((x) & 0x3f) << 26) -#define G1_REG_DEC_CTRL2_SYNC_MARKER_E BIT(25) -#define G1_REG_DEC_CTRL2_TYPE1_QUANT_E BIT(24) -#define G1_REG_DEC_CTRL2_CH_QP_OFFSET(x) (((x) & 0x1f) << 19) -#define G1_REG_DEC_CTRL2_CH_QP_OFFSET2(x) (((x) & 0x1f) << 14) -#define G1_REG_DEC_CTRL2_FIELDPIC_FLAG_E BIT(0) -#define G1_REG_DEC_CTRL2_INTRADC_VLC_THR(x) (((x) & 0x7) << 16) -#define G1_REG_DEC_CTRL2_VOP_TIME_INCR(x) (((x) & 0xffff) << 0) -#define G1_REG_DEC_CTRL2_DQ_PROFILE BIT(24) -#define G1_REG_DEC_CTRL2_DQBI_LEVEL BIT(23) -#define G1_REG_DEC_CTRL2_RANGE_RED_FRM_E BIT(22) -#define G1_REG_DEC_CTRL2_FAST_UVMC_E BIT(20) -#define G1_REG_DEC_CTRL2_TRANSDCTAB BIT(17) -#define G1_REG_DEC_CTRL2_TRANSACFRM(x) (((x) & 0x3) << 15) -#define G1_REG_DEC_CTRL2_TRANSACFRM2(x) (((x) & 0x3) << 13) -#define G1_REG_DEC_CTRL2_MB_MODE_TAB(x) (((x) & 0x7) << 10) -#define G1_REG_DEC_CTRL2_MVTAB(x) (((x) & 0x7) << 7) -#define G1_REG_DEC_CTRL2_CBPTAB(x) (((x) & 0x7) << 4) -#define G1_REG_DEC_CTRL2_2MV_BLK_PAT_TAB(x) (((x) & 0x3) << 2) -#define G1_REG_DEC_CTRL2_4MV_BLK_PAT_TAB(x) (((x) & 0x3) << 0) -#define G1_REG_DEC_CTRL2_QSCALE_TYPE BIT(24) -#define G1_REG_DEC_CTRL2_CON_MV_E BIT(4) -#define G1_REG_DEC_CTRL2_INTRA_DC_PREC(x) (((x) & 0x3) << 2) -#define G1_REG_DEC_CTRL2_INTRA_VLC_TAB BIT(1) -#define G1_REG_DEC_CTRL2_FRAME_PRED_DCT BIT(0) -#define G1_REG_DEC_CTRL2_JPEG_QTABLES(x) (((x) & 0x3) << 11) -#define G1_REG_DEC_CTRL2_JPEG_MODE(x) (((x) & 0x7) << 8) -#define G1_REG_DEC_CTRL2_JPEG_FILRIGHT_E BIT(7) -#define G1_REG_DEC_CTRL2_JPEG_STREAM_ALL BIT(6) -#define G1_REG_DEC_CTRL2_CR_AC_VLCTABLE BIT(5) -#define G1_REG_DEC_CTRL2_CB_AC_VLCTABLE BIT(4) -#define G1_REG_DEC_CTRL2_CR_DC_VLCTABLE BIT(3) -#define G1_REG_DEC_CTRL2_CB_DC_VLCTABLE BIT(2) -#define G1_REG_DEC_CTRL2_CR_DC_VLCTABLE3 BIT(1) -#define G1_REG_DEC_CTRL2_CB_DC_VLCTABLE3 BIT(0) -#define G1_REG_DEC_CTRL2_STRM1_START_BIT(x) (((x) & 0x3f) << 18) -#define G1_REG_DEC_CTRL2_HUFFMAN_E BIT(17) -#define G1_REG_DEC_CTRL2_MULTISTREAM_E BIT(16) -#define G1_REG_DEC_CTRL2_BOOLEAN_VALUE(x) (((x) & 0xff) << 8) -#define G1_REG_DEC_CTRL2_BOOLEAN_RANGE(x) (((x) & 0xff) << 0) -#define G1_REG_DEC_CTRL2_ALPHA_OFFSET(x) (((x) & 0x1f) << 5) -#define G1_REG_DEC_CTRL2_BETA_OFFSET(x) (((x) & 0x1f) << 0) -#define G1_REG_DEC_CTRL3 0x018 -#define G1_REG_DEC_CTRL3_START_CODE_E BIT(31) -#define G1_REG_DEC_CTRL3_INIT_QP(x) (((x) & 0x3f) << 25) -#define G1_REG_DEC_CTRL3_CH_8PIX_ILEAV_E BIT(24) -#define G1_REG_DEC_CTRL3_STREAM_LEN_EXT(x) (((x) & 0xff) << 24) -#define G1_REG_DEC_CTRL3_STREAM_LEN(x) (((x) & 0xffffff) << 0) -#define G1_REG_DEC_CTRL4 0x01c -#define G1_REG_DEC_CTRL4_CABAC_E BIT(31) -#define G1_REG_DEC_CTRL4_BLACKWHITE_E BIT(30) -#define G1_REG_DEC_CTRL4_DIR_8X8_INFER_E BIT(29) -#define G1_REG_DEC_CTRL4_WEIGHT_PRED_E BIT(28) -#define G1_REG_DEC_CTRL4_WEIGHT_BIPR_IDC(x) (((x) & 0x3) << 26) -#define G1_REG_DEC_CTRL4_AVS_H264_H_EXT BIT(25) -#define G1_REG_DEC_CTRL4_FRAMENUM_LEN(x) (((x) & 0x1f) << 16) -#define G1_REG_DEC_CTRL4_FRAMENUM(x) (((x) & 0xffff) << 0) -#define G1_REG_DEC_CTRL4_BITPLANE0_E BIT(31) -#define G1_REG_DEC_CTRL4_BITPLANE1_E BIT(30) -#define G1_REG_DEC_CTRL4_BITPLANE2_E BIT(29) -#define G1_REG_DEC_CTRL4_ALT_PQUANT(x) (((x) & 0x1f) << 24) -#define G1_REG_DEC_CTRL4_DQ_EDGES(x) (((x) & 0xf) << 20) -#define G1_REG_DEC_CTRL4_TTMBF BIT(19) -#define G1_REG_DEC_CTRL4_PQINDEX(x) (((x) & 0x1f) << 14) -#define G1_REG_DEC_CTRL4_VC1_HEIGHT_EXT BIT(13) -#define G1_REG_DEC_CTRL4_BILIN_MC_E BIT(12) -#define G1_REG_DEC_CTRL4_UNIQP_E BIT(11) -#define G1_REG_DEC_CTRL4_HALFQP_E BIT(10) -#define G1_REG_DEC_CTRL4_TTFRM(x) (((x) & 0x3) << 8) -#define G1_REG_DEC_CTRL4_2ND_BYTE_EMUL_E BIT(7) -#define G1_REG_DEC_CTRL4_DQUANT_E BIT(6) -#define G1_REG_DEC_CTRL4_VC1_ADV_E BIT(5) -#define G1_REG_DEC_CTRL4_PJPEG_FILDOWN_E BIT(26) -#define G1_REG_DEC_CTRL4_PJPEG_WDIV8 BIT(25) -#define G1_REG_DEC_CTRL4_PJPEG_HDIV8 BIT(24) -#define G1_REG_DEC_CTRL4_PJPEG_AH(x) (((x) & 0xf) << 20) -#define G1_REG_DEC_CTRL4_PJPEG_AL(x) (((x) & 0xf) << 16) -#define G1_REG_DEC_CTRL4_PJPEG_SS(x) (((x) & 0xff) << 8) -#define G1_REG_DEC_CTRL4_PJPEG_SE(x) (((x) & 0xff) << 0) -#define G1_REG_DEC_CTRL4_DCT1_START_BIT(x) (((x) & 0x3f) << 26) -#define G1_REG_DEC_CTRL4_DCT2_START_BIT(x) (((x) & 0x3f) << 20) -#define G1_REG_DEC_CTRL4_CH_MV_RES BIT(13) -#define G1_REG_DEC_CTRL4_INIT_DC_MATCH0(x) (((x) & 0x7) << 9) -#define G1_REG_DEC_CTRL4_INIT_DC_MATCH1(x) (((x) & 0x7) << 6) -#define G1_REG_DEC_CTRL4_VP7_VERSION BIT(5) -#define G1_REG_DEC_CTRL5 0x020 -#define G1_REG_DEC_CTRL5_CONST_INTRA_E BIT(31) -#define G1_REG_DEC_CTRL5_FILT_CTRL_PRES BIT(30) -#define G1_REG_DEC_CTRL5_RDPIC_CNT_PRES BIT(29) -#define G1_REG_DEC_CTRL5_8X8TRANS_FLAG_E BIT(28) -#define G1_REG_DEC_CTRL5_REFPIC_MK_LEN(x) (((x) & 0x7ff) << 17) -#define G1_REG_DEC_CTRL5_IDR_PIC_E BIT(16) -#define G1_REG_DEC_CTRL5_IDR_PIC_ID(x) (((x) & 0xffff) << 0) -#define G1_REG_DEC_CTRL5_MV_SCALEFACTOR(x) (((x) & 0xff) << 24) -#define G1_REG_DEC_CTRL5_REF_DIST_FWD(x) (((x) & 0x1f) << 19) -#define G1_REG_DEC_CTRL5_REF_DIST_BWD(x) (((x) & 0x1f) << 14) -#define G1_REG_DEC_CTRL5_LOOP_FILT_LIMIT(x) (((x) & 0xf) << 14) -#define G1_REG_DEC_CTRL5_VARIANCE_TEST_E BIT(13) -#define G1_REG_DEC_CTRL5_MV_THRESHOLD(x) (((x) & 0x7) << 10) -#define G1_REG_DEC_CTRL5_VAR_THRESHOLD(x) (((x) & 0x3ff) << 0) -#define G1_REG_DEC_CTRL5_DIVX_IDCT_E BIT(8) -#define G1_REG_DEC_CTRL5_DIVX3_SLICE_SIZE(x) (((x) & 0xff) << 0) -#define G1_REG_DEC_CTRL5_PJPEG_REST_FREQ(x) (((x) & 0xffff) << 0) -#define G1_REG_DEC_CTRL5_RV_PROFILE(x) (((x) & 0x3) << 30) -#define G1_REG_DEC_CTRL5_RV_OSV_QUANT(x) (((x) & 0x3) << 28) -#define G1_REG_DEC_CTRL5_RV_FWD_SCALE(x) (((x) & 0x3fff) << 14) -#define G1_REG_DEC_CTRL5_RV_BWD_SCALE(x) (((x) & 0x3fff) << 0) -#define G1_REG_DEC_CTRL5_INIT_DC_COMP0(x) (((x) & 0xffff) << 16) -#define G1_REG_DEC_CTRL5_INIT_DC_COMP1(x) (((x) & 0xffff) << 0) -#define G1_REG_DEC_CTRL6 0x024 -#define G1_REG_DEC_CTRL6_PPS_ID(x) (((x) & 0xff) << 24) -#define G1_REG_DEC_CTRL6_REFIDX1_ACTIVE(x) (((x) & 0x1f) << 19) -#define G1_REG_DEC_CTRL6_REFIDX0_ACTIVE(x) (((x) & 0x1f) << 14) -#define G1_REG_DEC_CTRL6_POC_LENGTH(x) (((x) & 0xff) << 0) -#define G1_REG_DEC_CTRL6_ICOMP0_E BIT(24) -#define G1_REG_DEC_CTRL6_ISCALE0(x) (((x) & 0xff) << 16) -#define G1_REG_DEC_CTRL6_ISHIFT0(x) (((x) & 0xffff) << 0) -#define G1_REG_DEC_CTRL6_STREAM1_LEN(x) (((x) & 0xffffff) << 0) -#define G1_REG_DEC_CTRL6_PIC_SLICE_AM(x) (((x) & 0x1fff) << 0) -#define G1_REG_DEC_CTRL6_COEFFS_PART_AM(x) (((x) & 0xf) << 24) -#define G1_REG_FWD_PIC(i) (0x028 + ((i) * 0x4)) -#define G1_REG_FWD_PIC_PINIT_RLIST_F5(x) (((x) & 0x1f) << 25) -#define G1_REG_FWD_PIC_PINIT_RLIST_F4(x) (((x) & 0x1f) << 20) -#define G1_REG_FWD_PIC_PINIT_RLIST_F3(x) (((x) & 0x1f) << 15) -#define G1_REG_FWD_PIC_PINIT_RLIST_F2(x) (((x) & 0x1f) << 10) -#define G1_REG_FWD_PIC_PINIT_RLIST_F1(x) (((x) & 0x1f) << 5) -#define G1_REG_FWD_PIC_PINIT_RLIST_F0(x) (((x) & 0x1f) << 0) -#define G1_REG_FWD_PIC1_ICOMP1_E BIT(24) -#define G1_REG_FWD_PIC1_ISCALE1(x) (((x) & 0xff) << 16) -#define G1_REG_FWD_PIC1_ISHIFT1(x) (((x) & 0xffff) << 0) -#define G1_REG_FWD_PIC1_SEGMENT_BASE(x) ((x) << 0) -#define G1_REG_FWD_PIC1_SEGMENT_UPD_E BIT(1) -#define G1_REG_FWD_PIC1_SEGMENT_E BIT(0) -#define G1_REG_DEC_CTRL7 0x02c -#define G1_REG_DEC_CTRL7_PINIT_RLIST_F15(x) (((x) & 0x1f) << 25) -#define G1_REG_DEC_CTRL7_PINIT_RLIST_F14(x) (((x) & 0x1f) << 20) -#define G1_REG_DEC_CTRL7_PINIT_RLIST_F13(x) (((x) & 0x1f) << 15) -#define G1_REG_DEC_CTRL7_PINIT_RLIST_F12(x) (((x) & 0x1f) << 10) -#define G1_REG_DEC_CTRL7_PINIT_RLIST_F11(x) (((x) & 0x1f) << 5) -#define G1_REG_DEC_CTRL7_PINIT_RLIST_F10(x) (((x) & 0x1f) << 0) -#define G1_REG_DEC_CTRL7_ICOMP2_E BIT(24) -#define G1_REG_DEC_CTRL7_ISCALE2(x) (((x) & 0xff) << 16) -#define G1_REG_DEC_CTRL7_ISHIFT2(x) (((x) & 0xffff) << 0) -#define G1_REG_DEC_CTRL7_DCT3_START_BIT(x) (((x) & 0x3f) << 24) -#define G1_REG_DEC_CTRL7_DCT4_START_BIT(x) (((x) & 0x3f) << 18) -#define G1_REG_DEC_CTRL7_DCT5_START_BIT(x) (((x) & 0x3f) << 12) -#define G1_REG_DEC_CTRL7_DCT6_START_BIT(x) (((x) & 0x3f) << 6) -#define G1_REG_DEC_CTRL7_DCT7_START_BIT(x) (((x) & 0x3f) << 0) -#define G1_REG_ADDR_STR 0x030 -#define G1_REG_ADDR_DST 0x034 -#define G1_REG_ADDR_REF(i) (0x038 + ((i) * 0x4)) -#define G1_REG_ADDR_REF_FIELD_E BIT(1) -#define G1_REG_ADDR_REF_TOPC_E BIT(0) -#define G1_REG_REF_PIC(i) (0x078 + ((i) * 0x4)) -#define G1_REG_REF_PIC_FILT_TYPE_E BIT(31) -#define G1_REG_REF_PIC_FILT_SHARPNESS(x) (((x) & 0x7) << 28) -#define G1_REG_REF_PIC_MB_ADJ_0(x) (((x) & 0x7f) << 21) -#define G1_REG_REF_PIC_MB_ADJ_1(x) (((x) & 0x7f) << 14) -#define G1_REG_REF_PIC_MB_ADJ_2(x) (((x) & 0x7f) << 7) -#define G1_REG_REF_PIC_MB_ADJ_3(x) (((x) & 0x7f) << 0) -#define G1_REG_REF_PIC_REFER1_NBR(x) (((x) & 0xffff) << 16) -#define G1_REG_REF_PIC_REFER0_NBR(x) (((x) & 0xffff) << 0) -#define G1_REG_REF_PIC_LF_LEVEL_0(x) (((x) & 0x3f) << 18) -#define G1_REG_REF_PIC_LF_LEVEL_1(x) (((x) & 0x3f) << 12) -#define G1_REG_REF_PIC_LF_LEVEL_2(x) (((x) & 0x3f) << 6) -#define G1_REG_REF_PIC_LF_LEVEL_3(x) (((x) & 0x3f) << 0) -#define G1_REG_REF_PIC_QUANT_DELTA_0(x) (((x) & 0x1f) << 27) -#define G1_REG_REF_PIC_QUANT_DELTA_1(x) (((x) & 0x1f) << 22) -#define G1_REG_REF_PIC_QUANT_0(x) (((x) & 0x7ff) << 11) -#define G1_REG_REF_PIC_QUANT_1(x) (((x) & 0x7ff) << 0) -#define G1_REG_LT_REF 0x098 -#define G1_REG_VALID_REF 0x09c -#define G1_REG_ADDR_QTABLE 0x0a0 -#define G1_REG_ADDR_DIR_MV 0x0a4 -#define G1_REG_BD_REF_PIC(i) (0x0a8 + ((i) * 0x4)) -#define G1_REG_BD_REF_PIC_BINIT_RLIST_B2(x) (((x) & 0x1f) << 25) -#define G1_REG_BD_REF_PIC_BINIT_RLIST_F2(x) (((x) & 0x1f) << 20) -#define G1_REG_BD_REF_PIC_BINIT_RLIST_B1(x) (((x) & 0x1f) << 15) -#define G1_REG_BD_REF_PIC_BINIT_RLIST_F1(x) (((x) & 0x1f) << 10) -#define G1_REG_BD_REF_PIC_BINIT_RLIST_B0(x) (((x) & 0x1f) << 5) -#define G1_REG_BD_REF_PIC_BINIT_RLIST_F0(x) (((x) & 0x1f) << 0) -#define G1_REG_BD_REF_PIC_PRED_TAP_2_M1(x) (((x) & 0x3) << 10) -#define G1_REG_BD_REF_PIC_PRED_TAP_2_4(x) (((x) & 0x3) << 8) -#define G1_REG_BD_REF_PIC_PRED_TAP_4_M1(x) (((x) & 0x3) << 6) -#define G1_REG_BD_REF_PIC_PRED_TAP_4_4(x) (((x) & 0x3) << 4) -#define G1_REG_BD_REF_PIC_PRED_TAP_6_M1(x) (((x) & 0x3) << 2) -#define G1_REG_BD_REF_PIC_PRED_TAP_6_4(x) (((x) & 0x3) << 0) -#define G1_REG_BD_REF_PIC_QUANT_DELTA_2(x) (((x) & 0x1f) << 27) -#define G1_REG_BD_REF_PIC_QUANT_DELTA_3(x) (((x) & 0x1f) << 22) -#define G1_REG_BD_REF_PIC_QUANT_2(x) (((x) & 0x7ff) << 11) -#define G1_REG_BD_REF_PIC_QUANT_3(x) (((x) & 0x7ff) << 0) -#define G1_REG_BD_P_REF_PIC 0x0bc -#define G1_REG_BD_P_REF_PIC_QUANT_DELTA_4(x) (((x) & 0x1f) << 27) -#define G1_REG_BD_P_REF_PIC_PINIT_RLIST_F3(x) (((x) & 0x1f) << 25) -#define G1_REG_BD_P_REF_PIC_PINIT_RLIST_F2(x) (((x) & 0x1f) << 20) -#define G1_REG_BD_P_REF_PIC_PINIT_RLIST_F1(x) (((x) & 0x1f) << 15) -#define G1_REG_BD_P_REF_PIC_PINIT_RLIST_F0(x) (((x) & 0x1f) << 10) -#define G1_REG_BD_P_REF_PIC_BINIT_RLIST_B15(x) (((x) & 0x1f) << 5) -#define G1_REG_BD_P_REF_PIC_BINIT_RLIST_F15(x) (((x) & 0x1f) << 0) -#define G1_REG_ERR_CONC 0x0c0 -#define G1_REG_ERR_CONC_STARTMB_X(x) (((x) & 0x1ff) << 23) -#define G1_REG_ERR_CONC_STARTMB_Y(x) (((x) & 0xff) << 15) -#define G1_REG_PRED_FLT 0x0c4 -#define G1_REG_PRED_FLT_PRED_BC_TAP_0_0(x) (((x) & 0x3ff) << 22) -#define G1_REG_PRED_FLT_PRED_BC_TAP_0_1(x) (((x) & 0x3ff) << 12) -#define G1_REG_PRED_FLT_PRED_BC_TAP_0_2(x) (((x) & 0x3ff) << 2) -#define G1_REG_REF_BUF_CTRL 0x0cc -#define G1_REG_REF_BUF_CTRL_REFBU_E BIT(31) -#define G1_REG_REF_BUF_CTRL_REFBU_THR(x) (((x) & 0xfff) << 19) -#define G1_REG_REF_BUF_CTRL_REFBU_PICID(x) (((x) & 0x1f) << 14) -#define G1_REG_REF_BUF_CTRL_REFBU_EVAL_E BIT(13) -#define G1_REG_REF_BUF_CTRL_REFBU_FPARMOD_E BIT(12) -#define G1_REG_REF_BUF_CTRL_REFBU_Y_OFFSET(x) (((x) & 0x1ff) << 0) -#define G1_REG_REF_BUF_CTRL2 0x0dc -#define G1_REG_REF_BUF_CTRL2_REFBU2_BUF_E BIT(31) -#define G1_REG_REF_BUF_CTRL2_REFBU2_THR(x) (((x) & 0xfff) << 19) -#define G1_REG_REF_BUF_CTRL2_REFBU2_PICID(x) (((x) & 0x1f) << 14) -#define G1_REG_REF_BUF_CTRL2_APF_THRESHOLD(x) (((x) & 0x3fff) << 0) -#define G1_REG_SOFT_RESET 0x194 - -/* Post-processor registers. */ -#define G1_REG_PP_INTERRUPT G1_SWREG(60) -#define G1_REG_PP_READY_IRQ BIT(12) -#define G1_REG_PP_IRQ BIT(8) -#define G1_REG_PP_IRQ_DIS BIT(4) -#define G1_REG_PP_PIPELINE_EN BIT(1) -#define G1_REG_PP_EXTERNAL_TRIGGER BIT(0) -#define G1_REG_PP_DEV_CONFIG G1_SWREG(61) -#define G1_REG_PP_AXI_RD_ID(v) (((v) << 24) & GENMASK(31, 24)) -#define G1_REG_PP_AXI_WR_ID(v) (((v) << 16) & GENMASK(23, 16)) -#define G1_REG_PP_INSWAP32_E(v) ((v) ? BIT(10) : 0) -#define G1_REG_PP_DATA_DISC_E(v) ((v) ? BIT(9) : 0) -#define G1_REG_PP_CLK_GATE_E(v) ((v) ? BIT(8) : 0) -#define G1_REG_PP_IN_ENDIAN(v) ((v) ? BIT(7) : 0) -#define G1_REG_PP_OUT_ENDIAN(v) ((v) ? BIT(6) : 0) -#define G1_REG_PP_OUTSWAP32_E(v) ((v) ? BIT(5) : 0) -#define G1_REG_PP_MAX_BURST(v) (((v) << 0) & GENMASK(4, 0)) -#define G1_REG_PP_IN_LUMA_BASE G1_SWREG(63) -#define G1_REG_PP_IN_CB_BASE G1_SWREG(64) -#define G1_REG_PP_IN_CR_BASE G1_SWREG(65) -#define G1_REG_PP_OUT_LUMA_BASE G1_SWREG(66) -#define G1_REG_PP_OUT_CHROMA_BASE G1_SWREG(67) -#define G1_REG_PP_CONTRAST_ADJUST G1_SWREG(68) -#define G1_REG_PP_COLOR_CONVERSION G1_SWREG(69) -#define G1_REG_PP_COLOR_CONVERSION0 G1_SWREG(70) -#define G1_REG_PP_COLOR_CONVERSION1 G1_SWREG(71) -#define G1_REG_PP_INPUT_SIZE G1_SWREG(72) -#define G1_REG_PP_INPUT_SIZE_HEIGHT(v) (((v) << 9) & GENMASK(16, 9)) -#define G1_REG_PP_INPUT_SIZE_WIDTH(v) (((v) << 0) & GENMASK(8, 0)) -#define G1_REG_PP_SCALING0 G1_SWREG(79) -#define G1_REG_PP_PADD_R(v) (((v) << 23) & GENMASK(27, 23)) -#define G1_REG_PP_PADD_G(v) (((v) << 18) & GENMASK(22, 18)) -#define G1_REG_PP_RANGEMAP_Y(v) ((v) ? BIT(31) : 0) -#define G1_REG_PP_RANGEMAP_C(v) ((v) ? BIT(30) : 0) -#define G1_REG_PP_YCBCR_RANGE(v) ((v) ? BIT(29) : 0) -#define G1_REG_PP_RGB_16(v) ((v) ? BIT(28) : 0) -#define G1_REG_PP_SCALING1 G1_SWREG(80) -#define G1_REG_PP_PADD_B(v) (((v) << 18) & GENMASK(22, 18)) -#define G1_REG_PP_MASK_R G1_SWREG(82) -#define G1_REG_PP_MASK_G G1_SWREG(83) -#define G1_REG_PP_MASK_B G1_SWREG(84) -#define G1_REG_PP_CONTROL G1_SWREG(85) -#define G1_REG_PP_CONTROL_IN_FMT(v) (((v) << 29) & GENMASK(31, 29)) -#define G1_REG_PP_CONTROL_OUT_FMT(v) (((v) << 26) & GENMASK(28, 26)) -#define G1_REG_PP_CONTROL_OUT_HEIGHT(v) (((v) << 15) & GENMASK(25, 15)) -#define G1_REG_PP_CONTROL_OUT_WIDTH(v) (((v) << 4) & GENMASK(14, 4)) -#define G1_REG_PP_MASK1_ORIG_WIDTH G1_SWREG(88) -#define G1_REG_PP_ORIG_WIDTH(v) (((v) << 23) & GENMASK(31, 23)) -#define G1_REG_PP_DISPLAY_WIDTH G1_SWREG(92) -#define G1_REG_PP_FUSE G1_SWREG(99) - -#endif /* HANTRO_G1_REGS_H_ */ diff --git a/drivers/staging/media/hantro/hantro_g1_vp8_dec.c b/drivers/staging/media/hantro/hantro_g1_vp8_dec.c deleted file mode 100644 index 851eb67f19f5..000000000000 --- a/drivers/staging/media/hantro/hantro_g1_vp8_dec.c +++ /dev/null @@ -1,511 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VP8 codec driver - * - * Copyright (C) 2019 Rockchip Electronics Co., Ltd. - * ZhiChao Yu <zhichao.yu@rock-chips.com> - * - * Copyright (C) 2019 Google, Inc. - * Tomasz Figa <tfiga@chromium.org> - */ - -#include <media/v4l2-mem2mem.h> - -#include "hantro_hw.h" -#include "hantro.h" -#include "hantro_g1_regs.h" - -/* DCT partition base address regs */ -static const struct hantro_reg vp8_dec_dct_base[8] = { - { G1_REG_ADDR_STR, 0, 0xffffffff }, - { G1_REG_ADDR_REF(8), 0, 0xffffffff }, - { G1_REG_ADDR_REF(9), 0, 0xffffffff }, - { G1_REG_ADDR_REF(10), 0, 0xffffffff }, - { G1_REG_ADDR_REF(11), 0, 0xffffffff }, - { G1_REG_ADDR_REF(12), 0, 0xffffffff }, - { G1_REG_ADDR_REF(14), 0, 0xffffffff }, - { G1_REG_ADDR_REF(15), 0, 0xffffffff }, -}; - -/* Loop filter level regs */ -static const struct hantro_reg vp8_dec_lf_level[4] = { - { G1_REG_REF_PIC(2), 18, 0x3f }, - { G1_REG_REF_PIC(2), 12, 0x3f }, - { G1_REG_REF_PIC(2), 6, 0x3f }, - { G1_REG_REF_PIC(2), 0, 0x3f }, -}; - -/* Macroblock loop filter level adjustment regs */ -static const struct hantro_reg vp8_dec_mb_adj[4] = { - { G1_REG_REF_PIC(0), 21, 0x7f }, - { G1_REG_REF_PIC(0), 14, 0x7f }, - { G1_REG_REF_PIC(0), 7, 0x7f }, - { G1_REG_REF_PIC(0), 0, 0x7f }, -}; - -/* Reference frame adjustment regs */ -static const struct hantro_reg vp8_dec_ref_adj[4] = { - { G1_REG_REF_PIC(1), 21, 0x7f }, - { G1_REG_REF_PIC(1), 14, 0x7f }, - { G1_REG_REF_PIC(1), 7, 0x7f }, - { G1_REG_REF_PIC(1), 0, 0x7f }, -}; - -/* Quantizer */ -static const struct hantro_reg vp8_dec_quant[4] = { - { G1_REG_REF_PIC(3), 11, 0x7ff }, - { G1_REG_REF_PIC(3), 0, 0x7ff }, - { G1_REG_BD_REF_PIC(4), 11, 0x7ff }, - { G1_REG_BD_REF_PIC(4), 0, 0x7ff }, -}; - -/* Quantizer delta regs */ -static const struct hantro_reg vp8_dec_quant_delta[5] = { - { G1_REG_REF_PIC(3), 27, 0x1f }, - { G1_REG_REF_PIC(3), 22, 0x1f }, - { G1_REG_BD_REF_PIC(4), 27, 0x1f }, - { G1_REG_BD_REF_PIC(4), 22, 0x1f }, - { G1_REG_BD_P_REF_PIC, 27, 0x1f }, -}; - -/* DCT partition start bits regs */ -static const struct hantro_reg vp8_dec_dct_start_bits[8] = { - { G1_REG_DEC_CTRL2, 26, 0x3f }, { G1_REG_DEC_CTRL4, 26, 0x3f }, - { G1_REG_DEC_CTRL4, 20, 0x3f }, { G1_REG_DEC_CTRL7, 24, 0x3f }, - { G1_REG_DEC_CTRL7, 18, 0x3f }, { G1_REG_DEC_CTRL7, 12, 0x3f }, - { G1_REG_DEC_CTRL7, 6, 0x3f }, { G1_REG_DEC_CTRL7, 0, 0x3f }, -}; - -/* Precision filter tap regs */ -static const struct hantro_reg vp8_dec_pred_bc_tap[8][4] = { - { - { G1_REG_PRED_FLT, 22, 0x3ff }, - { G1_REG_PRED_FLT, 12, 0x3ff }, - { G1_REG_PRED_FLT, 2, 0x3ff }, - { G1_REG_REF_PIC(4), 22, 0x3ff }, - }, - { - { G1_REG_REF_PIC(4), 12, 0x3ff }, - { G1_REG_REF_PIC(4), 2, 0x3ff }, - { G1_REG_REF_PIC(5), 22, 0x3ff }, - { G1_REG_REF_PIC(5), 12, 0x3ff }, - }, - { - { G1_REG_REF_PIC(5), 2, 0x3ff }, - { G1_REG_REF_PIC(6), 22, 0x3ff }, - { G1_REG_REF_PIC(6), 12, 0x3ff }, - { G1_REG_REF_PIC(6), 2, 0x3ff }, - }, - { - { G1_REG_REF_PIC(7), 22, 0x3ff }, - { G1_REG_REF_PIC(7), 12, 0x3ff }, - { G1_REG_REF_PIC(7), 2, 0x3ff }, - { G1_REG_LT_REF, 22, 0x3ff }, - }, - { - { G1_REG_LT_REF, 12, 0x3ff }, - { G1_REG_LT_REF, 2, 0x3ff }, - { G1_REG_VALID_REF, 22, 0x3ff }, - { G1_REG_VALID_REF, 12, 0x3ff }, - }, - { - { G1_REG_VALID_REF, 2, 0x3ff }, - { G1_REG_BD_REF_PIC(0), 22, 0x3ff }, - { G1_REG_BD_REF_PIC(0), 12, 0x3ff }, - { G1_REG_BD_REF_PIC(0), 2, 0x3ff }, - }, - { - { G1_REG_BD_REF_PIC(1), 22, 0x3ff }, - { G1_REG_BD_REF_PIC(1), 12, 0x3ff }, - { G1_REG_BD_REF_PIC(1), 2, 0x3ff }, - { G1_REG_BD_REF_PIC(2), 22, 0x3ff }, - }, - { - { G1_REG_BD_REF_PIC(2), 12, 0x3ff }, - { G1_REG_BD_REF_PIC(2), 2, 0x3ff }, - { G1_REG_BD_REF_PIC(3), 22, 0x3ff }, - { G1_REG_BD_REF_PIC(3), 12, 0x3ff }, - }, -}; - -/* - * Set loop filters - */ -static void cfg_lf(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp8_frame *hdr) -{ - const struct v4l2_vp8_segment *seg = &hdr->segment; - const struct v4l2_vp8_loop_filter *lf = &hdr->lf; - struct hantro_dev *vpu = ctx->dev; - unsigned int i; - u32 reg; - - if (!(seg->flags & V4L2_VP8_SEGMENT_FLAG_ENABLED)) { - hantro_reg_write(vpu, &vp8_dec_lf_level[0], lf->level); - } else if (seg->flags & V4L2_VP8_SEGMENT_FLAG_DELTA_VALUE_MODE) { - for (i = 0; i < 4; i++) { - u32 lf_level = clamp(lf->level + seg->lf_update[i], - 0, 63); - - hantro_reg_write(vpu, &vp8_dec_lf_level[i], lf_level); - } - } else { - for (i = 0; i < 4; i++) - hantro_reg_write(vpu, &vp8_dec_lf_level[i], - seg->lf_update[i]); - } - - reg = G1_REG_REF_PIC_FILT_SHARPNESS(lf->sharpness_level); - if (lf->flags & V4L2_VP8_LF_FILTER_TYPE_SIMPLE) - reg |= G1_REG_REF_PIC_FILT_TYPE_E; - vdpu_write_relaxed(vpu, reg, G1_REG_REF_PIC(0)); - - if (lf->flags & V4L2_VP8_LF_ADJ_ENABLE) { - for (i = 0; i < 4; i++) { - hantro_reg_write(vpu, &vp8_dec_mb_adj[i], - lf->mb_mode_delta[i]); - hantro_reg_write(vpu, &vp8_dec_ref_adj[i], - lf->ref_frm_delta[i]); - } - } -} - -/* - * Set quantization parameters - */ -static void cfg_qp(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp8_frame *hdr) -{ - const struct v4l2_vp8_quantization *q = &hdr->quant; - const struct v4l2_vp8_segment *seg = &hdr->segment; - struct hantro_dev *vpu = ctx->dev; - unsigned int i; - - if (!(seg->flags & V4L2_VP8_SEGMENT_FLAG_ENABLED)) { - hantro_reg_write(vpu, &vp8_dec_quant[0], q->y_ac_qi); - } else if (seg->flags & V4L2_VP8_SEGMENT_FLAG_DELTA_VALUE_MODE) { - for (i = 0; i < 4; i++) { - u32 quant = clamp(q->y_ac_qi + seg->quant_update[i], - 0, 127); - - hantro_reg_write(vpu, &vp8_dec_quant[i], quant); - } - } else { - for (i = 0; i < 4; i++) - hantro_reg_write(vpu, &vp8_dec_quant[i], - seg->quant_update[i]); - } - - hantro_reg_write(vpu, &vp8_dec_quant_delta[0], q->y_dc_delta); - hantro_reg_write(vpu, &vp8_dec_quant_delta[1], q->y2_dc_delta); - hantro_reg_write(vpu, &vp8_dec_quant_delta[2], q->y2_ac_delta); - hantro_reg_write(vpu, &vp8_dec_quant_delta[3], q->uv_dc_delta); - hantro_reg_write(vpu, &vp8_dec_quant_delta[4], q->uv_ac_delta); -} - -/* - * set control partition and DCT partition regs - * - * VP8 frame stream data layout: - * - * first_part_size parttion_sizes[0] - * ^ ^ - * src_dma | | - * ^ +--------+------+ +-----+-----+ - * | | control part | | | - * +--------+----------------+------------------+-----------+-----+-----------+ - * | tag 3B | extra 7B | hdr | mb_data | DCT sz | DCT part0 | ... | DCT partn | - * +--------+-----------------------------------+-----------+-----+-----------+ - * | | | | - * v +----+---+ v - * mb_start | src_dma_end - * v - * DCT size part - * (num_dct-1)*3B - * Note: - * 1. only key-frames have extra 7-bytes - * 2. all offsets are base on src_dma - * 3. number of DCT parts is 1, 2, 4 or 8 - * 4. the addresses set to the VPU must be 64-bits aligned - */ -static void cfg_parts(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp8_frame *hdr) -{ - struct hantro_dev *vpu = ctx->dev; - struct vb2_v4l2_buffer *vb2_src; - u32 first_part_offset = V4L2_VP8_FRAME_IS_KEY_FRAME(hdr) ? 10 : 3; - u32 mb_size, mb_offset_bytes, mb_offset_bits, mb_start_bits; - u32 dct_size_part_size, dct_part_offset; - struct hantro_reg reg; - dma_addr_t src_dma; - u32 dct_part_total_len = 0; - u32 count = 0; - unsigned int i; - - vb2_src = hantro_get_src_buf(ctx); - src_dma = vb2_dma_contig_plane_dma_addr(&vb2_src->vb2_buf, 0); - - /* - * Calculate control partition mb data info - * @first_part_header_bits: bits offset of mb data from first - * part start pos - * @mb_offset_bits: bits offset of mb data from src_dma - * base addr - * @mb_offset_byte: bytes offset of mb data from src_dma - * base addr - * @mb_start_bits: bits offset of mb data from mb data - * 64bits alignment addr - */ - mb_offset_bits = first_part_offset * 8 + - hdr->first_part_header_bits + 8; - mb_offset_bytes = mb_offset_bits / 8; - mb_start_bits = mb_offset_bits - - (mb_offset_bytes & (~DEC_8190_ALIGN_MASK)) * 8; - mb_size = hdr->first_part_size - - (mb_offset_bytes - first_part_offset) + - (mb_offset_bytes & DEC_8190_ALIGN_MASK); - - /* Macroblock data aligned base addr */ - vdpu_write_relaxed(vpu, (mb_offset_bytes & (~DEC_8190_ALIGN_MASK)) - + src_dma, G1_REG_ADDR_REF(13)); - - /* Macroblock data start bits */ - reg.base = G1_REG_DEC_CTRL2; - reg.mask = 0x3f; - reg.shift = 18; - hantro_reg_write(vpu, ®, mb_start_bits); - - /* Macroblock aligned data length */ - reg.base = G1_REG_DEC_CTRL6; - reg.mask = 0x3fffff; - reg.shift = 0; - hantro_reg_write(vpu, ®, mb_size + 1); - - /* - * Calculate DCT partition info - * @dct_size_part_size: Containing sizes of DCT part, every DCT part - * has 3 bytes to store its size, except the last - * DCT part - * @dct_part_offset: bytes offset of DCT parts from src_dma base addr - * @dct_part_total_len: total size of all DCT parts - */ - dct_size_part_size = (hdr->num_dct_parts - 1) * 3; - dct_part_offset = first_part_offset + hdr->first_part_size; - for (i = 0; i < hdr->num_dct_parts; i++) - dct_part_total_len += hdr->dct_part_sizes[i]; - dct_part_total_len += dct_size_part_size; - dct_part_total_len += (dct_part_offset & DEC_8190_ALIGN_MASK); - - /* Number of DCT partitions */ - reg.base = G1_REG_DEC_CTRL6; - reg.mask = 0xf; - reg.shift = 24; - hantro_reg_write(vpu, ®, hdr->num_dct_parts - 1); - - /* DCT partition length */ - vdpu_write_relaxed(vpu, - G1_REG_DEC_CTRL3_STREAM_LEN(dct_part_total_len), - G1_REG_DEC_CTRL3); - - /* DCT partitions base address */ - for (i = 0; i < hdr->num_dct_parts; i++) { - u32 byte_offset = dct_part_offset + dct_size_part_size + count; - u32 base_addr = byte_offset + src_dma; - - hantro_reg_write(vpu, &vp8_dec_dct_base[i], - base_addr & (~DEC_8190_ALIGN_MASK)); - - hantro_reg_write(vpu, &vp8_dec_dct_start_bits[i], - (byte_offset & DEC_8190_ALIGN_MASK) * 8); - - count += hdr->dct_part_sizes[i]; - } -} - -/* - * prediction filter taps - * normal 6-tap filters - */ -static void cfg_tap(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp8_frame *hdr) -{ - struct hantro_dev *vpu = ctx->dev; - struct hantro_reg reg; - u32 val = 0; - int i, j; - - reg.base = G1_REG_BD_REF_PIC(3); - reg.mask = 0xf; - - if ((hdr->version & 0x03) != 0) - return; /* Tap filter not used. */ - - for (i = 0; i < 8; i++) { - val = (hantro_vp8_dec_mc_filter[i][0] << 2) | - hantro_vp8_dec_mc_filter[i][5]; - - for (j = 0; j < 4; j++) - hantro_reg_write(vpu, &vp8_dec_pred_bc_tap[i][j], - hantro_vp8_dec_mc_filter[i][j + 1]); - - switch (i) { - case 2: - reg.shift = 8; - break; - case 4: - reg.shift = 4; - break; - case 6: - reg.shift = 0; - break; - default: - continue; - } - - hantro_reg_write(vpu, ®, val); - } -} - -static void cfg_ref(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp8_frame *hdr, - struct vb2_v4l2_buffer *vb2_dst) -{ - struct hantro_dev *vpu = ctx->dev; - dma_addr_t ref; - - - ref = hantro_get_ref(ctx, hdr->last_frame_ts); - if (!ref) { - vpu_debug(0, "failed to find last frame ts=%llu\n", - hdr->last_frame_ts); - ref = vb2_dma_contig_plane_dma_addr(&vb2_dst->vb2_buf, 0); - } - vdpu_write_relaxed(vpu, ref, G1_REG_ADDR_REF(0)); - - ref = hantro_get_ref(ctx, hdr->golden_frame_ts); - if (!ref && hdr->golden_frame_ts) - vpu_debug(0, "failed to find golden frame ts=%llu\n", - hdr->golden_frame_ts); - if (!ref) - ref = vb2_dma_contig_plane_dma_addr(&vb2_dst->vb2_buf, 0); - if (hdr->flags & V4L2_VP8_FRAME_FLAG_SIGN_BIAS_GOLDEN) - ref |= G1_REG_ADDR_REF_TOPC_E; - vdpu_write_relaxed(vpu, ref, G1_REG_ADDR_REF(4)); - - ref = hantro_get_ref(ctx, hdr->alt_frame_ts); - if (!ref && hdr->alt_frame_ts) - vpu_debug(0, "failed to find alt frame ts=%llu\n", - hdr->alt_frame_ts); - if (!ref) - ref = vb2_dma_contig_plane_dma_addr(&vb2_dst->vb2_buf, 0); - if (hdr->flags & V4L2_VP8_FRAME_FLAG_SIGN_BIAS_ALT) - ref |= G1_REG_ADDR_REF_TOPC_E; - vdpu_write_relaxed(vpu, ref, G1_REG_ADDR_REF(5)); -} - -static void cfg_buffers(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp8_frame *hdr, - struct vb2_v4l2_buffer *vb2_dst) -{ - const struct v4l2_vp8_segment *seg = &hdr->segment; - struct hantro_dev *vpu = ctx->dev; - dma_addr_t dst_dma; - u32 reg; - - /* Set probability table buffer address */ - vdpu_write_relaxed(vpu, ctx->vp8_dec.prob_tbl.dma, - G1_REG_ADDR_QTABLE); - - /* Set segment map address */ - reg = G1_REG_FWD_PIC1_SEGMENT_BASE(ctx->vp8_dec.segment_map.dma); - if (seg->flags & V4L2_VP8_SEGMENT_FLAG_ENABLED) { - reg |= G1_REG_FWD_PIC1_SEGMENT_E; - if (seg->flags & V4L2_VP8_SEGMENT_FLAG_UPDATE_MAP) - reg |= G1_REG_FWD_PIC1_SEGMENT_UPD_E; - } - vdpu_write_relaxed(vpu, reg, G1_REG_FWD_PIC(0)); - - dst_dma = hantro_get_dec_buf_addr(ctx, &vb2_dst->vb2_buf); - vdpu_write_relaxed(vpu, dst_dma, G1_REG_ADDR_DST); -} - -int hantro_g1_vp8_dec_run(struct hantro_ctx *ctx) -{ - const struct v4l2_ctrl_vp8_frame *hdr; - struct hantro_dev *vpu = ctx->dev; - struct vb2_v4l2_buffer *vb2_dst; - size_t height = ctx->dst_fmt.height; - size_t width = ctx->dst_fmt.width; - u32 mb_width, mb_height; - u32 reg; - - hantro_start_prepare_run(ctx); - - hdr = hantro_get_ctrl(ctx, V4L2_CID_STATELESS_VP8_FRAME); - if (WARN_ON(!hdr)) - return -EINVAL; - - /* Reset segment_map buffer in keyframe */ - if (V4L2_VP8_FRAME_IS_KEY_FRAME(hdr) && ctx->vp8_dec.segment_map.cpu) - memset(ctx->vp8_dec.segment_map.cpu, 0, - ctx->vp8_dec.segment_map.size); - - hantro_vp8_prob_update(ctx, hdr); - - reg = G1_REG_CONFIG_DEC_TIMEOUT_E | - G1_REG_CONFIG_DEC_STRENDIAN_E | - G1_REG_CONFIG_DEC_INSWAP32_E | - G1_REG_CONFIG_DEC_STRSWAP32_E | - G1_REG_CONFIG_DEC_OUTSWAP32_E | - G1_REG_CONFIG_DEC_CLK_GATE_E | - G1_REG_CONFIG_DEC_IN_ENDIAN | - G1_REG_CONFIG_DEC_OUT_ENDIAN | - G1_REG_CONFIG_DEC_MAX_BURST(16); - vdpu_write_relaxed(vpu, reg, G1_REG_CONFIG); - - reg = G1_REG_DEC_CTRL0_DEC_MODE(10) | - G1_REG_DEC_CTRL0_DEC_AXI_AUTO; - if (!V4L2_VP8_FRAME_IS_KEY_FRAME(hdr)) - reg |= G1_REG_DEC_CTRL0_PIC_INTER_E; - if (!(hdr->flags & V4L2_VP8_FRAME_FLAG_MB_NO_SKIP_COEFF)) - reg |= G1_REG_DEC_CTRL0_SKIP_MODE; - if (hdr->lf.level == 0) - reg |= G1_REG_DEC_CTRL0_FILTERING_DIS; - vdpu_write_relaxed(vpu, reg, G1_REG_DEC_CTRL0); - - /* Frame dimensions */ - mb_width = MB_WIDTH(width); - mb_height = MB_HEIGHT(height); - reg = G1_REG_DEC_CTRL1_PIC_MB_WIDTH(mb_width) | - G1_REG_DEC_CTRL1_PIC_MB_HEIGHT_P(mb_height) | - G1_REG_DEC_CTRL1_PIC_MB_W_EXT(mb_width >> 9) | - G1_REG_DEC_CTRL1_PIC_MB_H_EXT(mb_height >> 8); - vdpu_write_relaxed(vpu, reg, G1_REG_DEC_CTRL1); - - /* Boolean decoder */ - reg = G1_REG_DEC_CTRL2_BOOLEAN_RANGE(hdr->coder_state.range) - | G1_REG_DEC_CTRL2_BOOLEAN_VALUE(hdr->coder_state.value); - vdpu_write_relaxed(vpu, reg, G1_REG_DEC_CTRL2); - - reg = 0; - if (hdr->version != 3) - reg |= G1_REG_DEC_CTRL4_VC1_HEIGHT_EXT; - if (hdr->version & 0x3) - reg |= G1_REG_DEC_CTRL4_BILIN_MC_E; - vdpu_write_relaxed(vpu, reg, G1_REG_DEC_CTRL4); - - cfg_lf(ctx, hdr); - cfg_qp(ctx, hdr); - cfg_parts(ctx, hdr); - cfg_tap(ctx, hdr); - - vb2_dst = hantro_get_dst_buf(ctx); - cfg_ref(ctx, hdr, vb2_dst); - cfg_buffers(ctx, hdr, vb2_dst); - - hantro_end_prepare_run(ctx); - - vdpu_write(vpu, G1_REG_INTERRUPT_DEC_E, G1_REG_INTERRUPT); - - return 0; -} diff --git a/drivers/staging/media/hantro/hantro_g2.c b/drivers/staging/media/hantro/hantro_g2.c deleted file mode 100644 index ee5f14c5f8f2..000000000000 --- a/drivers/staging/media/hantro/hantro_g2.c +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VPU codec driver - * - * Copyright (C) 2021 Collabora Ltd, Andrzej Pietrasiewicz <andrzej.p@collabora.com> - */ - -#include "hantro_hw.h" -#include "hantro_g2_regs.h" - -void hantro_g2_check_idle(struct hantro_dev *vpu) -{ - int i; - - for (i = 0; i < 3; i++) { - u32 status; - - /* Make sure the VPU is idle */ - status = vdpu_read(vpu, G2_REG_INTERRUPT); - if (status & G2_REG_INTERRUPT_DEC_E) { - dev_warn(vpu->dev, "device still running, aborting"); - status |= G2_REG_INTERRUPT_DEC_ABORT_E | G2_REG_INTERRUPT_DEC_IRQ_DIS; - vdpu_write(vpu, status, G2_REG_INTERRUPT); - } - } -} - -irqreturn_t hantro_g2_irq(int irq, void *dev_id) -{ - struct hantro_dev *vpu = dev_id; - enum vb2_buffer_state state; - u32 status; - - status = vdpu_read(vpu, G2_REG_INTERRUPT); - state = (status & G2_REG_INTERRUPT_DEC_RDY_INT) ? - VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR; - - vdpu_write(vpu, 0, G2_REG_INTERRUPT); - vdpu_write(vpu, G2_REG_CONFIG_DEC_CLK_GATE_E, G2_REG_CONFIG); - - hantro_irq_done(vpu, state); - - return IRQ_HANDLED; -} diff --git a/drivers/staging/media/hantro/hantro_g2_hevc_dec.c b/drivers/staging/media/hantro/hantro_g2_hevc_dec.c deleted file mode 100644 index 233ecd863d5f..000000000000 --- a/drivers/staging/media/hantro/hantro_g2_hevc_dec.c +++ /dev/null @@ -1,629 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VPU HEVC codec driver - * - * Copyright (C) 2020 Safran Passenger Innovations LLC - */ - -#include "hantro_hw.h" -#include "hantro_g2_regs.h" - -#define G2_ALIGN 16 - -static size_t hantro_hevc_chroma_offset(struct hantro_ctx *ctx) -{ - return ctx->dst_fmt.width * ctx->dst_fmt.height; -} - -static size_t hantro_hevc_motion_vectors_offset(struct hantro_ctx *ctx) -{ - size_t cr_offset = hantro_hevc_chroma_offset(ctx); - - return ALIGN((cr_offset * 3) / 2, G2_ALIGN); -} - -static void prepare_tile_info_buffer(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - const struct hantro_hevc_dec_ctrls *ctrls = &ctx->hevc_dec.ctrls; - const struct v4l2_ctrl_hevc_pps *pps = ctrls->pps; - const struct v4l2_ctrl_hevc_sps *sps = ctrls->sps; - u16 *p = (u16 *)((u8 *)ctx->hevc_dec.tile_sizes.cpu); - unsigned int num_tile_rows = pps->num_tile_rows_minus1 + 1; - unsigned int num_tile_cols = pps->num_tile_columns_minus1 + 1; - unsigned int pic_width_in_ctbs, pic_height_in_ctbs; - unsigned int max_log2_ctb_size, ctb_size; - bool tiles_enabled, uniform_spacing; - u32 no_chroma = 0; - - tiles_enabled = !!(pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED); - uniform_spacing = !!(pps->flags & V4L2_HEVC_PPS_FLAG_UNIFORM_SPACING); - - hantro_reg_write(vpu, &g2_tile_e, tiles_enabled); - - max_log2_ctb_size = sps->log2_min_luma_coding_block_size_minus3 + 3 + - sps->log2_diff_max_min_luma_coding_block_size; - pic_width_in_ctbs = (sps->pic_width_in_luma_samples + - (1 << max_log2_ctb_size) - 1) >> max_log2_ctb_size; - pic_height_in_ctbs = (sps->pic_height_in_luma_samples + (1 << max_log2_ctb_size) - 1) - >> max_log2_ctb_size; - ctb_size = 1 << max_log2_ctb_size; - - vpu_debug(1, "Preparing tile sizes buffer for %dx%d CTBs (CTB size %d)\n", - pic_width_in_ctbs, pic_height_in_ctbs, ctb_size); - - if (tiles_enabled) { - unsigned int i, j, h; - - vpu_debug(1, "Tiles enabled! %dx%d\n", num_tile_cols, num_tile_rows); - - hantro_reg_write(vpu, &g2_num_tile_rows, num_tile_rows); - hantro_reg_write(vpu, &g2_num_tile_cols, num_tile_cols); - - /* write width + height for each tile in pic */ - if (!uniform_spacing) { - u32 tmp_w = 0, tmp_h = 0; - - for (i = 0; i < num_tile_rows; i++) { - if (i == num_tile_rows - 1) - h = pic_height_in_ctbs - tmp_h; - else - h = pps->row_height_minus1[i] + 1; - tmp_h += h; - if (i == 0 && h == 1 && ctb_size == 16) - no_chroma = 1; - for (j = 0, tmp_w = 0; j < num_tile_cols - 1; j++) { - tmp_w += pps->column_width_minus1[j] + 1; - *p++ = pps->column_width_minus1[j] + 1; - *p++ = h; - if (i == 0 && h == 1 && ctb_size == 16) - no_chroma = 1; - } - /* last column */ - *p++ = pic_width_in_ctbs - tmp_w; - *p++ = h; - } - } else { /* uniform spacing */ - u32 tmp, prev_h, prev_w; - - for (i = 0, prev_h = 0; i < num_tile_rows; i++) { - tmp = (i + 1) * pic_height_in_ctbs / num_tile_rows; - h = tmp - prev_h; - prev_h = tmp; - if (i == 0 && h == 1 && ctb_size == 16) - no_chroma = 1; - for (j = 0, prev_w = 0; j < num_tile_cols; j++) { - tmp = (j + 1) * pic_width_in_ctbs / num_tile_cols; - *p++ = tmp - prev_w; - *p++ = h; - if (j == 0 && - (pps->column_width_minus1[0] + 1) == 1 && - ctb_size == 16) - no_chroma = 1; - prev_w = tmp; - } - } - } - } else { - hantro_reg_write(vpu, &g2_num_tile_rows, 1); - hantro_reg_write(vpu, &g2_num_tile_cols, 1); - - /* There's one tile, with dimensions equal to pic size. */ - p[0] = pic_width_in_ctbs; - p[1] = pic_height_in_ctbs; - } - - if (no_chroma) - vpu_debug(1, "%s: no chroma!\n", __func__); -} - -static int compute_header_skip_length(struct hantro_ctx *ctx) -{ - const struct hantro_hevc_dec_ctrls *ctrls = &ctx->hevc_dec.ctrls; - const struct v4l2_ctrl_hevc_decode_params *decode_params = ctrls->decode_params; - const struct v4l2_ctrl_hevc_sps *sps = ctrls->sps; - const struct v4l2_ctrl_hevc_pps *pps = ctrls->pps; - int skip = 0; - - if (pps->flags & V4L2_HEVC_PPS_FLAG_OUTPUT_FLAG_PRESENT) - /* size of pic_output_flag */ - skip++; - - if (sps->flags & V4L2_HEVC_SPS_FLAG_SEPARATE_COLOUR_PLANE) - /* size of pic_order_cnt_lsb */ - skip += 2; - - if (!(decode_params->flags & V4L2_HEVC_DECODE_PARAM_FLAG_IDR_PIC)) { - /* size of pic_order_cnt_lsb */ - skip += sps->log2_max_pic_order_cnt_lsb_minus4 + 4; - - /* size of short_term_ref_pic_set_sps_flag */ - skip++; - - if (decode_params->short_term_ref_pic_set_size) - /* size of st_ref_pic_set( num_short_term_ref_pic_sets ) */ - skip += decode_params->short_term_ref_pic_set_size; - else if (sps->num_short_term_ref_pic_sets > 1) - skip += fls(sps->num_short_term_ref_pic_sets - 1); - - skip += decode_params->long_term_ref_pic_set_size; - } - - return skip; -} - -static void set_params(struct hantro_ctx *ctx) -{ - const struct hantro_hevc_dec_ctrls *ctrls = &ctx->hevc_dec.ctrls; - const struct v4l2_ctrl_hevc_sps *sps = ctrls->sps; - const struct v4l2_ctrl_hevc_pps *pps = ctrls->pps; - const struct v4l2_ctrl_hevc_decode_params *decode_params = ctrls->decode_params; - struct hantro_dev *vpu = ctx->dev; - u32 min_log2_cb_size, max_log2_ctb_size, min_cb_size, max_ctb_size; - u32 pic_width_in_min_cbs, pic_height_in_min_cbs; - u32 pic_width_aligned, pic_height_aligned; - u32 partial_ctb_x, partial_ctb_y; - - hantro_reg_write(vpu, &g2_bit_depth_y_minus8, sps->bit_depth_luma_minus8); - hantro_reg_write(vpu, &g2_bit_depth_c_minus8, sps->bit_depth_chroma_minus8); - - hantro_reg_write(vpu, &g2_output_8_bits, 0); - - hantro_reg_write(vpu, &g2_hdr_skip_length, compute_header_skip_length(ctx)); - - min_log2_cb_size = sps->log2_min_luma_coding_block_size_minus3 + 3; - max_log2_ctb_size = min_log2_cb_size + sps->log2_diff_max_min_luma_coding_block_size; - - hantro_reg_write(vpu, &g2_min_cb_size, min_log2_cb_size); - hantro_reg_write(vpu, &g2_max_cb_size, max_log2_ctb_size); - - min_cb_size = 1 << min_log2_cb_size; - max_ctb_size = 1 << max_log2_ctb_size; - - pic_width_in_min_cbs = sps->pic_width_in_luma_samples / min_cb_size; - pic_height_in_min_cbs = sps->pic_height_in_luma_samples / min_cb_size; - pic_width_aligned = ALIGN(sps->pic_width_in_luma_samples, max_ctb_size); - pic_height_aligned = ALIGN(sps->pic_height_in_luma_samples, max_ctb_size); - - partial_ctb_x = !!(sps->pic_width_in_luma_samples != pic_width_aligned); - partial_ctb_y = !!(sps->pic_height_in_luma_samples != pic_height_aligned); - - hantro_reg_write(vpu, &g2_partial_ctb_x, partial_ctb_x); - hantro_reg_write(vpu, &g2_partial_ctb_y, partial_ctb_y); - - hantro_reg_write(vpu, &g2_pic_width_in_cbs, pic_width_in_min_cbs); - hantro_reg_write(vpu, &g2_pic_height_in_cbs, pic_height_in_min_cbs); - - hantro_reg_write(vpu, &g2_pic_width_4x4, - (pic_width_in_min_cbs * min_cb_size) / 4); - hantro_reg_write(vpu, &g2_pic_height_4x4, - (pic_height_in_min_cbs * min_cb_size) / 4); - - hantro_reg_write(vpu, &hevc_max_inter_hierdepth, - sps->max_transform_hierarchy_depth_inter); - hantro_reg_write(vpu, &hevc_max_intra_hierdepth, - sps->max_transform_hierarchy_depth_intra); - hantro_reg_write(vpu, &hevc_min_trb_size, - sps->log2_min_luma_transform_block_size_minus2 + 2); - hantro_reg_write(vpu, &hevc_max_trb_size, - sps->log2_min_luma_transform_block_size_minus2 + 2 + - sps->log2_diff_max_min_luma_transform_block_size); - - hantro_reg_write(vpu, &g2_tempor_mvp_e, - !!(sps->flags & V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED) && - !(decode_params->flags & V4L2_HEVC_DECODE_PARAM_FLAG_IDR_PIC)); - hantro_reg_write(vpu, &g2_strong_smooth_e, - !!(sps->flags & V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED)); - hantro_reg_write(vpu, &g2_asym_pred_e, - !!(sps->flags & V4L2_HEVC_SPS_FLAG_AMP_ENABLED)); - hantro_reg_write(vpu, &g2_sao_e, - !!(sps->flags & V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET)); - hantro_reg_write(vpu, &g2_sign_data_hide, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED)); - - if (pps->flags & V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED) { - hantro_reg_write(vpu, &g2_cu_qpd_e, 1); - hantro_reg_write(vpu, &g2_max_cu_qpd_depth, pps->diff_cu_qp_delta_depth); - } else { - hantro_reg_write(vpu, &g2_cu_qpd_e, 0); - hantro_reg_write(vpu, &g2_max_cu_qpd_depth, 0); - } - - hantro_reg_write(vpu, &g2_cb_qp_offset, pps->pps_cb_qp_offset); - hantro_reg_write(vpu, &g2_cr_qp_offset, pps->pps_cr_qp_offset); - - hantro_reg_write(vpu, &g2_filt_offset_beta, pps->pps_beta_offset_div2); - hantro_reg_write(vpu, &g2_filt_offset_tc, pps->pps_tc_offset_div2); - hantro_reg_write(vpu, &g2_slice_hdr_ext_e, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_SLICE_SEGMENT_HEADER_EXTENSION_PRESENT)); - hantro_reg_write(vpu, &g2_slice_hdr_ext_bits, pps->num_extra_slice_header_bits); - hantro_reg_write(vpu, &g2_slice_chqp_present, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT)); - hantro_reg_write(vpu, &g2_weight_bipr_idc, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED)); - hantro_reg_write(vpu, &g2_transq_bypass, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED)); - hantro_reg_write(vpu, &g2_list_mod_e, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_LISTS_MODIFICATION_PRESENT)); - hantro_reg_write(vpu, &g2_entropy_sync_e, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED)); - hantro_reg_write(vpu, &g2_cabac_init_present, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT)); - hantro_reg_write(vpu, &g2_idr_pic_e, - !!(decode_params->flags & V4L2_HEVC_DECODE_PARAM_FLAG_IRAP_PIC)); - hantro_reg_write(vpu, &hevc_parallel_merge, - pps->log2_parallel_merge_level_minus2 + 2); - hantro_reg_write(vpu, &g2_pcm_filt_d, - !!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED)); - hantro_reg_write(vpu, &g2_pcm_e, - !!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_ENABLED)); - if (sps->flags & V4L2_HEVC_SPS_FLAG_PCM_ENABLED) { - hantro_reg_write(vpu, &g2_max_pcm_size, - sps->log2_diff_max_min_pcm_luma_coding_block_size + - sps->log2_min_pcm_luma_coding_block_size_minus3 + 3); - hantro_reg_write(vpu, &g2_min_pcm_size, - sps->log2_min_pcm_luma_coding_block_size_minus3 + 3); - hantro_reg_write(vpu, &g2_bit_depth_pcm_y, - sps->pcm_sample_bit_depth_luma_minus1 + 1); - hantro_reg_write(vpu, &g2_bit_depth_pcm_c, - sps->pcm_sample_bit_depth_chroma_minus1 + 1); - } else { - hantro_reg_write(vpu, &g2_max_pcm_size, 0); - hantro_reg_write(vpu, &g2_min_pcm_size, 0); - hantro_reg_write(vpu, &g2_bit_depth_pcm_y, 0); - hantro_reg_write(vpu, &g2_bit_depth_pcm_c, 0); - } - - hantro_reg_write(vpu, &g2_start_code_e, 1); - hantro_reg_write(vpu, &g2_init_qp, pps->init_qp_minus26 + 26); - hantro_reg_write(vpu, &g2_weight_pred_e, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED)); - hantro_reg_write(vpu, &g2_cabac_init_present, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT)); - hantro_reg_write(vpu, &g2_const_intra_e, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED)); - hantro_reg_write(vpu, &g2_transform_skip, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED)); - hantro_reg_write(vpu, &g2_out_filtering_dis, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER)); - hantro_reg_write(vpu, &g2_filt_ctrl_pres, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT)); - hantro_reg_write(vpu, &g2_dependent_slice, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT_ENABLED)); - hantro_reg_write(vpu, &g2_filter_override, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_OVERRIDE_ENABLED)); - hantro_reg_write(vpu, &g2_refidx0_active, - pps->num_ref_idx_l0_default_active_minus1 + 1); - hantro_reg_write(vpu, &g2_refidx1_active, - pps->num_ref_idx_l1_default_active_minus1 + 1); - hantro_reg_write(vpu, &g2_apf_threshold, 8); -} - -static void set_ref_pic_list(struct hantro_ctx *ctx) -{ - const struct hantro_hevc_dec_ctrls *ctrls = &ctx->hevc_dec.ctrls; - struct hantro_dev *vpu = ctx->dev; - const struct v4l2_ctrl_hevc_decode_params *decode_params = ctrls->decode_params; - u32 list0[V4L2_HEVC_DPB_ENTRIES_NUM_MAX] = {}; - u32 list1[V4L2_HEVC_DPB_ENTRIES_NUM_MAX] = {}; - static const struct hantro_reg ref_pic_regs0[] = { - hevc_rlist_f0, - hevc_rlist_f1, - hevc_rlist_f2, - hevc_rlist_f3, - hevc_rlist_f4, - hevc_rlist_f5, - hevc_rlist_f6, - hevc_rlist_f7, - hevc_rlist_f8, - hevc_rlist_f9, - hevc_rlist_f10, - hevc_rlist_f11, - hevc_rlist_f12, - hevc_rlist_f13, - hevc_rlist_f14, - hevc_rlist_f15, - }; - static const struct hantro_reg ref_pic_regs1[] = { - hevc_rlist_b0, - hevc_rlist_b1, - hevc_rlist_b2, - hevc_rlist_b3, - hevc_rlist_b4, - hevc_rlist_b5, - hevc_rlist_b6, - hevc_rlist_b7, - hevc_rlist_b8, - hevc_rlist_b9, - hevc_rlist_b10, - hevc_rlist_b11, - hevc_rlist_b12, - hevc_rlist_b13, - hevc_rlist_b14, - hevc_rlist_b15, - }; - unsigned int i, j; - - /* List 0 contains: short term before, short term after and long term */ - j = 0; - for (i = 0; i < decode_params->num_poc_st_curr_before && j < ARRAY_SIZE(list0); i++) - list0[j++] = decode_params->poc_st_curr_before[i]; - for (i = 0; i < decode_params->num_poc_st_curr_after && j < ARRAY_SIZE(list0); i++) - list0[j++] = decode_params->poc_st_curr_after[i]; - for (i = 0; i < decode_params->num_poc_lt_curr && j < ARRAY_SIZE(list0); i++) - list0[j++] = decode_params->poc_lt_curr[i]; - - /* Fill the list, copying over and over */ - i = 0; - while (j < ARRAY_SIZE(list0)) - list0[j++] = list0[i++]; - - j = 0; - for (i = 0; i < decode_params->num_poc_st_curr_after && j < ARRAY_SIZE(list1); i++) - list1[j++] = decode_params->poc_st_curr_after[i]; - for (i = 0; i < decode_params->num_poc_st_curr_before && j < ARRAY_SIZE(list1); i++) - list1[j++] = decode_params->poc_st_curr_before[i]; - for (i = 0; i < decode_params->num_poc_lt_curr && j < ARRAY_SIZE(list1); i++) - list1[j++] = decode_params->poc_lt_curr[i]; - - i = 0; - while (j < ARRAY_SIZE(list1)) - list1[j++] = list1[i++]; - - for (i = 0; i < V4L2_HEVC_DPB_ENTRIES_NUM_MAX; i++) { - hantro_reg_write(vpu, &ref_pic_regs0[i], list0[i]); - hantro_reg_write(vpu, &ref_pic_regs1[i], list1[i]); - } -} - -static int set_ref(struct hantro_ctx *ctx) -{ - const struct hantro_hevc_dec_ctrls *ctrls = &ctx->hevc_dec.ctrls; - const struct v4l2_ctrl_hevc_pps *pps = ctrls->pps; - const struct v4l2_ctrl_hevc_decode_params *decode_params = ctrls->decode_params; - const struct v4l2_hevc_dpb_entry *dpb = decode_params->dpb; - dma_addr_t luma_addr, chroma_addr, mv_addr = 0; - struct hantro_dev *vpu = ctx->dev; - struct vb2_v4l2_buffer *vb2_dst; - struct hantro_decoded_buffer *dst; - size_t cr_offset = hantro_hevc_chroma_offset(ctx); - size_t mv_offset = hantro_hevc_motion_vectors_offset(ctx); - u32 max_ref_frames; - u16 dpb_longterm_e; - static const struct hantro_reg cur_poc[] = { - hevc_cur_poc_00, - hevc_cur_poc_01, - hevc_cur_poc_02, - hevc_cur_poc_03, - hevc_cur_poc_04, - hevc_cur_poc_05, - hevc_cur_poc_06, - hevc_cur_poc_07, - hevc_cur_poc_08, - hevc_cur_poc_09, - hevc_cur_poc_10, - hevc_cur_poc_11, - hevc_cur_poc_12, - hevc_cur_poc_13, - hevc_cur_poc_14, - hevc_cur_poc_15, - }; - unsigned int i; - - max_ref_frames = decode_params->num_poc_lt_curr + - decode_params->num_poc_st_curr_before + - decode_params->num_poc_st_curr_after; - /* - * Set max_ref_frames to non-zero to avoid HW hang when decoding - * badly marked I-frames. - */ - max_ref_frames = max_ref_frames ? max_ref_frames : 1; - hantro_reg_write(vpu, &g2_num_ref_frames, max_ref_frames); - hantro_reg_write(vpu, &g2_filter_over_slices, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED)); - hantro_reg_write(vpu, &g2_filter_over_tiles, - !!(pps->flags & V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED)); - - /* - * Write POC count diff from current pic. - */ - for (i = 0; i < decode_params->num_active_dpb_entries && i < ARRAY_SIZE(cur_poc); i++) { - char poc_diff = decode_params->pic_order_cnt_val - dpb[i].pic_order_cnt_val; - - hantro_reg_write(vpu, &cur_poc[i], poc_diff); - } - - if (i < ARRAY_SIZE(cur_poc)) { - /* - * After the references, fill one entry pointing to itself, - * i.e. difference is zero. - */ - hantro_reg_write(vpu, &cur_poc[i], 0); - i++; - } - - /* Fill the rest with the current picture */ - for (; i < ARRAY_SIZE(cur_poc); i++) - hantro_reg_write(vpu, &cur_poc[i], decode_params->pic_order_cnt_val); - - set_ref_pic_list(ctx); - - /* We will only keep the reference pictures that are still used */ - hantro_hevc_ref_init(ctx); - - /* Set up addresses of DPB buffers */ - dpb_longterm_e = 0; - for (i = 0; i < decode_params->num_active_dpb_entries && - i < (V4L2_HEVC_DPB_ENTRIES_NUM_MAX - 1); i++) { - luma_addr = hantro_hevc_get_ref_buf(ctx, dpb[i].pic_order_cnt_val); - if (!luma_addr) - return -ENOMEM; - - chroma_addr = luma_addr + cr_offset; - mv_addr = luma_addr + mv_offset; - - if (dpb[i].flags & V4L2_HEVC_DPB_ENTRY_LONG_TERM_REFERENCE) - dpb_longterm_e |= BIT(V4L2_HEVC_DPB_ENTRIES_NUM_MAX - 1 - i); - - hantro_write_addr(vpu, G2_REF_LUMA_ADDR(i), luma_addr); - hantro_write_addr(vpu, G2_REF_CHROMA_ADDR(i), chroma_addr); - hantro_write_addr(vpu, G2_REF_MV_ADDR(i), mv_addr); - } - - vb2_dst = hantro_get_dst_buf(ctx); - dst = vb2_to_hantro_decoded_buf(&vb2_dst->vb2_buf); - luma_addr = hantro_get_dec_buf_addr(ctx, &dst->base.vb.vb2_buf); - if (!luma_addr) - return -ENOMEM; - - if (hantro_hevc_add_ref_buf(ctx, decode_params->pic_order_cnt_val, luma_addr)) - return -EINVAL; - - chroma_addr = luma_addr + cr_offset; - mv_addr = luma_addr + mv_offset; - - hantro_write_addr(vpu, G2_REF_LUMA_ADDR(i), luma_addr); - hantro_write_addr(vpu, G2_REF_CHROMA_ADDR(i), chroma_addr); - hantro_write_addr(vpu, G2_REF_MV_ADDR(i++), mv_addr); - - hantro_write_addr(vpu, G2_OUT_LUMA_ADDR, luma_addr); - hantro_write_addr(vpu, G2_OUT_CHROMA_ADDR, chroma_addr); - hantro_write_addr(vpu, G2_OUT_MV_ADDR, mv_addr); - - for (; i < V4L2_HEVC_DPB_ENTRIES_NUM_MAX; i++) { - hantro_write_addr(vpu, G2_REF_LUMA_ADDR(i), 0); - hantro_write_addr(vpu, G2_REF_CHROMA_ADDR(i), 0); - hantro_write_addr(vpu, G2_REF_MV_ADDR(i), 0); - } - - hantro_reg_write(vpu, &g2_refer_lterm_e, dpb_longterm_e); - - return 0; -} - -static void set_buffers(struct hantro_ctx *ctx) -{ - struct vb2_v4l2_buffer *src_buf; - struct hantro_dev *vpu = ctx->dev; - dma_addr_t src_dma; - u32 src_len, src_buf_len; - - src_buf = hantro_get_src_buf(ctx); - - /* Source (stream) buffer. */ - src_dma = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); - src_len = vb2_get_plane_payload(&src_buf->vb2_buf, 0); - src_buf_len = vb2_plane_size(&src_buf->vb2_buf, 0); - - hantro_write_addr(vpu, G2_STREAM_ADDR, src_dma); - hantro_reg_write(vpu, &g2_stream_len, src_len); - hantro_reg_write(vpu, &g2_strm_buffer_len, src_buf_len); - hantro_reg_write(vpu, &g2_strm_start_offset, 0); - hantro_reg_write(vpu, &g2_write_mvs_e, 1); - - hantro_write_addr(vpu, G2_TILE_SIZES_ADDR, ctx->hevc_dec.tile_sizes.dma); - hantro_write_addr(vpu, G2_TILE_FILTER_ADDR, ctx->hevc_dec.tile_filter.dma); - hantro_write_addr(vpu, G2_TILE_SAO_ADDR, ctx->hevc_dec.tile_sao.dma); - hantro_write_addr(vpu, G2_TILE_BSD_ADDR, ctx->hevc_dec.tile_bsd.dma); -} - -static void prepare_scaling_list_buffer(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - const struct hantro_hevc_dec_ctrls *ctrls = &ctx->hevc_dec.ctrls; - const struct v4l2_ctrl_hevc_scaling_matrix *sc = ctrls->scaling; - const struct v4l2_ctrl_hevc_sps *sps = ctrls->sps; - u8 *p = ((u8 *)ctx->hevc_dec.scaling_lists.cpu); - unsigned int scaling_list_enabled; - unsigned int i, j, k; - - scaling_list_enabled = !!(sps->flags & V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED); - hantro_reg_write(vpu, &g2_scaling_list_e, scaling_list_enabled); - - if (!scaling_list_enabled) - return; - - for (i = 0; i < ARRAY_SIZE(sc->scaling_list_dc_coef_16x16); i++) - *p++ = sc->scaling_list_dc_coef_16x16[i]; - - for (i = 0; i < ARRAY_SIZE(sc->scaling_list_dc_coef_32x32); i++) - *p++ = sc->scaling_list_dc_coef_32x32[i]; - - /* 128-bit boundary */ - p += 8; - - /* write scaling lists column by column */ - - for (i = 0; i < 6; i++) - for (j = 0; j < 4; j++) - for (k = 0; k < 4; k++) - *p++ = sc->scaling_list_4x4[i][4 * k + j]; - - for (i = 0; i < 6; i++) - for (j = 0; j < 8; j++) - for (k = 0; k < 8; k++) - *p++ = sc->scaling_list_8x8[i][8 * k + j]; - - for (i = 0; i < 6; i++) - for (j = 0; j < 8; j++) - for (k = 0; k < 8; k++) - *p++ = sc->scaling_list_16x16[i][8 * k + j]; - - for (i = 0; i < 2; i++) - for (j = 0; j < 8; j++) - for (k = 0; k < 8; k++) - *p++ = sc->scaling_list_32x32[i][8 * k + j]; - - hantro_write_addr(vpu, G2_HEVC_SCALING_LIST_ADDR, ctx->hevc_dec.scaling_lists.dma); -} - -int hantro_g2_hevc_dec_run(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - int ret; - - hantro_g2_check_idle(vpu); - - /* Prepare HEVC decoder context. */ - ret = hantro_hevc_dec_prepare_run(ctx); - if (ret) - return ret; - - /* Configure hardware registers. */ - set_params(ctx); - - /* set reference pictures */ - ret = set_ref(ctx); - if (ret) - return ret; - - set_buffers(ctx); - prepare_tile_info_buffer(ctx); - - prepare_scaling_list_buffer(ctx); - - hantro_end_prepare_run(ctx); - - hantro_reg_write(vpu, &g2_mode, HEVC_DEC_MODE); - hantro_reg_write(vpu, &g2_clk_gate_e, 1); - - /* Don't disable output */ - hantro_reg_write(vpu, &g2_out_dis, 0); - - /* Don't compress buffers */ - hantro_reg_write(vpu, &g2_ref_compress_bypass, 1); - - /* Bus width and max burst */ - hantro_reg_write(vpu, &g2_buswidth, BUS_WIDTH_128); - hantro_reg_write(vpu, &g2_max_burst, 16); - - /* Swap */ - hantro_reg_write(vpu, &g2_strm_swap, 0xf); - hantro_reg_write(vpu, &g2_dirmv_swap, 0xf); - hantro_reg_write(vpu, &g2_compress_swap, 0xf); - - /* Start decoding! */ - vdpu_write(vpu, G2_REG_INTERRUPT_DEC_E, G2_REG_INTERRUPT); - - return 0; -} diff --git a/drivers/staging/media/hantro/hantro_g2_regs.h b/drivers/staging/media/hantro/hantro_g2_regs.h deleted file mode 100644 index 82606783591a..000000000000 --- a/drivers/staging/media/hantro/hantro_g2_regs.h +++ /dev/null @@ -1,325 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2021, Collabora - * - * Author: Benjamin Gaignard <benjamin.gaignard@collabora.com> - */ - -#ifndef HANTRO_G2_REGS_H_ -#define HANTRO_G2_REGS_H_ - -#include "hantro.h" - -#define G2_SWREG(nr) ((nr) * 4) - -#define G2_DEC_REG(b, s, m) \ - ((const struct hantro_reg) { \ - .base = G2_SWREG(b), \ - .shift = s, \ - .mask = m, \ - }) - -#define G2_REG_VERSION G2_SWREG(0) - -#define G2_REG_INTERRUPT G2_SWREG(1) -#define G2_REG_INTERRUPT_DEC_RDY_INT BIT(12) -#define G2_REG_INTERRUPT_DEC_ABORT_E BIT(5) -#define G2_REG_INTERRUPT_DEC_IRQ_DIS BIT(4) -#define G2_REG_INTERRUPT_DEC_E BIT(0) - -#define HEVC_DEC_MODE 0xc -#define VP9_DEC_MODE 0xd - -#define BUS_WIDTH_32 0 -#define BUS_WIDTH_64 1 -#define BUS_WIDTH_128 2 -#define BUS_WIDTH_256 3 - -#define g2_strm_swap G2_DEC_REG(2, 28, 0xf) -#define g2_strm_swap_old G2_DEC_REG(2, 27, 0x1f) -#define g2_pic_swap G2_DEC_REG(2, 22, 0x1f) -#define g2_dirmv_swap G2_DEC_REG(2, 20, 0xf) -#define g2_dirmv_swap_old G2_DEC_REG(2, 17, 0x1f) -#define g2_tab0_swap_old G2_DEC_REG(2, 12, 0x1f) -#define g2_tab1_swap_old G2_DEC_REG(2, 7, 0x1f) -#define g2_tab2_swap_old G2_DEC_REG(2, 2, 0x1f) - -#define g2_mode G2_DEC_REG(3, 27, 0x1f) -#define g2_compress_swap G2_DEC_REG(3, 20, 0xf) -#define g2_ref_compress_bypass G2_DEC_REG(3, 17, 0x1) -#define g2_out_rs_e G2_DEC_REG(3, 16, 0x1) -#define g2_out_dis G2_DEC_REG(3, 15, 0x1) -#define g2_out_filtering_dis G2_DEC_REG(3, 14, 0x1) -#define g2_write_mvs_e G2_DEC_REG(3, 12, 0x1) -#define g2_tab3_swap_old G2_DEC_REG(3, 7, 0x1f) -#define g2_rscan_swap G2_DEC_REG(3, 2, 0x1f) - -#define g2_pic_width_in_cbs G2_DEC_REG(4, 19, 0x1fff) -#define g2_pic_height_in_cbs G2_DEC_REG(4, 6, 0x1fff) -#define g2_num_ref_frames G2_DEC_REG(4, 0, 0x1f) - -#define g2_start_bit G2_DEC_REG(5, 25, 0x7f) -#define g2_scaling_list_e G2_DEC_REG(5, 24, 0x1) -#define g2_cb_qp_offset G2_DEC_REG(5, 19, 0x1f) -#define g2_cr_qp_offset G2_DEC_REG(5, 14, 0x1f) -#define g2_sign_data_hide G2_DEC_REG(5, 12, 0x1) -#define g2_tempor_mvp_e G2_DEC_REG(5, 11, 0x1) -#define g2_max_cu_qpd_depth G2_DEC_REG(5, 5, 0x3f) -#define g2_cu_qpd_e G2_DEC_REG(5, 4, 0x1) -#define g2_pix_shift G2_DEC_REG(5, 0, 0xf) - -#define g2_stream_len G2_DEC_REG(6, 0, 0xffffffff) - -#define g2_cabac_init_present G2_DEC_REG(7, 31, 0x1) -#define g2_weight_pred_e G2_DEC_REG(7, 28, 0x1) -#define g2_weight_bipr_idc G2_DEC_REG(7, 26, 0x3) -#define g2_filter_over_slices G2_DEC_REG(7, 25, 0x1) -#define g2_filter_over_tiles G2_DEC_REG(7, 24, 0x1) -#define g2_asym_pred_e G2_DEC_REG(7, 23, 0x1) -#define g2_sao_e G2_DEC_REG(7, 22, 0x1) -#define g2_pcm_filt_d G2_DEC_REG(7, 21, 0x1) -#define g2_slice_chqp_present G2_DEC_REG(7, 20, 0x1) -#define g2_dependent_slice G2_DEC_REG(7, 19, 0x1) -#define g2_filter_override G2_DEC_REG(7, 18, 0x1) -#define g2_strong_smooth_e G2_DEC_REG(7, 17, 0x1) -#define g2_filt_offset_beta G2_DEC_REG(7, 12, 0x1f) -#define g2_filt_offset_tc G2_DEC_REG(7, 7, 0x1f) -#define g2_slice_hdr_ext_e G2_DEC_REG(7, 6, 0x1) -#define g2_slice_hdr_ext_bits G2_DEC_REG(7, 3, 0x7) - -#define g2_const_intra_e G2_DEC_REG(8, 31, 0x1) -#define g2_filt_ctrl_pres G2_DEC_REG(8, 30, 0x1) -#define g2_bit_depth_y G2_DEC_REG(8, 21, 0xf) -#define g2_bit_depth_c G2_DEC_REG(8, 17, 0xf) -#define g2_idr_pic_e G2_DEC_REG(8, 16, 0x1) -#define g2_bit_depth_pcm_y G2_DEC_REG(8, 12, 0xf) -#define g2_bit_depth_pcm_c G2_DEC_REG(8, 8, 0xf) -#define g2_bit_depth_y_minus8 G2_DEC_REG(8, 6, 0x3) -#define g2_bit_depth_c_minus8 G2_DEC_REG(8, 4, 0x3) -#define g2_rs_out_bit_depth G2_DEC_REG(8, 4, 0xf) -#define g2_output_8_bits G2_DEC_REG(8, 3, 0x1) -#define g2_output_format G2_DEC_REG(8, 0, 0x7) -#define g2_pp_pix_shift G2_DEC_REG(8, 0, 0xf) - -#define g2_refidx1_active G2_DEC_REG(9, 19, 0x1f) -#define g2_refidx0_active G2_DEC_REG(9, 14, 0x1f) -#define g2_hdr_skip_length G2_DEC_REG(9, 0, 0x3fff) - -#define g2_start_code_e G2_DEC_REG(10, 31, 0x1) -#define g2_init_qp_old G2_DEC_REG(10, 25, 0x3f) -#define g2_init_qp G2_DEC_REG(10, 24, 0x7f) -#define g2_num_tile_cols_old G2_DEC_REG(10, 20, 0x1f) -#define g2_num_tile_cols G2_DEC_REG(10, 19, 0x1f) -#define g2_num_tile_rows_old G2_DEC_REG(10, 15, 0x1f) -#define g2_num_tile_rows G2_DEC_REG(10, 14, 0x1f) -#define g2_tile_e G2_DEC_REG(10, 1, 0x1) -#define g2_entropy_sync_e G2_DEC_REG(10, 0, 0x1) - -#define vp9_transform_mode G2_DEC_REG(11, 27, 0x7) -#define vp9_filt_sharpness G2_DEC_REG(11, 21, 0x7) -#define vp9_mcomp_filt_type G2_DEC_REG(11, 8, 0x7) -#define vp9_high_prec_mv_e G2_DEC_REG(11, 7, 0x1) -#define vp9_comp_pred_mode G2_DEC_REG(11, 4, 0x3) -#define vp9_gref_sign_bias G2_DEC_REG(11, 2, 0x1) -#define vp9_aref_sign_bias G2_DEC_REG(11, 0, 0x1) - -#define g2_refer_lterm_e G2_DEC_REG(12, 16, 0xffff) -#define g2_min_cb_size G2_DEC_REG(12, 13, 0x7) -#define g2_max_cb_size G2_DEC_REG(12, 10, 0x7) -#define g2_min_pcm_size G2_DEC_REG(12, 7, 0x7) -#define g2_max_pcm_size G2_DEC_REG(12, 4, 0x7) -#define g2_pcm_e G2_DEC_REG(12, 3, 0x1) -#define g2_transform_skip G2_DEC_REG(12, 2, 0x1) -#define g2_transq_bypass G2_DEC_REG(12, 1, 0x1) -#define g2_list_mod_e G2_DEC_REG(12, 0, 0x1) - -#define hevc_min_trb_size G2_DEC_REG(13, 13, 0x7) -#define hevc_max_trb_size G2_DEC_REG(13, 10, 0x7) -#define hevc_max_intra_hierdepth G2_DEC_REG(13, 7, 0x7) -#define hevc_max_inter_hierdepth G2_DEC_REG(13, 4, 0x7) -#define hevc_parallel_merge G2_DEC_REG(13, 0, 0xf) - -#define hevc_rlist_f0 G2_DEC_REG(14, 0, 0x1f) -#define hevc_rlist_f1 G2_DEC_REG(14, 10, 0x1f) -#define hevc_rlist_f2 G2_DEC_REG(14, 20, 0x1f) -#define hevc_rlist_b0 G2_DEC_REG(14, 5, 0x1f) -#define hevc_rlist_b1 G2_DEC_REG(14, 15, 0x1f) -#define hevc_rlist_b2 G2_DEC_REG(14, 25, 0x1f) - -#define hevc_rlist_f3 G2_DEC_REG(15, 0, 0x1f) -#define hevc_rlist_f4 G2_DEC_REG(15, 10, 0x1f) -#define hevc_rlist_f5 G2_DEC_REG(15, 20, 0x1f) -#define hevc_rlist_b3 G2_DEC_REG(15, 5, 0x1f) -#define hevc_rlist_b4 G2_DEC_REG(15, 15, 0x1f) -#define hevc_rlist_b5 G2_DEC_REG(15, 25, 0x1f) - -#define hevc_rlist_f6 G2_DEC_REG(16, 0, 0x1f) -#define hevc_rlist_f7 G2_DEC_REG(16, 10, 0x1f) -#define hevc_rlist_f8 G2_DEC_REG(16, 20, 0x1f) -#define hevc_rlist_b6 G2_DEC_REG(16, 5, 0x1f) -#define hevc_rlist_b7 G2_DEC_REG(16, 15, 0x1f) -#define hevc_rlist_b8 G2_DEC_REG(16, 25, 0x1f) - -#define hevc_rlist_f9 G2_DEC_REG(17, 0, 0x1f) -#define hevc_rlist_f10 G2_DEC_REG(17, 10, 0x1f) -#define hevc_rlist_f11 G2_DEC_REG(17, 20, 0x1f) -#define hevc_rlist_b9 G2_DEC_REG(17, 5, 0x1f) -#define hevc_rlist_b10 G2_DEC_REG(17, 15, 0x1f) -#define hevc_rlist_b11 G2_DEC_REG(17, 25, 0x1f) - -#define hevc_rlist_f12 G2_DEC_REG(18, 0, 0x1f) -#define hevc_rlist_f13 G2_DEC_REG(18, 10, 0x1f) -#define hevc_rlist_f14 G2_DEC_REG(18, 20, 0x1f) -#define hevc_rlist_b12 G2_DEC_REG(18, 5, 0x1f) -#define hevc_rlist_b13 G2_DEC_REG(18, 15, 0x1f) -#define hevc_rlist_b14 G2_DEC_REG(18, 25, 0x1f) - -#define hevc_rlist_f15 G2_DEC_REG(19, 0, 0x1f) -#define hevc_rlist_b15 G2_DEC_REG(19, 5, 0x1f) - -#define g2_partial_ctb_x G2_DEC_REG(20, 31, 0x1) -#define g2_partial_ctb_y G2_DEC_REG(20, 30, 0x1) -#define g2_pic_width_4x4 G2_DEC_REG(20, 16, 0xfff) -#define g2_pic_height_4x4 G2_DEC_REG(20, 0, 0xfff) - -#define vp9_qp_delta_y_dc G2_DEC_REG(13, 23, 0x3f) -#define vp9_qp_delta_ch_dc G2_DEC_REG(13, 17, 0x3f) -#define vp9_qp_delta_ch_ac G2_DEC_REG(13, 11, 0x3f) -#define vp9_last_sign_bias G2_DEC_REG(13, 10, 0x1) -#define vp9_lossless_e G2_DEC_REG(13, 9, 0x1) -#define vp9_comp_pred_var_ref1 G2_DEC_REG(13, 7, 0x3) -#define vp9_comp_pred_var_ref0 G2_DEC_REG(13, 5, 0x3) -#define vp9_comp_pred_fixed_ref G2_DEC_REG(13, 3, 0x3) -#define vp9_segment_temp_upd_e G2_DEC_REG(13, 2, 0x1) -#define vp9_segment_upd_e G2_DEC_REG(13, 1, 0x1) -#define vp9_segment_e G2_DEC_REG(13, 0, 0x1) - -#define vp9_filt_level G2_DEC_REG(14, 18, 0x3f) -#define vp9_refpic_seg0 G2_DEC_REG(14, 15, 0x7) -#define vp9_skip_seg0 G2_DEC_REG(14, 14, 0x1) -#define vp9_filt_level_seg0 G2_DEC_REG(14, 8, 0x3f) -#define vp9_quant_seg0 G2_DEC_REG(14, 0, 0xff) - -#define vp9_refpic_seg1 G2_DEC_REG(15, 15, 0x7) -#define vp9_skip_seg1 G2_DEC_REG(15, 14, 0x1) -#define vp9_filt_level_seg1 G2_DEC_REG(15, 8, 0x3f) -#define vp9_quant_seg1 G2_DEC_REG(15, 0, 0xff) - -#define vp9_refpic_seg2 G2_DEC_REG(16, 15, 0x7) -#define vp9_skip_seg2 G2_DEC_REG(16, 14, 0x1) -#define vp9_filt_level_seg2 G2_DEC_REG(16, 8, 0x3f) -#define vp9_quant_seg2 G2_DEC_REG(16, 0, 0xff) - -#define vp9_refpic_seg3 G2_DEC_REG(17, 15, 0x7) -#define vp9_skip_seg3 G2_DEC_REG(17, 14, 0x1) -#define vp9_filt_level_seg3 G2_DEC_REG(17, 8, 0x3f) -#define vp9_quant_seg3 G2_DEC_REG(17, 0, 0xff) - -#define vp9_refpic_seg4 G2_DEC_REG(18, 15, 0x7) -#define vp9_skip_seg4 G2_DEC_REG(18, 14, 0x1) -#define vp9_filt_level_seg4 G2_DEC_REG(18, 8, 0x3f) -#define vp9_quant_seg4 G2_DEC_REG(18, 0, 0xff) - -#define vp9_refpic_seg5 G2_DEC_REG(19, 15, 0x7) -#define vp9_skip_seg5 G2_DEC_REG(19, 14, 0x1) -#define vp9_filt_level_seg5 G2_DEC_REG(19, 8, 0x3f) -#define vp9_quant_seg5 G2_DEC_REG(19, 0, 0xff) - -#define hevc_cur_poc_00 G2_DEC_REG(46, 24, 0xff) -#define hevc_cur_poc_01 G2_DEC_REG(46, 16, 0xff) -#define hevc_cur_poc_02 G2_DEC_REG(46, 8, 0xff) -#define hevc_cur_poc_03 G2_DEC_REG(46, 0, 0xff) - -#define hevc_cur_poc_04 G2_DEC_REG(47, 24, 0xff) -#define hevc_cur_poc_05 G2_DEC_REG(47, 16, 0xff) -#define hevc_cur_poc_06 G2_DEC_REG(47, 8, 0xff) -#define hevc_cur_poc_07 G2_DEC_REG(47, 0, 0xff) - -#define hevc_cur_poc_08 G2_DEC_REG(48, 24, 0xff) -#define hevc_cur_poc_09 G2_DEC_REG(48, 16, 0xff) -#define hevc_cur_poc_10 G2_DEC_REG(48, 8, 0xff) -#define hevc_cur_poc_11 G2_DEC_REG(48, 0, 0xff) - -#define hevc_cur_poc_12 G2_DEC_REG(49, 24, 0xff) -#define hevc_cur_poc_13 G2_DEC_REG(49, 16, 0xff) -#define hevc_cur_poc_14 G2_DEC_REG(49, 8, 0xff) -#define hevc_cur_poc_15 G2_DEC_REG(49, 0, 0xff) - -#define vp9_refpic_seg6 G2_DEC_REG(31, 15, 0x7) -#define vp9_skip_seg6 G2_DEC_REG(31, 14, 0x1) -#define vp9_filt_level_seg6 G2_DEC_REG(31, 8, 0x3f) -#define vp9_quant_seg6 G2_DEC_REG(31, 0, 0xff) - -#define vp9_refpic_seg7 G2_DEC_REG(32, 15, 0x7) -#define vp9_skip_seg7 G2_DEC_REG(32, 14, 0x1) -#define vp9_filt_level_seg7 G2_DEC_REG(32, 8, 0x3f) -#define vp9_quant_seg7 G2_DEC_REG(32, 0, 0xff) - -#define vp9_lref_width G2_DEC_REG(33, 16, 0xffff) -#define vp9_lref_height G2_DEC_REG(33, 0, 0xffff) - -#define vp9_gref_width G2_DEC_REG(34, 16, 0xffff) -#define vp9_gref_height G2_DEC_REG(34, 0, 0xffff) - -#define vp9_aref_width G2_DEC_REG(35, 16, 0xffff) -#define vp9_aref_height G2_DEC_REG(35, 0, 0xffff) - -#define vp9_lref_hor_scale G2_DEC_REG(36, 16, 0xffff) -#define vp9_lref_ver_scale G2_DEC_REG(36, 0, 0xffff) - -#define vp9_gref_hor_scale G2_DEC_REG(37, 16, 0xffff) -#define vp9_gref_ver_scale G2_DEC_REG(37, 0, 0xffff) - -#define vp9_aref_hor_scale G2_DEC_REG(38, 16, 0xffff) -#define vp9_aref_ver_scale G2_DEC_REG(38, 0, 0xffff) - -#define vp9_filt_ref_adj_0 G2_DEC_REG(46, 24, 0x7f) -#define vp9_filt_ref_adj_1 G2_DEC_REG(46, 16, 0x7f) -#define vp9_filt_ref_adj_2 G2_DEC_REG(46, 8, 0x7f) -#define vp9_filt_ref_adj_3 G2_DEC_REG(46, 0, 0x7f) - -#define vp9_filt_mb_adj_0 G2_DEC_REG(47, 24, 0x7f) -#define vp9_filt_mb_adj_1 G2_DEC_REG(47, 16, 0x7f) -#define vp9_filt_mb_adj_2 G2_DEC_REG(47, 8, 0x7f) -#define vp9_filt_mb_adj_3 G2_DEC_REG(47, 0, 0x7f) - -#define g2_apf_threshold G2_DEC_REG(55, 0, 0xffff) - -#define g2_clk_gate_e G2_DEC_REG(58, 16, 0x1) -#define g2_double_buffer_e G2_DEC_REG(58, 15, 0x1) -#define g2_buswidth G2_DEC_REG(58, 8, 0x7) -#define g2_max_burst G2_DEC_REG(58, 0, 0xff) - -#define g2_down_scale_e G2_DEC_REG(184, 7, 0x1) -#define g2_down_scale_y G2_DEC_REG(184, 2, 0x3) -#define g2_down_scale_x G2_DEC_REG(184, 0, 0x3) - -#define G2_REG_CONFIG G2_SWREG(58) -#define G2_REG_CONFIG_DEC_CLK_GATE_E BIT(16) -#define G2_REG_CONFIG_DEC_CLK_GATE_IDLE_E BIT(17) - -#define G2_OUT_LUMA_ADDR (G2_SWREG(65)) -#define G2_REF_LUMA_ADDR(i) (G2_SWREG(67) + ((i) * 0x8)) -#define G2_VP9_SEGMENT_WRITE_ADDR (G2_SWREG(79)) -#define G2_VP9_SEGMENT_READ_ADDR (G2_SWREG(81)) -#define G2_OUT_CHROMA_ADDR (G2_SWREG(99)) -#define G2_REF_CHROMA_ADDR(i) (G2_SWREG(101) + ((i) * 0x8)) -#define G2_OUT_MV_ADDR (G2_SWREG(133)) -#define G2_REF_MV_ADDR(i) (G2_SWREG(135) + ((i) * 0x8)) -#define G2_TILE_SIZES_ADDR (G2_SWREG(167)) -#define G2_STREAM_ADDR (G2_SWREG(169)) -#define G2_HEVC_SCALING_LIST_ADDR (G2_SWREG(171)) -#define G2_VP9_CTX_COUNT_ADDR (G2_SWREG(171)) -#define G2_VP9_PROBS_ADDR (G2_SWREG(173)) -#define G2_RS_OUT_LUMA_ADDR (G2_SWREG(175)) -#define G2_RS_OUT_CHROMA_ADDR (G2_SWREG(177)) -#define G2_TILE_FILTER_ADDR (G2_SWREG(179)) -#define G2_TILE_SAO_ADDR (G2_SWREG(181)) -#define G2_TILE_BSD_ADDR (G2_SWREG(183)) -#define G2_DS_DST (G2_SWREG(186)) -#define G2_DS_DST_CHR (G2_SWREG(188)) - -#define g2_strm_buffer_len G2_DEC_REG(258, 0, 0xffffffff) -#define g2_strm_start_offset G2_DEC_REG(259, 0, 0xffffffff) - -#endif diff --git a/drivers/staging/media/hantro/hantro_g2_vp9_dec.c b/drivers/staging/media/hantro/hantro_g2_vp9_dec.c deleted file mode 100644 index 6fc4b555517f..000000000000 --- a/drivers/staging/media/hantro/hantro_g2_vp9_dec.c +++ /dev/null @@ -1,1014 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VP9 codec driver - * - * Copyright (C) 2021 Collabora Ltd. - */ -#include "media/videobuf2-core.h" -#include "media/videobuf2-dma-contig.h" -#include "media/videobuf2-v4l2.h" -#include <linux/kernel.h> -#include <linux/vmalloc.h> -#include <media/v4l2-mem2mem.h> -#include <media/v4l2-vp9.h> - -#include "hantro.h" -#include "hantro_vp9.h" -#include "hantro_g2_regs.h" - -#define G2_ALIGN 16 - -enum hantro_ref_frames { - INTRA_FRAME = 0, - LAST_FRAME = 1, - GOLDEN_FRAME = 2, - ALTREF_FRAME = 3, - MAX_REF_FRAMES = 4 -}; - -static int start_prepare_run(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame **dec_params) -{ - const struct v4l2_ctrl_vp9_compressed_hdr *prob_updates; - struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec; - struct v4l2_ctrl *ctrl; - unsigned int fctx_idx; - - /* v4l2-specific stuff */ - hantro_start_prepare_run(ctx); - - ctrl = v4l2_ctrl_find(&ctx->ctrl_handler, V4L2_CID_STATELESS_VP9_FRAME); - if (WARN_ON(!ctrl)) - return -EINVAL; - *dec_params = ctrl->p_cur.p; - - ctrl = v4l2_ctrl_find(&ctx->ctrl_handler, V4L2_CID_STATELESS_VP9_COMPRESSED_HDR); - if (WARN_ON(!ctrl)) - return -EINVAL; - prob_updates = ctrl->p_cur.p; - vp9_ctx->cur.tx_mode = prob_updates->tx_mode; - - /* - * vp9 stuff - * - * by this point the userspace has done all parts of 6.2 uncompressed_header() - * except this fragment: - * if ( FrameIsIntra || error_resilient_mode ) { - * setup_past_independence ( ) - * if ( frame_type == KEY_FRAME || error_resilient_mode == 1 || - * reset_frame_context == 3 ) { - * for ( i = 0; i < 4; i ++ ) { - * save_probs( i ) - * } - * } else if ( reset_frame_context == 2 ) { - * save_probs( frame_context_idx ) - * } - * frame_context_idx = 0 - * } - */ - fctx_idx = v4l2_vp9_reset_frame_ctx(*dec_params, vp9_ctx->frame_context); - vp9_ctx->cur.frame_context_idx = fctx_idx; - - /* 6.1 frame(sz): load_probs() and load_probs2() */ - vp9_ctx->probability_tables = vp9_ctx->frame_context[fctx_idx]; - - /* - * The userspace has also performed 6.3 compressed_header(), but handling the - * probs in a special way. All probs which need updating, except MV-related, - * have been read from the bitstream and translated through inv_map_table[], - * but no 6.3.6 inv_recenter_nonneg(v, m) has been performed. The values passed - * by userspace are either translated values (there are no 0 values in - * inv_map_table[]), or zero to indicate no update. All MV-related probs which need - * updating have been read from the bitstream and (mv_prob << 1) | 1 has been - * performed. The values passed by userspace are either new values - * to replace old ones (the above mentioned shift and bitwise or never result in - * a zero) or zero to indicate no update. - * fw_update_probs() performs actual probs updates or leaves probs as-is - * for values for which a zero was passed from userspace. - */ - v4l2_vp9_fw_update_probs(&vp9_ctx->probability_tables, prob_updates, *dec_params); - - return 0; -} - -static size_t chroma_offset(const struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp9_frame *dec_params) -{ - int bytes_per_pixel = dec_params->bit_depth == 8 ? 1 : 2; - - return ctx->src_fmt.width * ctx->src_fmt.height * bytes_per_pixel; -} - -static size_t mv_offset(const struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp9_frame *dec_params) -{ - size_t cr_offset = chroma_offset(ctx, dec_params); - - return ALIGN((cr_offset * 3) / 2, G2_ALIGN); -} - -static struct hantro_decoded_buffer * -get_ref_buf(struct hantro_ctx *ctx, struct vb2_v4l2_buffer *dst, u64 timestamp) -{ - struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx; - struct vb2_queue *cap_q = &m2m_ctx->cap_q_ctx.q; - struct vb2_buffer *buf; - - /* - * If a ref is unused or invalid, address of current destination - * buffer is returned. - */ - buf = vb2_find_buffer(cap_q, timestamp); - if (!buf) - buf = &dst->vb2_buf; - - return vb2_to_hantro_decoded_buf(buf); -} - -static void update_dec_buf_info(struct hantro_decoded_buffer *buf, - const struct v4l2_ctrl_vp9_frame *dec_params) -{ - buf->vp9.width = dec_params->frame_width_minus_1 + 1; - buf->vp9.height = dec_params->frame_height_minus_1 + 1; - buf->vp9.bit_depth = dec_params->bit_depth; -} - -static void update_ctx_cur_info(struct hantro_vp9_dec_hw_ctx *vp9_ctx, - struct hantro_decoded_buffer *buf, - const struct v4l2_ctrl_vp9_frame *dec_params) -{ - vp9_ctx->cur.valid = true; - vp9_ctx->cur.reference_mode = dec_params->reference_mode; - vp9_ctx->cur.interpolation_filter = dec_params->interpolation_filter; - vp9_ctx->cur.flags = dec_params->flags; - vp9_ctx->cur.timestamp = buf->base.vb.vb2_buf.timestamp; -} - -static void config_output(struct hantro_ctx *ctx, - struct hantro_decoded_buffer *dst, - const struct v4l2_ctrl_vp9_frame *dec_params) -{ - dma_addr_t luma_addr, chroma_addr, mv_addr; - - hantro_reg_write(ctx->dev, &g2_out_dis, 0); - if (!ctx->dev->variant->legacy_regs) - hantro_reg_write(ctx->dev, &g2_output_format, 0); - - luma_addr = hantro_get_dec_buf_addr(ctx, &dst->base.vb.vb2_buf); - hantro_write_addr(ctx->dev, G2_OUT_LUMA_ADDR, luma_addr); - - chroma_addr = luma_addr + chroma_offset(ctx, dec_params); - hantro_write_addr(ctx->dev, G2_OUT_CHROMA_ADDR, chroma_addr); - - mv_addr = luma_addr + mv_offset(ctx, dec_params); - hantro_write_addr(ctx->dev, G2_OUT_MV_ADDR, mv_addr); -} - -struct hantro_vp9_ref_reg { - const struct hantro_reg width; - const struct hantro_reg height; - const struct hantro_reg hor_scale; - const struct hantro_reg ver_scale; - u32 y_base; - u32 c_base; -}; - -static void config_ref(struct hantro_ctx *ctx, - struct hantro_decoded_buffer *dst, - const struct hantro_vp9_ref_reg *ref_reg, - const struct v4l2_ctrl_vp9_frame *dec_params, - u64 ref_ts) -{ - struct hantro_decoded_buffer *buf; - dma_addr_t luma_addr, chroma_addr; - u32 refw, refh; - - buf = get_ref_buf(ctx, &dst->base.vb, ref_ts); - refw = buf->vp9.width; - refh = buf->vp9.height; - - hantro_reg_write(ctx->dev, &ref_reg->width, refw); - hantro_reg_write(ctx->dev, &ref_reg->height, refh); - - hantro_reg_write(ctx->dev, &ref_reg->hor_scale, (refw << 14) / dst->vp9.width); - hantro_reg_write(ctx->dev, &ref_reg->ver_scale, (refh << 14) / dst->vp9.height); - - luma_addr = hantro_get_dec_buf_addr(ctx, &buf->base.vb.vb2_buf); - hantro_write_addr(ctx->dev, ref_reg->y_base, luma_addr); - - chroma_addr = luma_addr + chroma_offset(ctx, dec_params); - hantro_write_addr(ctx->dev, ref_reg->c_base, chroma_addr); -} - -static void config_ref_registers(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp9_frame *dec_params, - struct hantro_decoded_buffer *dst, - struct hantro_decoded_buffer *mv_ref) -{ - static const struct hantro_vp9_ref_reg ref_regs[] = { - { - /* Last */ - .width = vp9_lref_width, - .height = vp9_lref_height, - .hor_scale = vp9_lref_hor_scale, - .ver_scale = vp9_lref_ver_scale, - .y_base = G2_REF_LUMA_ADDR(0), - .c_base = G2_REF_CHROMA_ADDR(0), - }, { - /* Golden */ - .width = vp9_gref_width, - .height = vp9_gref_height, - .hor_scale = vp9_gref_hor_scale, - .ver_scale = vp9_gref_ver_scale, - .y_base = G2_REF_LUMA_ADDR(4), - .c_base = G2_REF_CHROMA_ADDR(4), - }, { - /* Altref */ - .width = vp9_aref_width, - .height = vp9_aref_height, - .hor_scale = vp9_aref_hor_scale, - .ver_scale = vp9_aref_ver_scale, - .y_base = G2_REF_LUMA_ADDR(5), - .c_base = G2_REF_CHROMA_ADDR(5), - }, - }; - dma_addr_t mv_addr; - - config_ref(ctx, dst, &ref_regs[0], dec_params, dec_params->last_frame_ts); - config_ref(ctx, dst, &ref_regs[1], dec_params, dec_params->golden_frame_ts); - config_ref(ctx, dst, &ref_regs[2], dec_params, dec_params->alt_frame_ts); - - mv_addr = hantro_get_dec_buf_addr(ctx, &mv_ref->base.vb.vb2_buf) + - mv_offset(ctx, dec_params); - hantro_write_addr(ctx->dev, G2_REF_MV_ADDR(0), mv_addr); - - hantro_reg_write(ctx->dev, &vp9_last_sign_bias, - dec_params->ref_frame_sign_bias & V4L2_VP9_SIGN_BIAS_LAST ? 1 : 0); - - hantro_reg_write(ctx->dev, &vp9_gref_sign_bias, - dec_params->ref_frame_sign_bias & V4L2_VP9_SIGN_BIAS_GOLDEN ? 1 : 0); - - hantro_reg_write(ctx->dev, &vp9_aref_sign_bias, - dec_params->ref_frame_sign_bias & V4L2_VP9_SIGN_BIAS_ALT ? 1 : 0); -} - -static void recompute_tile_info(unsigned short *tile_info, unsigned int tiles, unsigned int sbs) -{ - int i; - unsigned int accumulated = 0; - unsigned int next_accumulated; - - for (i = 1; i <= tiles; ++i) { - next_accumulated = i * sbs / tiles; - *tile_info++ = next_accumulated - accumulated; - accumulated = next_accumulated; - } -} - -static void -recompute_tile_rc_info(struct hantro_ctx *ctx, - unsigned int tile_r, unsigned int tile_c, - unsigned int sbs_r, unsigned int sbs_c) -{ - struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec; - - recompute_tile_info(vp9_ctx->tile_r_info, tile_r, sbs_r); - recompute_tile_info(vp9_ctx->tile_c_info, tile_c, sbs_c); - - vp9_ctx->last_tile_r = tile_r; - vp9_ctx->last_tile_c = tile_c; - vp9_ctx->last_sbs_r = sbs_r; - vp9_ctx->last_sbs_c = sbs_c; -} - -static inline unsigned int first_tile_row(unsigned int tile_r, unsigned int sbs_r) -{ - if (tile_r == sbs_r + 1) - return 1; - - if (tile_r == sbs_r + 2) - return 2; - - return 0; -} - -static void -fill_tile_info(struct hantro_ctx *ctx, - unsigned int tile_r, unsigned int tile_c, - unsigned int sbs_r, unsigned int sbs_c, - unsigned short *tile_mem) -{ - struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec; - unsigned int i, j; - bool first = true; - - for (i = first_tile_row(tile_r, sbs_r); i < tile_r; ++i) { - unsigned short r_info = vp9_ctx->tile_r_info[i]; - - if (first) { - if (i > 0) - r_info += vp9_ctx->tile_r_info[0]; - if (i == 2) - r_info += vp9_ctx->tile_r_info[1]; - first = false; - } - for (j = 0; j < tile_c; ++j) { - *tile_mem++ = vp9_ctx->tile_c_info[j]; - *tile_mem++ = r_info; - } - } -} - -static void -config_tiles(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp9_frame *dec_params, - struct hantro_decoded_buffer *dst) -{ - struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec; - struct hantro_aux_buf *misc = &vp9_ctx->misc; - struct hantro_aux_buf *tile_edge = &vp9_ctx->tile_edge; - dma_addr_t addr; - unsigned short *tile_mem; - unsigned int rows, cols; - - addr = misc->dma + vp9_ctx->tile_info_offset; - hantro_write_addr(ctx->dev, G2_TILE_SIZES_ADDR, addr); - - tile_mem = misc->cpu + vp9_ctx->tile_info_offset; - if (dec_params->tile_cols_log2 || dec_params->tile_rows_log2) { - unsigned int tile_r = (1 << dec_params->tile_rows_log2); - unsigned int tile_c = (1 << dec_params->tile_cols_log2); - unsigned int sbs_r = hantro_vp9_num_sbs(dst->vp9.height); - unsigned int sbs_c = hantro_vp9_num_sbs(dst->vp9.width); - - if (tile_r != vp9_ctx->last_tile_r || tile_c != vp9_ctx->last_tile_c || - sbs_r != vp9_ctx->last_sbs_r || sbs_c != vp9_ctx->last_sbs_c) - recompute_tile_rc_info(ctx, tile_r, tile_c, sbs_r, sbs_c); - - fill_tile_info(ctx, tile_r, tile_c, sbs_r, sbs_c, tile_mem); - - cols = tile_c; - rows = tile_r; - hantro_reg_write(ctx->dev, &g2_tile_e, 1); - } else { - tile_mem[0] = hantro_vp9_num_sbs(dst->vp9.width); - tile_mem[1] = hantro_vp9_num_sbs(dst->vp9.height); - - cols = 1; - rows = 1; - hantro_reg_write(ctx->dev, &g2_tile_e, 0); - } - - if (ctx->dev->variant->legacy_regs) { - hantro_reg_write(ctx->dev, &g2_num_tile_cols_old, cols); - hantro_reg_write(ctx->dev, &g2_num_tile_rows_old, rows); - } else { - hantro_reg_write(ctx->dev, &g2_num_tile_cols, cols); - hantro_reg_write(ctx->dev, &g2_num_tile_rows, rows); - } - - /* provide aux buffers even if no tiles are used */ - addr = tile_edge->dma; - hantro_write_addr(ctx->dev, G2_TILE_FILTER_ADDR, addr); - - addr = tile_edge->dma + vp9_ctx->bsd_ctrl_offset; - hantro_write_addr(ctx->dev, G2_TILE_BSD_ADDR, addr); -} - -static void -update_feat_and_flag(struct hantro_vp9_dec_hw_ctx *vp9_ctx, - const struct v4l2_vp9_segmentation *seg, - unsigned int feature, - unsigned int segid) -{ - u8 mask = V4L2_VP9_SEGMENT_FEATURE_ENABLED(feature); - - vp9_ctx->feature_data[segid][feature] = seg->feature_data[segid][feature]; - vp9_ctx->feature_enabled[segid] &= ~mask; - vp9_ctx->feature_enabled[segid] |= (seg->feature_enabled[segid] & mask); -} - -static inline s16 clip3(s16 x, s16 y, s16 z) -{ - return (z < x) ? x : (z > y) ? y : z; -} - -static s16 feat_val_clip3(s16 feat_val, s16 feature_data, bool absolute, u8 clip) -{ - if (absolute) - return feature_data; - - return clip3(0, 255, feat_val + feature_data); -} - -static void config_segment(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame *dec_params) -{ - struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec; - const struct v4l2_vp9_segmentation *seg; - s16 feat_val; - unsigned char feat_id; - unsigned int segid; - bool segment_enabled, absolute, update_data; - - static const struct hantro_reg seg_regs[8][V4L2_VP9_SEG_LVL_MAX] = { - { vp9_quant_seg0, vp9_filt_level_seg0, vp9_refpic_seg0, vp9_skip_seg0 }, - { vp9_quant_seg1, vp9_filt_level_seg1, vp9_refpic_seg1, vp9_skip_seg1 }, - { vp9_quant_seg2, vp9_filt_level_seg2, vp9_refpic_seg2, vp9_skip_seg2 }, - { vp9_quant_seg3, vp9_filt_level_seg3, vp9_refpic_seg3, vp9_skip_seg3 }, - { vp9_quant_seg4, vp9_filt_level_seg4, vp9_refpic_seg4, vp9_skip_seg4 }, - { vp9_quant_seg5, vp9_filt_level_seg5, vp9_refpic_seg5, vp9_skip_seg5 }, - { vp9_quant_seg6, vp9_filt_level_seg6, vp9_refpic_seg6, vp9_skip_seg6 }, - { vp9_quant_seg7, vp9_filt_level_seg7, vp9_refpic_seg7, vp9_skip_seg7 }, - }; - - segment_enabled = !!(dec_params->seg.flags & V4L2_VP9_SEGMENTATION_FLAG_ENABLED); - hantro_reg_write(ctx->dev, &vp9_segment_e, segment_enabled); - hantro_reg_write(ctx->dev, &vp9_segment_upd_e, - !!(dec_params->seg.flags & V4L2_VP9_SEGMENTATION_FLAG_UPDATE_MAP)); - hantro_reg_write(ctx->dev, &vp9_segment_temp_upd_e, - !!(dec_params->seg.flags & V4L2_VP9_SEGMENTATION_FLAG_TEMPORAL_UPDATE)); - - seg = &dec_params->seg; - absolute = !!(seg->flags & V4L2_VP9_SEGMENTATION_FLAG_ABS_OR_DELTA_UPDATE); - update_data = !!(seg->flags & V4L2_VP9_SEGMENTATION_FLAG_UPDATE_DATA); - - for (segid = 0; segid < 8; ++segid) { - /* Quantizer segment feature */ - feat_id = V4L2_VP9_SEG_LVL_ALT_Q; - feat_val = dec_params->quant.base_q_idx; - if (segment_enabled) { - if (update_data) - update_feat_and_flag(vp9_ctx, seg, feat_id, segid); - if (v4l2_vp9_seg_feat_enabled(vp9_ctx->feature_enabled, feat_id, segid)) - feat_val = feat_val_clip3(feat_val, - vp9_ctx->feature_data[segid][feat_id], - absolute, 255); - } - hantro_reg_write(ctx->dev, &seg_regs[segid][feat_id], feat_val); - - /* Loop filter segment feature */ - feat_id = V4L2_VP9_SEG_LVL_ALT_L; - feat_val = dec_params->lf.level; - if (segment_enabled) { - if (update_data) - update_feat_and_flag(vp9_ctx, seg, feat_id, segid); - if (v4l2_vp9_seg_feat_enabled(vp9_ctx->feature_enabled, feat_id, segid)) - feat_val = feat_val_clip3(feat_val, - vp9_ctx->feature_data[segid][feat_id], - absolute, 63); - } - hantro_reg_write(ctx->dev, &seg_regs[segid][feat_id], feat_val); - - /* Reference frame segment feature */ - feat_id = V4L2_VP9_SEG_LVL_REF_FRAME; - feat_val = 0; - if (segment_enabled) { - if (update_data) - update_feat_and_flag(vp9_ctx, seg, feat_id, segid); - if (!(dec_params->flags & V4L2_VP9_FRAME_FLAG_KEY_FRAME) && - v4l2_vp9_seg_feat_enabled(vp9_ctx->feature_enabled, feat_id, segid)) - feat_val = vp9_ctx->feature_data[segid][feat_id] + 1; - } - hantro_reg_write(ctx->dev, &seg_regs[segid][feat_id], feat_val); - - /* Skip segment feature */ - feat_id = V4L2_VP9_SEG_LVL_SKIP; - feat_val = 0; - if (segment_enabled) { - if (update_data) - update_feat_and_flag(vp9_ctx, seg, feat_id, segid); - feat_val = v4l2_vp9_seg_feat_enabled(vp9_ctx->feature_enabled, - feat_id, segid) ? 1 : 0; - } - hantro_reg_write(ctx->dev, &seg_regs[segid][feat_id], feat_val); - } -} - -static void config_loop_filter(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame *dec_params) -{ - bool d = dec_params->lf.flags & V4L2_VP9_LOOP_FILTER_FLAG_DELTA_ENABLED; - - hantro_reg_write(ctx->dev, &vp9_filt_level, dec_params->lf.level); - hantro_reg_write(ctx->dev, &g2_out_filtering_dis, dec_params->lf.level == 0); - hantro_reg_write(ctx->dev, &vp9_filt_sharpness, dec_params->lf.sharpness); - - hantro_reg_write(ctx->dev, &vp9_filt_ref_adj_0, d ? dec_params->lf.ref_deltas[0] : 0); - hantro_reg_write(ctx->dev, &vp9_filt_ref_adj_1, d ? dec_params->lf.ref_deltas[1] : 0); - hantro_reg_write(ctx->dev, &vp9_filt_ref_adj_2, d ? dec_params->lf.ref_deltas[2] : 0); - hantro_reg_write(ctx->dev, &vp9_filt_ref_adj_3, d ? dec_params->lf.ref_deltas[3] : 0); - hantro_reg_write(ctx->dev, &vp9_filt_mb_adj_0, d ? dec_params->lf.mode_deltas[0] : 0); - hantro_reg_write(ctx->dev, &vp9_filt_mb_adj_1, d ? dec_params->lf.mode_deltas[1] : 0); -} - -static void config_picture_dimensions(struct hantro_ctx *ctx, struct hantro_decoded_buffer *dst) -{ - u32 pic_w_4x4, pic_h_4x4; - - hantro_reg_write(ctx->dev, &g2_pic_width_in_cbs, (dst->vp9.width + 7) / 8); - hantro_reg_write(ctx->dev, &g2_pic_height_in_cbs, (dst->vp9.height + 7) / 8); - pic_w_4x4 = roundup(dst->vp9.width, 8) >> 2; - pic_h_4x4 = roundup(dst->vp9.height, 8) >> 2; - hantro_reg_write(ctx->dev, &g2_pic_width_4x4, pic_w_4x4); - hantro_reg_write(ctx->dev, &g2_pic_height_4x4, pic_h_4x4); -} - -static void -config_bit_depth(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame *dec_params) -{ - if (ctx->dev->variant->legacy_regs) { - hantro_reg_write(ctx->dev, &g2_bit_depth_y, dec_params->bit_depth); - hantro_reg_write(ctx->dev, &g2_bit_depth_c, dec_params->bit_depth); - hantro_reg_write(ctx->dev, &g2_pix_shift, 0); - } else { - hantro_reg_write(ctx->dev, &g2_bit_depth_y_minus8, dec_params->bit_depth - 8); - hantro_reg_write(ctx->dev, &g2_bit_depth_c_minus8, dec_params->bit_depth - 8); - } -} - -static inline bool is_lossless(const struct v4l2_vp9_quantization *quant) -{ - return quant->base_q_idx == 0 && quant->delta_q_uv_ac == 0 && - quant->delta_q_uv_dc == 0 && quant->delta_q_y_dc == 0; -} - -static void -config_quant(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame *dec_params) -{ - hantro_reg_write(ctx->dev, &vp9_qp_delta_y_dc, dec_params->quant.delta_q_y_dc); - hantro_reg_write(ctx->dev, &vp9_qp_delta_ch_dc, dec_params->quant.delta_q_uv_dc); - hantro_reg_write(ctx->dev, &vp9_qp_delta_ch_ac, dec_params->quant.delta_q_uv_ac); - hantro_reg_write(ctx->dev, &vp9_lossless_e, is_lossless(&dec_params->quant)); -} - -static u32 -hantro_interp_filter_from_v4l2(unsigned int interpolation_filter) -{ - switch (interpolation_filter) { - case V4L2_VP9_INTERP_FILTER_EIGHTTAP: - return 0x1; - case V4L2_VP9_INTERP_FILTER_EIGHTTAP_SMOOTH: - return 0; - case V4L2_VP9_INTERP_FILTER_EIGHTTAP_SHARP: - return 0x2; - case V4L2_VP9_INTERP_FILTER_BILINEAR: - return 0x3; - case V4L2_VP9_INTERP_FILTER_SWITCHABLE: - return 0x4; - } - - return 0; -} - -static void -config_others(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame *dec_params, - bool intra_only, bool resolution_change) -{ - struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec; - - hantro_reg_write(ctx->dev, &g2_idr_pic_e, intra_only); - - hantro_reg_write(ctx->dev, &vp9_transform_mode, vp9_ctx->cur.tx_mode); - - hantro_reg_write(ctx->dev, &vp9_mcomp_filt_type, intra_only ? - 0 : hantro_interp_filter_from_v4l2(dec_params->interpolation_filter)); - - hantro_reg_write(ctx->dev, &vp9_high_prec_mv_e, - !!(dec_params->flags & V4L2_VP9_FRAME_FLAG_ALLOW_HIGH_PREC_MV)); - - hantro_reg_write(ctx->dev, &vp9_comp_pred_mode, dec_params->reference_mode); - - hantro_reg_write(ctx->dev, &g2_tempor_mvp_e, - !(dec_params->flags & V4L2_VP9_FRAME_FLAG_ERROR_RESILIENT) && - !(dec_params->flags & V4L2_VP9_FRAME_FLAG_KEY_FRAME) && - !(vp9_ctx->last.flags & V4L2_VP9_FRAME_FLAG_KEY_FRAME) && - !(dec_params->flags & V4L2_VP9_FRAME_FLAG_INTRA_ONLY) && - !resolution_change && - vp9_ctx->last.flags & V4L2_VP9_FRAME_FLAG_SHOW_FRAME - ); - - hantro_reg_write(ctx->dev, &g2_write_mvs_e, - !(dec_params->flags & V4L2_VP9_FRAME_FLAG_KEY_FRAME)); -} - -static void -config_compound_reference(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp9_frame *dec_params) -{ - u32 comp_fixed_ref, comp_var_ref[2]; - bool last_ref_frame_sign_bias; - bool golden_ref_frame_sign_bias; - bool alt_ref_frame_sign_bias; - bool comp_ref_allowed = 0; - - comp_fixed_ref = 0; - comp_var_ref[0] = 0; - comp_var_ref[1] = 0; - - last_ref_frame_sign_bias = dec_params->ref_frame_sign_bias & V4L2_VP9_SIGN_BIAS_LAST; - golden_ref_frame_sign_bias = dec_params->ref_frame_sign_bias & V4L2_VP9_SIGN_BIAS_GOLDEN; - alt_ref_frame_sign_bias = dec_params->ref_frame_sign_bias & V4L2_VP9_SIGN_BIAS_ALT; - - /* 6.3.12 Frame reference mode syntax */ - comp_ref_allowed |= golden_ref_frame_sign_bias != last_ref_frame_sign_bias; - comp_ref_allowed |= alt_ref_frame_sign_bias != last_ref_frame_sign_bias; - - if (comp_ref_allowed) { - if (last_ref_frame_sign_bias == - golden_ref_frame_sign_bias) { - comp_fixed_ref = ALTREF_FRAME; - comp_var_ref[0] = LAST_FRAME; - comp_var_ref[1] = GOLDEN_FRAME; - } else if (last_ref_frame_sign_bias == - alt_ref_frame_sign_bias) { - comp_fixed_ref = GOLDEN_FRAME; - comp_var_ref[0] = LAST_FRAME; - comp_var_ref[1] = ALTREF_FRAME; - } else { - comp_fixed_ref = LAST_FRAME; - comp_var_ref[0] = GOLDEN_FRAME; - comp_var_ref[1] = ALTREF_FRAME; - } - } - - hantro_reg_write(ctx->dev, &vp9_comp_pred_fixed_ref, comp_fixed_ref); - hantro_reg_write(ctx->dev, &vp9_comp_pred_var_ref0, comp_var_ref[0]); - hantro_reg_write(ctx->dev, &vp9_comp_pred_var_ref1, comp_var_ref[1]); -} - -#define INNER_LOOP \ -do { \ - for (m = 0; m < ARRAY_SIZE(adaptive->coef[0][0][0][0]); ++m) { \ - memcpy(adaptive->coef[i][j][k][l][m], \ - probs->coef[i][j][k][l][m], \ - sizeof(probs->coef[i][j][k][l][m])); \ - \ - adaptive->coef[i][j][k][l][m][3] = 0; \ - } \ -} while (0) - -static void config_probs(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame *dec_params) -{ - struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec; - struct hantro_aux_buf *misc = &vp9_ctx->misc; - struct hantro_g2_all_probs *all_probs = misc->cpu; - struct hantro_g2_probs *adaptive; - struct hantro_g2_mv_probs *mv; - const struct v4l2_vp9_segmentation *seg = &dec_params->seg; - const struct v4l2_vp9_frame_context *probs = &vp9_ctx->probability_tables; - int i, j, k, l, m; - - for (i = 0; i < ARRAY_SIZE(all_probs->kf_y_mode_prob); ++i) - for (j = 0; j < ARRAY_SIZE(all_probs->kf_y_mode_prob[0]); ++j) { - memcpy(all_probs->kf_y_mode_prob[i][j], - v4l2_vp9_kf_y_mode_prob[i][j], - ARRAY_SIZE(all_probs->kf_y_mode_prob[i][j])); - - all_probs->kf_y_mode_prob_tail[i][j][0] = - v4l2_vp9_kf_y_mode_prob[i][j][8]; - } - - memcpy(all_probs->mb_segment_tree_probs, seg->tree_probs, - sizeof(all_probs->mb_segment_tree_probs)); - - memcpy(all_probs->segment_pred_probs, seg->pred_probs, - sizeof(all_probs->segment_pred_probs)); - - for (i = 0; i < ARRAY_SIZE(all_probs->kf_uv_mode_prob); ++i) { - memcpy(all_probs->kf_uv_mode_prob[i], v4l2_vp9_kf_uv_mode_prob[i], - ARRAY_SIZE(all_probs->kf_uv_mode_prob[i])); - - all_probs->kf_uv_mode_prob_tail[i][0] = v4l2_vp9_kf_uv_mode_prob[i][8]; - } - - adaptive = &all_probs->probs; - - for (i = 0; i < ARRAY_SIZE(adaptive->inter_mode); ++i) { - memcpy(adaptive->inter_mode[i], probs->inter_mode[i], - ARRAY_SIZE(probs->inter_mode[i])); - - adaptive->inter_mode[i][3] = 0; - } - - memcpy(adaptive->is_inter, probs->is_inter, sizeof(adaptive->is_inter)); - - for (i = 0; i < ARRAY_SIZE(adaptive->uv_mode); ++i) { - memcpy(adaptive->uv_mode[i], probs->uv_mode[i], - sizeof(adaptive->uv_mode[i])); - adaptive->uv_mode_tail[i][0] = probs->uv_mode[i][8]; - } - - memcpy(adaptive->tx8, probs->tx8, sizeof(adaptive->tx8)); - memcpy(adaptive->tx16, probs->tx16, sizeof(adaptive->tx16)); - memcpy(adaptive->tx32, probs->tx32, sizeof(adaptive->tx32)); - - for (i = 0; i < ARRAY_SIZE(adaptive->y_mode); ++i) { - memcpy(adaptive->y_mode[i], probs->y_mode[i], - ARRAY_SIZE(adaptive->y_mode[i])); - - adaptive->y_mode_tail[i][0] = probs->y_mode[i][8]; - } - - for (i = 0; i < ARRAY_SIZE(adaptive->partition[0]); ++i) { - memcpy(adaptive->partition[0][i], v4l2_vp9_kf_partition_probs[i], - sizeof(v4l2_vp9_kf_partition_probs[i])); - - adaptive->partition[0][i][3] = 0; - } - - for (i = 0; i < ARRAY_SIZE(adaptive->partition[1]); ++i) { - memcpy(adaptive->partition[1][i], probs->partition[i], - sizeof(probs->partition[i])); - - adaptive->partition[1][i][3] = 0; - } - - memcpy(adaptive->interp_filter, probs->interp_filter, - sizeof(adaptive->interp_filter)); - - memcpy(adaptive->comp_mode, probs->comp_mode, sizeof(adaptive->comp_mode)); - - memcpy(adaptive->skip, probs->skip, sizeof(adaptive->skip)); - - mv = &adaptive->mv; - - memcpy(mv->joint, probs->mv.joint, sizeof(mv->joint)); - memcpy(mv->sign, probs->mv.sign, sizeof(mv->sign)); - memcpy(mv->class0_bit, probs->mv.class0_bit, sizeof(mv->class0_bit)); - memcpy(mv->fr, probs->mv.fr, sizeof(mv->fr)); - memcpy(mv->class0_hp, probs->mv.class0_hp, sizeof(mv->class0_hp)); - memcpy(mv->hp, probs->mv.hp, sizeof(mv->hp)); - memcpy(mv->classes, probs->mv.classes, sizeof(mv->classes)); - memcpy(mv->class0_fr, probs->mv.class0_fr, sizeof(mv->class0_fr)); - memcpy(mv->bits, probs->mv.bits, sizeof(mv->bits)); - - memcpy(adaptive->single_ref, probs->single_ref, sizeof(adaptive->single_ref)); - - memcpy(adaptive->comp_ref, probs->comp_ref, sizeof(adaptive->comp_ref)); - - for (i = 0; i < ARRAY_SIZE(adaptive->coef); ++i) - for (j = 0; j < ARRAY_SIZE(adaptive->coef[0]); ++j) - for (k = 0; k < ARRAY_SIZE(adaptive->coef[0][0]); ++k) - for (l = 0; l < ARRAY_SIZE(adaptive->coef[0][0][0]); ++l) - INNER_LOOP; - - hantro_write_addr(ctx->dev, G2_VP9_PROBS_ADDR, misc->dma); -} - -static void config_counts(struct hantro_ctx *ctx) -{ - struct hantro_vp9_dec_hw_ctx *vp9_dec = &ctx->vp9_dec; - struct hantro_aux_buf *misc = &vp9_dec->misc; - dma_addr_t addr = misc->dma + vp9_dec->ctx_counters_offset; - - hantro_write_addr(ctx->dev, G2_VP9_CTX_COUNT_ADDR, addr); -} - -static void config_seg_map(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp9_frame *dec_params, - bool intra_only, bool update_map) -{ - struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec; - struct hantro_aux_buf *segment_map = &vp9_ctx->segment_map; - dma_addr_t addr; - - if (intra_only || - (dec_params->flags & V4L2_VP9_FRAME_FLAG_ERROR_RESILIENT)) { - memset(segment_map->cpu, 0, segment_map->size); - memset(vp9_ctx->feature_data, 0, sizeof(vp9_ctx->feature_data)); - memset(vp9_ctx->feature_enabled, 0, sizeof(vp9_ctx->feature_enabled)); - } - - addr = segment_map->dma + vp9_ctx->active_segment * vp9_ctx->segment_map_size; - hantro_write_addr(ctx->dev, G2_VP9_SEGMENT_READ_ADDR, addr); - - addr = segment_map->dma + (1 - vp9_ctx->active_segment) * vp9_ctx->segment_map_size; - hantro_write_addr(ctx->dev, G2_VP9_SEGMENT_WRITE_ADDR, addr); - - if (update_map) - vp9_ctx->active_segment = 1 - vp9_ctx->active_segment; -} - -static void -config_source(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame *dec_params, - struct vb2_v4l2_buffer *vb2_src) -{ - dma_addr_t stream_base, tmp_addr; - unsigned int headres_size; - u32 src_len, start_bit, src_buf_len; - - headres_size = dec_params->uncompressed_header_size - + dec_params->compressed_header_size; - - stream_base = vb2_dma_contig_plane_dma_addr(&vb2_src->vb2_buf, 0); - - tmp_addr = stream_base + headres_size; - if (ctx->dev->variant->legacy_regs) - hantro_write_addr(ctx->dev, G2_STREAM_ADDR, (tmp_addr & ~0xf)); - else - hantro_write_addr(ctx->dev, G2_STREAM_ADDR, stream_base); - - start_bit = (tmp_addr & 0xf) * 8; - hantro_reg_write(ctx->dev, &g2_start_bit, start_bit); - - src_len = vb2_get_plane_payload(&vb2_src->vb2_buf, 0); - src_len += start_bit / 8 - headres_size; - hantro_reg_write(ctx->dev, &g2_stream_len, src_len); - - if (!ctx->dev->variant->legacy_regs) { - tmp_addr &= ~0xf; - hantro_reg_write(ctx->dev, &g2_strm_start_offset, tmp_addr - stream_base); - src_buf_len = vb2_plane_size(&vb2_src->vb2_buf, 0); - hantro_reg_write(ctx->dev, &g2_strm_buffer_len, src_buf_len); - } -} - -static void -config_registers(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame *dec_params, - struct vb2_v4l2_buffer *vb2_src, struct vb2_v4l2_buffer *vb2_dst) -{ - struct hantro_decoded_buffer *dst, *last, *mv_ref; - struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec; - const struct v4l2_vp9_segmentation *seg; - bool intra_only, resolution_change; - - /* vp9 stuff */ - dst = vb2_to_hantro_decoded_buf(&vb2_dst->vb2_buf); - - if (vp9_ctx->last.valid) - last = get_ref_buf(ctx, &dst->base.vb, vp9_ctx->last.timestamp); - else - last = dst; - - update_dec_buf_info(dst, dec_params); - update_ctx_cur_info(vp9_ctx, dst, dec_params); - seg = &dec_params->seg; - - intra_only = !!(dec_params->flags & - (V4L2_VP9_FRAME_FLAG_KEY_FRAME | - V4L2_VP9_FRAME_FLAG_INTRA_ONLY)); - - if (!intra_only && - !(dec_params->flags & V4L2_VP9_FRAME_FLAG_ERROR_RESILIENT) && - vp9_ctx->last.valid) - mv_ref = last; - else - mv_ref = dst; - - resolution_change = dst->vp9.width != last->vp9.width || - dst->vp9.height != last->vp9.height; - - /* configure basic registers */ - hantro_reg_write(ctx->dev, &g2_mode, VP9_DEC_MODE); - if (!ctx->dev->variant->legacy_regs) { - hantro_reg_write(ctx->dev, &g2_strm_swap, 0xf); - hantro_reg_write(ctx->dev, &g2_dirmv_swap, 0xf); - hantro_reg_write(ctx->dev, &g2_compress_swap, 0xf); - hantro_reg_write(ctx->dev, &g2_ref_compress_bypass, 1); - } else { - hantro_reg_write(ctx->dev, &g2_strm_swap_old, 0x1f); - hantro_reg_write(ctx->dev, &g2_pic_swap, 0x10); - hantro_reg_write(ctx->dev, &g2_dirmv_swap_old, 0x10); - hantro_reg_write(ctx->dev, &g2_tab0_swap_old, 0x10); - hantro_reg_write(ctx->dev, &g2_tab1_swap_old, 0x10); - hantro_reg_write(ctx->dev, &g2_tab2_swap_old, 0x10); - hantro_reg_write(ctx->dev, &g2_tab3_swap_old, 0x10); - hantro_reg_write(ctx->dev, &g2_rscan_swap, 0x10); - } - hantro_reg_write(ctx->dev, &g2_buswidth, BUS_WIDTH_128); - hantro_reg_write(ctx->dev, &g2_max_burst, 16); - hantro_reg_write(ctx->dev, &g2_apf_threshold, 8); - hantro_reg_write(ctx->dev, &g2_clk_gate_e, 1); - hantro_reg_write(ctx->dev, &g2_max_cb_size, 6); - hantro_reg_write(ctx->dev, &g2_min_cb_size, 3); - if (ctx->dev->variant->double_buffer) - hantro_reg_write(ctx->dev, &g2_double_buffer_e, 1); - - config_output(ctx, dst, dec_params); - - if (!intra_only) - config_ref_registers(ctx, dec_params, dst, mv_ref); - - config_tiles(ctx, dec_params, dst); - config_segment(ctx, dec_params); - config_loop_filter(ctx, dec_params); - config_picture_dimensions(ctx, dst); - config_bit_depth(ctx, dec_params); - config_quant(ctx, dec_params); - config_others(ctx, dec_params, intra_only, resolution_change); - config_compound_reference(ctx, dec_params); - config_probs(ctx, dec_params); - config_counts(ctx); - config_seg_map(ctx, dec_params, intra_only, - seg->flags & V4L2_VP9_SEGMENTATION_FLAG_UPDATE_MAP); - config_source(ctx, dec_params, vb2_src); -} - -int hantro_g2_vp9_dec_run(struct hantro_ctx *ctx) -{ - const struct v4l2_ctrl_vp9_frame *decode_params; - struct vb2_v4l2_buffer *src; - struct vb2_v4l2_buffer *dst; - int ret; - - hantro_g2_check_idle(ctx->dev); - - ret = start_prepare_run(ctx, &decode_params); - if (ret) { - hantro_end_prepare_run(ctx); - return ret; - } - - src = hantro_get_src_buf(ctx); - dst = hantro_get_dst_buf(ctx); - - config_registers(ctx, decode_params, src, dst); - - hantro_end_prepare_run(ctx); - - vdpu_write(ctx->dev, G2_REG_INTERRUPT_DEC_E, G2_REG_INTERRUPT); - - return 0; -} - -#define copy_tx_and_skip(p1, p2) \ -do { \ - memcpy((p1)->tx8, (p2)->tx8, sizeof((p1)->tx8)); \ - memcpy((p1)->tx16, (p2)->tx16, sizeof((p1)->tx16)); \ - memcpy((p1)->tx32, (p2)->tx32, sizeof((p1)->tx32)); \ - memcpy((p1)->skip, (p2)->skip, sizeof((p1)->skip)); \ -} while (0) - -void hantro_g2_vp9_dec_done(struct hantro_ctx *ctx) -{ - struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec; - unsigned int fctx_idx; - - if (!(vp9_ctx->cur.flags & V4L2_VP9_FRAME_FLAG_REFRESH_FRAME_CTX)) - goto out_update_last; - - fctx_idx = vp9_ctx->cur.frame_context_idx; - - if (!(vp9_ctx->cur.flags & V4L2_VP9_FRAME_FLAG_PARALLEL_DEC_MODE)) { - /* error_resilient_mode == 0 && frame_parallel_decoding_mode == 0 */ - struct v4l2_vp9_frame_context *probs = &vp9_ctx->probability_tables; - bool frame_is_intra = vp9_ctx->cur.flags & - (V4L2_VP9_FRAME_FLAG_KEY_FRAME | V4L2_VP9_FRAME_FLAG_INTRA_ONLY); - struct tx_and_skip { - u8 tx8[2][1]; - u8 tx16[2][2]; - u8 tx32[2][3]; - u8 skip[3]; - } _tx_skip, *tx_skip = &_tx_skip; - struct v4l2_vp9_frame_symbol_counts *counts; - struct symbol_counts *hantro_cnts; - u32 tx16p[2][4]; - int i; - - /* buffer the forward-updated TX and skip probs */ - if (frame_is_intra) - copy_tx_and_skip(tx_skip, probs); - - /* 6.1.2 refresh_probs(): load_probs() and load_probs2() */ - *probs = vp9_ctx->frame_context[fctx_idx]; - - /* if FrameIsIntra then undo the effect of load_probs2() */ - if (frame_is_intra) - copy_tx_and_skip(probs, tx_skip); - - counts = &vp9_ctx->cnts; - hantro_cnts = vp9_ctx->misc.cpu + vp9_ctx->ctx_counters_offset; - for (i = 0; i < ARRAY_SIZE(tx16p); ++i) { - memcpy(tx16p[i], - hantro_cnts->tx16x16_count[i], - sizeof(hantro_cnts->tx16x16_count[0])); - tx16p[i][3] = 0; - } - counts->tx16p = &tx16p; - - v4l2_vp9_adapt_coef_probs(probs, counts, - !vp9_ctx->last.valid || - vp9_ctx->last.flags & V4L2_VP9_FRAME_FLAG_KEY_FRAME, - frame_is_intra); - - if (!frame_is_intra) { - /* load_probs2() already done */ - u32 mv_mode[7][4]; - - for (i = 0; i < ARRAY_SIZE(mv_mode); ++i) { - mv_mode[i][0] = hantro_cnts->inter_mode_counts[i][1][0]; - mv_mode[i][1] = hantro_cnts->inter_mode_counts[i][2][0]; - mv_mode[i][2] = hantro_cnts->inter_mode_counts[i][0][0]; - mv_mode[i][3] = hantro_cnts->inter_mode_counts[i][2][1]; - } - counts->mv_mode = &mv_mode; - v4l2_vp9_adapt_noncoef_probs(&vp9_ctx->probability_tables, counts, - vp9_ctx->cur.reference_mode, - vp9_ctx->cur.interpolation_filter, - vp9_ctx->cur.tx_mode, vp9_ctx->cur.flags); - } - } - - vp9_ctx->frame_context[fctx_idx] = vp9_ctx->probability_tables; - -out_update_last: - vp9_ctx->last = vp9_ctx->cur; -} diff --git a/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c b/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c deleted file mode 100644 index 12d69503d6ba..000000000000 --- a/drivers/staging/media/hantro/hantro_h1_jpeg_enc.c +++ /dev/null @@ -1,166 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VPU codec driver - * - * Copyright (C) 2018 Rockchip Electronics Co., Ltd. - */ - -#include <asm/unaligned.h> -#include <media/v4l2-mem2mem.h> -#include "hantro_jpeg.h" -#include "hantro.h" -#include "hantro_v4l2.h" -#include "hantro_hw.h" -#include "hantro_h1_regs.h" - -#define H1_JPEG_QUANT_TABLE_COUNT 16 - -static void hantro_h1_set_src_img_ctrl(struct hantro_dev *vpu, - struct hantro_ctx *ctx) -{ - u32 overfill_r, overfill_b; - u32 reg; - - /* - * The format width and height are already macroblock aligned - * by .vidioc_s_fmt_vid_cap_mplane() callback. Destination - * format width and height can be further modified by - * .vidioc_s_selection(), and the width is 4-aligned. - */ - overfill_r = ctx->src_fmt.width - ctx->dst_fmt.width; - overfill_b = ctx->src_fmt.height - ctx->dst_fmt.height; - - reg = H1_REG_IN_IMG_CTRL_ROW_LEN(ctx->src_fmt.width) - | H1_REG_IN_IMG_CTRL_OVRFLR_D4(overfill_r / 4) - | H1_REG_IN_IMG_CTRL_OVRFLB(overfill_b) - | H1_REG_IN_IMG_CTRL_FMT(ctx->vpu_src_fmt->enc_fmt); - vepu_write_relaxed(vpu, reg, H1_REG_IN_IMG_CTRL); -} - -static void hantro_h1_jpeg_enc_set_buffers(struct hantro_dev *vpu, - struct hantro_ctx *ctx, - struct vb2_buffer *src_buf, - struct vb2_buffer *dst_buf) -{ - struct v4l2_pix_format_mplane *pix_fmt = &ctx->src_fmt; - dma_addr_t src[3]; - u32 size_left; - - size_left = vb2_plane_size(dst_buf, 0) - ctx->vpu_dst_fmt->header_size; - if (WARN_ON(vb2_plane_size(dst_buf, 0) < ctx->vpu_dst_fmt->header_size)) - size_left = 0; - - WARN_ON(pix_fmt->num_planes > 3); - - vepu_write_relaxed(vpu, vb2_dma_contig_plane_dma_addr(dst_buf, 0) + - ctx->vpu_dst_fmt->header_size, - H1_REG_ADDR_OUTPUT_STREAM); - vepu_write_relaxed(vpu, size_left, H1_REG_STR_BUF_LIMIT); - - if (pix_fmt->num_planes == 1) { - src[0] = vb2_dma_contig_plane_dma_addr(src_buf, 0); - /* single plane formats we supported are all interlaced */ - vepu_write_relaxed(vpu, src[0], H1_REG_ADDR_IN_PLANE_0); - } else if (pix_fmt->num_planes == 2) { - src[0] = vb2_dma_contig_plane_dma_addr(src_buf, 0); - src[1] = vb2_dma_contig_plane_dma_addr(src_buf, 1); - vepu_write_relaxed(vpu, src[0], H1_REG_ADDR_IN_PLANE_0); - vepu_write_relaxed(vpu, src[1], H1_REG_ADDR_IN_PLANE_1); - } else { - src[0] = vb2_dma_contig_plane_dma_addr(src_buf, 0); - src[1] = vb2_dma_contig_plane_dma_addr(src_buf, 1); - src[2] = vb2_dma_contig_plane_dma_addr(src_buf, 2); - vepu_write_relaxed(vpu, src[0], H1_REG_ADDR_IN_PLANE_0); - vepu_write_relaxed(vpu, src[1], H1_REG_ADDR_IN_PLANE_1); - vepu_write_relaxed(vpu, src[2], H1_REG_ADDR_IN_PLANE_2); - } -} - -static void -hantro_h1_jpeg_enc_set_qtable(struct hantro_dev *vpu, - unsigned char *luma_qtable, - 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_p[i]); - vepu_write_relaxed(vpu, reg, H1_REG_JPEG_LUMA_QUAT(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)); - } -} - -int hantro_h1_jpeg_enc_run(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - struct vb2_v4l2_buffer *src_buf, *dst_buf; - struct hantro_jpeg_ctx jpeg_ctx; - u32 reg; - - src_buf = hantro_get_src_buf(ctx); - dst_buf = hantro_get_dst_buf(ctx); - - hantro_start_prepare_run(ctx); - - memset(&jpeg_ctx, 0, sizeof(jpeg_ctx)); - jpeg_ctx.buffer = vb2_plane_vaddr(&dst_buf->vb2_buf, 0); - jpeg_ctx.width = ctx->dst_fmt.width; - jpeg_ctx.height = ctx->dst_fmt.height; - jpeg_ctx.quality = ctx->jpeg_quality; - hantro_jpeg_header_assemble(&jpeg_ctx); - - /* Switch to JPEG encoder mode before writing registers */ - vepu_write_relaxed(vpu, H1_REG_ENC_CTRL_ENC_MODE_JPEG, - H1_REG_ENC_CTRL); - - hantro_h1_set_src_img_ctrl(vpu, ctx); - hantro_h1_jpeg_enc_set_buffers(vpu, ctx, &src_buf->vb2_buf, - &dst_buf->vb2_buf); - hantro_h1_jpeg_enc_set_qtable(vpu, jpeg_ctx.hw_luma_qtable, - jpeg_ctx.hw_chroma_qtable); - - reg = H1_REG_AXI_CTRL_OUTPUT_SWAP16 - | H1_REG_AXI_CTRL_INPUT_SWAP16 - | H1_REG_AXI_CTRL_BURST_LEN(16) - | H1_REG_AXI_CTRL_OUTPUT_SWAP32 - | H1_REG_AXI_CTRL_INPUT_SWAP32 - | H1_REG_AXI_CTRL_OUTPUT_SWAP8 - | H1_REG_AXI_CTRL_INPUT_SWAP8; - /* Make sure that all registers are written at this point. */ - vepu_write(vpu, reg, H1_REG_AXI_CTRL); - - reg = H1_REG_ENC_CTRL_WIDTH(MB_WIDTH(ctx->src_fmt.width)) - | H1_REG_ENC_CTRL_HEIGHT(MB_HEIGHT(ctx->src_fmt.height)) - | H1_REG_ENC_CTRL_ENC_MODE_JPEG - | H1_REG_ENC_PIC_INTRA - | H1_REG_ENC_CTRL_EN_BIT; - - hantro_end_prepare_run(ctx); - - vepu_write(vpu, reg, H1_REG_ENC_CTRL); - - return 0; -} - -void hantro_h1_jpeg_enc_done(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - u32 bytesused = vepu_read(vpu, H1_REG_STR_BUF_LIMIT) / 8; - struct vb2_v4l2_buffer *dst_buf = hantro_get_dst_buf(ctx); - - vb2_set_plane_payload(&dst_buf->vb2_buf, 0, - ctx->vpu_dst_fmt->header_size + bytesused); -} diff --git a/drivers/staging/media/hantro/hantro_h1_regs.h b/drivers/staging/media/hantro/hantro_h1_regs.h deleted file mode 100644 index 30e7e7b920b5..000000000000 --- a/drivers/staging/media/hantro/hantro_h1_regs.h +++ /dev/null @@ -1,154 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Hantro VPU codec driver - * - * Copyright 2018 Google LLC. - * Tomasz Figa <tfiga@chromium.org> - */ - -#ifndef HANTRO_H1_REGS_H_ -#define HANTRO_H1_REGS_H_ - -/* Encoder registers. */ -#define H1_REG_INTERRUPT 0x004 -#define H1_REG_INTERRUPT_FRAME_RDY BIT(2) -#define H1_REG_INTERRUPT_DIS_BIT BIT(1) -#define H1_REG_INTERRUPT_BIT BIT(0) -#define H1_REG_AXI_CTRL 0x008 -#define H1_REG_AXI_CTRL_OUTPUT_SWAP16 BIT(15) -#define H1_REG_AXI_CTRL_INPUT_SWAP16 BIT(14) -#define H1_REG_AXI_CTRL_BURST_LEN(x) ((x) << 8) -#define H1_REG_AXI_CTRL_GATE_BIT BIT(4) -#define H1_REG_AXI_CTRL_OUTPUT_SWAP32 BIT(3) -#define H1_REG_AXI_CTRL_INPUT_SWAP32 BIT(2) -#define H1_REG_AXI_CTRL_OUTPUT_SWAP8 BIT(1) -#define H1_REG_AXI_CTRL_INPUT_SWAP8 BIT(0) -#define H1_REG_ADDR_OUTPUT_STREAM 0x014 -#define H1_REG_ADDR_OUTPUT_CTRL 0x018 -#define H1_REG_ADDR_REF_LUMA 0x01c -#define H1_REG_ADDR_REF_CHROMA 0x020 -#define H1_REG_ADDR_REC_LUMA 0x024 -#define H1_REG_ADDR_REC_CHROMA 0x028 -#define H1_REG_ADDR_IN_PLANE_0 0x02c -#define H1_REG_ADDR_IN_PLANE_1 0x030 -#define H1_REG_ADDR_IN_PLANE_2 0x034 -#define H1_REG_ENC_CTRL 0x038 -#define H1_REG_ENC_CTRL_TIMEOUT_EN BIT(31) -#define H1_REG_ENC_CTRL_NAL_MODE_BIT BIT(29) -#define H1_REG_ENC_CTRL_WIDTH(w) ((w) << 19) -#define H1_REG_ENC_CTRL_HEIGHT(h) ((h) << 10) -#define H1_REG_ENC_PIC_INTER (0x0 << 3) -#define H1_REG_ENC_PIC_INTRA (0x1 << 3) -#define H1_REG_ENC_PIC_MVCINTER (0x2 << 3) -#define H1_REG_ENC_CTRL_ENC_MODE_H264 (0x3 << 1) -#define H1_REG_ENC_CTRL_ENC_MODE_JPEG (0x2 << 1) -#define H1_REG_ENC_CTRL_ENC_MODE_VP8 (0x1 << 1) -#define H1_REG_ENC_CTRL_EN_BIT BIT(0) -#define H1_REG_IN_IMG_CTRL 0x03c -#define H1_REG_IN_IMG_CTRL_ROW_LEN(x) ((x) << 12) -#define H1_REG_IN_IMG_CTRL_OVRFLR_D4(x) ((x) << 10) -#define H1_REG_IN_IMG_CTRL_OVRFLB(x) ((x) << 6) -#define H1_REG_IN_IMG_CTRL_FMT(x) ((x) << 2) -#define H1_REG_ENC_CTRL0 0x040 -#define H1_REG_ENC_CTRL0_INIT_QP(x) ((x) << 26) -#define H1_REG_ENC_CTRL0_SLICE_ALPHA(x) ((x) << 22) -#define H1_REG_ENC_CTRL0_SLICE_BETA(x) ((x) << 18) -#define H1_REG_ENC_CTRL0_CHROMA_QP_OFFSET(x) ((x) << 13) -#define H1_REG_ENC_CTRL0_FILTER_DIS(x) ((x) << 5) -#define H1_REG_ENC_CTRL0_IDR_PICID(x) ((x) << 1) -#define H1_REG_ENC_CTRL0_CONSTR_INTRA_PRED BIT(0) -#define H1_REG_ENC_CTRL1 0x044 -#define H1_REG_ENC_CTRL1_PPS_ID(x) ((x) << 24) -#define H1_REG_ENC_CTRL1_INTRA_PRED_MODE(x) ((x) << 16) -#define H1_REG_ENC_CTRL1_FRAME_NUM(x) ((x)) -#define H1_REG_ENC_CTRL2 0x048 -#define H1_REG_ENC_CTRL2_DEBLOCKING_FILETER_MODE(x) ((x) << 30) -#define H1_REG_ENC_CTRL2_H264_SLICE_SIZE(x) ((x) << 23) -#define H1_REG_ENC_CTRL2_DISABLE_QUARTER_PIXMV BIT(22) -#define H1_REG_ENC_CTRL2_TRANS8X8_MODE_EN BIT(21) -#define H1_REG_ENC_CTRL2_CABAC_INIT_IDC(x) ((x) << 19) -#define H1_REG_ENC_CTRL2_ENTROPY_CODING_MODE BIT(18) -#define H1_REG_ENC_CTRL2_H264_INTER4X4_MODE BIT(17) -#define H1_REG_ENC_CTRL2_H264_STREAM_MODE BIT(16) -#define H1_REG_ENC_CTRL2_INTRA16X16_MODE(x) ((x)) -#define H1_REG_ENC_CTRL3 0x04c -#define H1_REG_ENC_CTRL3_MUTIMV_EN BIT(30) -#define H1_REG_ENC_CTRL3_MV_PENALTY_1_4P(x) ((x) << 20) -#define H1_REG_ENC_CTRL3_MV_PENALTY_4P(x) ((x) << 10) -#define H1_REG_ENC_CTRL3_MV_PENALTY_1P(x) ((x)) -#define H1_REG_ENC_CTRL4 0x050 -#define H1_REG_ENC_CTRL4_MV_PENALTY_16X8_8X16(x) ((x) << 20) -#define H1_REG_ENC_CTRL4_MV_PENALTY_8X8(x) ((x) << 10) -#define H1_REG_ENC_CTRL4_8X4_4X8(x) ((x)) -#define H1_REG_ENC_CTRL5 0x054 -#define H1_REG_ENC_CTRL5_MACROBLOCK_PENALTY(x) ((x) << 24) -#define H1_REG_ENC_CTRL5_COMPLETE_SLICES(x) ((x) << 16) -#define H1_REG_ENC_CTRL5_INTER_MODE(x) ((x)) -#define H1_REG_STR_HDR_REM_MSB 0x058 -#define H1_REG_STR_HDR_REM_LSB 0x05c -#define H1_REG_STR_BUF_LIMIT 0x060 -#define H1_REG_MAD_CTRL 0x064 -#define H1_REG_MAD_CTRL_QP_ADJUST(x) ((x) << 28) -#define H1_REG_MAD_CTRL_MAD_THREDHOLD(x) ((x) << 22) -#define H1_REG_MAD_CTRL_QP_SUM_DIV2(x) ((x)) -#define H1_REG_ADDR_VP8_PROB_CNT 0x068 -#define H1_REG_QP_VAL 0x06c -#define H1_REG_QP_VAL_LUM(x) ((x) << 26) -#define H1_REG_QP_VAL_MAX(x) ((x) << 20) -#define H1_REG_QP_VAL_MIN(x) ((x) << 14) -#define H1_REG_QP_VAL_CHECKPOINT_DISTAN(x) ((x)) -#define H1_REG_VP8_QP_VAL(i) (0x06c + ((i) * 0x4)) -#define H1_REG_CHECKPOINT(i) (0x070 + ((i) * 0x4)) -#define H1_REG_CHECKPOINT_CHECK0(x) (((x) & 0xffff)) -#define H1_REG_CHECKPOINT_CHECK1(x) (((x) & 0xffff) << 16) -#define H1_REG_CHECKPOINT_RESULT(x) ((((x) >> (16 - 16 \ - * (i & 1))) & 0xffff) \ - * 32) -#define H1_REG_CHKPT_WORD_ERR(i) (0x084 + ((i) * 0x4)) -#define H1_REG_CHKPT_WORD_ERR_CHK0(x) (((x) & 0xffff)) -#define H1_REG_CHKPT_WORD_ERR_CHK1(x) (((x) & 0xffff) << 16) -#define H1_REG_VP8_BOOL_ENC 0x08c -#define H1_REG_CHKPT_DELTA_QP 0x090 -#define H1_REG_CHKPT_DELTA_QP_CHK0(x) (((x) & 0x0f) << 0) -#define H1_REG_CHKPT_DELTA_QP_CHK1(x) (((x) & 0x0f) << 4) -#define H1_REG_CHKPT_DELTA_QP_CHK2(x) (((x) & 0x0f) << 8) -#define H1_REG_CHKPT_DELTA_QP_CHK3(x) (((x) & 0x0f) << 12) -#define H1_REG_CHKPT_DELTA_QP_CHK4(x) (((x) & 0x0f) << 16) -#define H1_REG_CHKPT_DELTA_QP_CHK5(x) (((x) & 0x0f) << 20) -#define H1_REG_CHKPT_DELTA_QP_CHK6(x) (((x) & 0x0f) << 24) -#define H1_REG_VP8_CTRL0 0x090 -#define H1_REG_RLC_CTRL 0x094 -#define H1_REG_RLC_CTRL_STR_OFFS_SHIFT 23 -#define H1_REG_RLC_CTRL_STR_OFFS_MASK (0x3f << 23) -#define H1_REG_RLC_CTRL_RLC_SUM(x) ((x)) -#define H1_REG_MB_CTRL 0x098 -#define H1_REG_MB_CNT_OUT(x) (((x) & 0xffff)) -#define H1_REG_MB_CNT_SET(x) (((x) & 0xffff) << 16) -#define H1_REG_ADDR_NEXT_PIC 0x09c -#define H1_REG_JPEG_LUMA_QUAT(i) (0x100 + ((i) * 0x4)) -#define H1_REG_JPEG_CHROMA_QUAT(i) (0x140 + ((i) * 0x4)) -#define H1_REG_STABILIZATION_OUTPUT 0x0A0 -#define H1_REG_ADDR_CABAC_TBL 0x0cc -#define H1_REG_ADDR_MV_OUT 0x0d0 -#define H1_REG_RGB_YUV_COEFF(i) (0x0d4 + ((i) * 0x4)) -#define H1_REG_RGB_MASK_MSB 0x0dc -#define H1_REG_INTRA_AREA_CTRL 0x0e0 -#define H1_REG_CIR_INTRA_CTRL 0x0e4 -#define H1_REG_INTRA_SLICE_BITMAP(i) (0x0e8 + ((i) * 0x4)) -#define H1_REG_ADDR_VP8_DCT_PART(i) (0x0e8 + ((i) * 0x4)) -#define H1_REG_FIRST_ROI_AREA 0x0f0 -#define H1_REG_SECOND_ROI_AREA 0x0f4 -#define H1_REG_MVC_CTRL 0x0f8 -#define H1_REG_MVC_CTRL_MV16X16_FAVOR(x) ((x) << 28) -#define H1_REG_VP8_INTRA_PENALTY(i) (0x100 + ((i) * 0x4)) -#define H1_REG_ADDR_VP8_SEG_MAP 0x11c -#define H1_REG_VP8_SEG_QP(i) (0x120 + ((i) * 0x4)) -#define H1_REG_DMV_4P_1P_PENALTY(i) (0x180 + ((i) * 0x4)) -#define H1_REG_DMV_4P_1P_PENALTY_BIT(x, i) ((x) << (i) * 8) -#define H1_REG_DMV_QPEL_PENALTY(i) (0x200 + ((i) * 0x4)) -#define H1_REG_DMV_QPEL_PENALTY_BIT(x, i) ((x) << (i) * 8) -#define H1_REG_VP8_CTRL1 0x280 -#define H1_REG_VP8_BIT_COST_GOLDEN 0x284 -#define H1_REG_VP8_LOOP_FLT_DELTA(i) (0x288 + ((i) * 0x4)) - -#endif /* HANTRO_H1_REGS_H_ */ diff --git a/drivers/staging/media/hantro/hantro_h264.c b/drivers/staging/media/hantro/hantro_h264.c deleted file mode 100644 index 4e9a0ecf5c13..000000000000 --- a/drivers/staging/media/hantro/hantro_h264.c +++ /dev/null @@ -1,521 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Rockchip RK3288 VPU codec driver - * - * Copyright (c) 2014 Rockchip Electronics Co., Ltd. - * Hertz Wong <hertz.wong@rock-chips.com> - * Herman Chen <herman.chen@rock-chips.com> - * - * Copyright (C) 2014 Google, Inc. - * Tomasz Figa <tfiga@chromium.org> - */ - -#include <linux/types.h> -#include <media/v4l2-h264.h> -#include <media/v4l2-mem2mem.h> - -#include "hantro.h" -#include "hantro_hw.h" - -/* Size with u32 units. */ -#define CABAC_INIT_BUFFER_SIZE (460 * 2) -#define POC_BUFFER_SIZE 34 -#define SCALING_LIST_SIZE (6 * 16 + 2 * 64) - -/* - * For valid and long term reference marking, index are reversed, so bit 31 - * indicates the status of the picture 0. - */ -#define REF_BIT(i) BIT(32 - 1 - (i)) - -/* Data structure describing auxiliary buffer format. */ -struct hantro_h264_dec_priv_tbl { - u32 cabac_table[CABAC_INIT_BUFFER_SIZE]; - u32 poc[POC_BUFFER_SIZE]; - u8 scaling_list[SCALING_LIST_SIZE]; -}; - -/* - * Constant CABAC table. - * From drivers/media/platform/rk3288-vpu/rk3288_vpu_hw_h264d.c - * in https://chromium.googlesource.com/chromiumos/third_party/kernel, - * chromeos-3.14 branch. - */ -static const u32 h264_cabac_table[] = { - 0x14f10236, 0x034a14f1, 0x0236034a, 0xe47fe968, 0xfa35ff36, 0x07330000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x0029003f, 0x003f003f, 0xf7530456, 0x0061f948, 0x0d29033e, 0x000b0137, - 0x0045ef7f, 0xf3660052, 0xf94aeb6b, 0xe57fe17f, 0xe87fee5f, 0xe57feb72, - 0xe27fef7b, 0xf473f07a, 0xf573f43f, 0xfe44f154, 0xf368fd46, 0xf85df65a, - 0xe27fff4a, 0xfa61f95b, 0xec7ffc38, 0xfb52f94c, 0xea7df95d, 0xf557fd4d, - 0xfb47fc3f, 0xfc44f454, 0xf93ef941, 0x083d0538, 0xfe420140, 0x003dfe4e, - 0x01320734, 0x0a23002c, 0x0b26012d, 0x002e052c, 0x1f110133, 0x07321c13, - 0x10210e3e, 0xf36cf164, 0xf365f35b, 0xf45ef658, 0xf054f656, 0xf953f357, - 0xed5e0146, 0x0048fb4a, 0x123bf866, 0xf164005f, 0xfc4b0248, 0xf54bfd47, - 0x0f2ef345, 0x003e0041, 0x1525f148, 0x09391036, 0x003e0c48, 0x18000f09, - 0x08190d12, 0x0f090d13, 0x0a250c12, 0x061d1421, 0x0f1e042d, 0x013a003e, - 0x073d0c26, 0x0b2d0f27, 0x0b2a0d2c, 0x102d0c29, 0x0a311e22, 0x122a0a37, - 0x1133112e, 0x00591aed, 0x16ef1aef, 0x1ee71cec, 0x21e925e5, 0x21e928e4, - 0x26ef21f5, 0x28f129fa, 0x26012911, 0x1efa1b03, 0x1a1625f0, 0x23fc26f8, - 0x26fd2503, 0x26052a00, 0x23102716, 0x0e301b25, 0x153c0c44, 0x0261fd47, - 0xfa2afb32, 0xfd36fe3e, 0x003a013f, 0xfe48ff4a, 0xf75bfb43, 0xfb1bfd27, - 0xfe2c002e, 0xf040f844, 0xf64efa4d, 0xf656f45c, 0xf137f63c, 0xfa3efc41, - 0xf449f84c, 0xf950f758, 0xef6ef561, 0xec54f54f, 0xfa49fc4a, 0xf356f360, - 0xf561ed75, 0xf84efb21, 0xfc30fe35, 0xfd3ef347, 0xf64ff456, 0xf35af261, - 0x0000fa5d, 0xfa54f84f, 0x0042ff47, 0x003efe3c, 0xfe3bfb4b, 0xfd3efc3a, - 0xf742ff4f, 0x00470344, 0x0a2cf93e, 0x0f240e28, 0x101b0c1d, 0x012c1424, - 0x1220052a, 0x01300a3e, 0x112e0940, 0xf468f561, 0xf060f958, 0xf855f955, - 0xf755f358, 0x0442fd4d, 0xfd4cfa4c, 0x0a3aff4c, 0xff53f963, 0xf25f025f, - 0x004cfb4a, 0x0046f54b, 0x01440041, 0xf249033e, 0x043eff44, 0xf34b0b37, - 0x05400c46, 0x0f060613, 0x07100c0e, 0x120d0d0b, 0x0d0f0f10, 0x0c170d17, - 0x0f140e1a, 0x0e2c1128, 0x112f1811, 0x15151916, 0x1f1b161d, 0x13230e32, - 0x0a39073f, 0xfe4dfc52, 0xfd5e0945, 0xf46d24dd, 0x24de20e6, 0x25e22ce0, - 0x22ee22f1, 0x28f121f9, 0x23fb2100, 0x2602210d, 0x17230d3a, 0x1dfd1a00, - 0x161e1ff9, 0x23f122fd, 0x220324ff, 0x2205200b, 0x2305220c, 0x270b1e1d, - 0x221a1d27, 0x13421f15, 0x1f1f1932, 0xef78ec70, 0xee72f555, 0xf15cf259, - 0xe647f151, 0xf2500044, 0xf246e838, 0xe944e832, 0xf54a17f3, 0x1af328f1, - 0x31f22c03, 0x2d062c22, 0x21361352, 0xfd4bff17, 0x0122012b, 0x0036fe37, - 0x003d0140, 0x0044f75c, 0xf26af361, 0xf15af45a, 0xee58f649, 0xf74ff256, - 0xf649f646, 0xf645fb42, 0xf740fb3a, 0x023b15f6, 0x18f51cf8, 0x1cff1d03, - 0x1d092314, 0x1d240e43, 0x14f10236, 0x034a14f1, 0x0236034a, 0xe47fe968, - 0xfa35ff36, 0x07331721, 0x17021500, 0x01090031, 0xdb760539, 0xf34ef541, - 0x013e0c31, 0xfc491132, 0x1240092b, 0x1d001a43, 0x105a0968, 0xd27fec68, - 0x0143f34e, 0xf541013e, 0xfa56ef5f, 0xfa3d092d, 0xfd45fa51, 0xf5600637, - 0x0743fb56, 0x0258003a, 0xfd4cf65e, 0x05360445, 0xfd510058, 0xf943fb4a, - 0xfc4afb50, 0xf948013a, 0x0029003f, 0x003f003f, 0xf7530456, 0x0061f948, - 0x0d29033e, 0x002dfc4e, 0xfd60e57e, 0xe462e765, 0xe943e452, 0xec5ef053, - 0xea6eeb5b, 0xee66f35d, 0xe37ff95c, 0xfb59f960, 0xf36cfd2e, 0xff41ff39, - 0xf75dfd4a, 0xf75cf857, 0xe97e0536, 0x063c063b, 0x0645ff30, 0x0044fc45, - 0xf858fe55, 0xfa4eff4b, 0xf94d0236, 0x0532fd44, 0x0132062a, 0xfc51013f, - 0xfc460043, 0x0239fe4c, 0x0b230440, 0x013d0b23, 0x12190c18, 0x0d1d0d24, - 0xf65df949, 0xfe490d2e, 0x0931f964, 0x09350235, 0x0535fe3d, 0x00380038, - 0xf33ffb3c, 0xff3e0439, 0xfa450439, 0x0e270433, 0x0d440340, 0x013d093f, - 0x07321027, 0x052c0434, 0x0b30fb3c, 0xff3b003b, 0x1621052c, 0x0e2bff4e, - 0x003c0945, 0x0b1c0228, 0x032c0031, 0x002e022c, 0x0233002f, 0x0427023e, - 0x062e0036, 0x0336023a, 0x043f0633, 0x06390735, 0x06340637, 0x0b2d0e24, - 0x0835ff52, 0x0737fd4e, 0x0f2e161f, 0xff541907, 0x1ef91c03, 0x1c042000, - 0x22ff1e06, 0x1e062009, 0x1f131a1b, 0x1a1e2514, 0x1c221146, 0x0143053b, - 0x0943101e, 0x12201223, 0x161d181f, 0x1726122b, 0x14290b3f, 0x093b0940, - 0xff5efe59, 0xf76cfa4c, 0xfe2c002d, 0x0034fd40, 0xfe3bfc46, 0xfc4bf852, - 0xef66f74d, 0x0318002a, 0x00300037, 0xfa3bf947, 0xf453f557, 0xe277013a, - 0xfd1dff24, 0x0126022b, 0xfa37003a, 0x0040fd4a, 0xf65a0046, 0xfc1d051f, - 0x072a013b, 0xfe3afd48, 0xfd51f561, 0x003a0805, 0x0a0e0e12, 0x0d1b0228, - 0x003afd46, 0xfa4ff855, 0x0000f36a, 0xf06af657, 0xeb72ee6e, 0xf262ea6e, - 0xeb6aee67, 0xeb6be96c, 0xe670f660, 0xf45ffb5b, 0xf75dea5e, 0xfb560943, - 0xfc50f655, 0xff46073c, 0x093a053d, 0x0c320f32, 0x12311136, 0x0a29072e, - 0xff330731, 0x08340929, 0x062f0237, 0x0d290a2c, 0x06320535, 0x0d31043f, - 0x0640fe45, 0xfe3b0646, 0x0a2c091f, 0x0c2b0335, 0x0e220a26, 0xfd340d28, - 0x1120072c, 0x07260d32, 0x0a391a2b, 0x0e0b0b0e, 0x090b120b, 0x150917fe, - 0x20f120f1, 0x22eb27e9, 0x2adf29e1, 0x2ee426f4, 0x151d2de8, 0x35d330e6, - 0x41d52bed, 0x27f61e09, 0x121a141b, 0x0039f252, 0xfb4bed61, 0xdd7d1b00, - 0x1c001ffc, 0x1b062208, 0x1e0a1816, 0x21131620, 0x1a1f1529, 0x1a2c172f, - 0x10410e47, 0x083c063f, 0x11411518, 0x17141a17, 0x1b201c17, 0x1c181728, - 0x18201c1d, 0x172a1339, 0x1635163d, 0x0b560c28, 0x0b330e3b, 0xfc4ff947, - 0xfb45f746, 0xf842f644, 0xed49f445, 0xf046f143, 0xec3eed46, 0xf042ea41, - 0xec3f09fe, 0x1af721f7, 0x27f929fe, 0x2d033109, 0x2d1b243b, 0xfa42f923, - 0xf92af82d, 0xfb30f438, 0xfa3cfb3e, 0xf842f84c, 0xfb55fa51, 0xf64df951, - 0xef50ee49, 0xfc4af653, 0xf747f743, 0xff3df842, 0xf242003b, 0x023b15f3, - 0x21f227f9, 0x2efe3302, 0x3c063d11, 0x37222a3e, 0x14f10236, 0x034a14f1, - 0x0236034a, 0xe47fe968, 0xfa35ff36, 0x07331619, 0x22001000, 0xfe090429, - 0xe3760241, 0xfa47f34f, 0x05340932, 0xfd460a36, 0x1a221316, 0x28003902, - 0x29241a45, 0xd37ff165, 0xfc4cfa47, 0xf34f0534, 0x0645f35a, 0x0034082b, - 0xfe45fb52, 0xf660023b, 0x024bfd57, 0xfd640138, 0xfd4afa55, 0x003bfd51, - 0xf956fb5f, 0xff42ff4d, 0x0146fe56, 0xfb48003d, 0x0029003f, 0x003f003f, - 0xf7530456, 0x0061f948, 0x0d29033e, 0x0d0f0733, 0x0250d97f, 0xee5bef60, - 0xe651dd62, 0xe866e961, 0xe577e863, 0xeb6eee66, 0xdc7f0050, 0xfb59f95e, - 0xfc5c0027, 0x0041f154, 0xdd7ffe49, 0xf468f75b, 0xe17f0337, 0x07380737, - 0x083dfd35, 0x0044f94a, 0xf758f367, 0xf35bf759, 0xf25cf84c, 0xf457e96e, - 0xe869f64e, 0xec70ef63, 0xb27fba7f, 0xce7fd27f, 0xfc42fb4e, 0xfc47f848, - 0x023bff37, 0xf946fa4b, 0xf859de77, 0xfd4b2014, 0x1e16d47f, 0x0036fb3d, - 0x003aff3c, 0xfd3df843, 0xe754f24a, 0xfb410534, 0x0239003d, 0xf745f546, - 0x1237fc47, 0x003a073d, 0x09291219, 0x0920052b, 0x092f002c, 0x0033022e, - 0x1326fc42, 0x0f260c2a, 0x09220059, 0x042d0a1c, 0x0a1f21f5, 0x34d5120f, - 0x1c0023ea, 0x26e72200, 0x27ee20f4, 0x66a20000, 0x38f121fc, 0x1d0a25fb, - 0x33e327f7, 0x34de45c6, 0x43c12cfb, 0x200737e3, 0x20010000, 0x1b2421e7, - 0x22e224e4, 0x26e426e5, 0x22ee23f0, 0x22f220f8, 0x25fa2300, 0x1e0a1c12, - 0x1a191d29, 0x004b0248, 0x084d0e23, 0x121f1123, 0x151e112d, 0x142a122d, - 0x1b1a1036, 0x07421038, 0x0b490a43, 0xf674e970, 0xf147f93d, 0x0035fb42, - 0xf54df750, 0xf754f657, 0xde7feb65, 0xfd27fb35, 0xf93df54b, 0xf14def5b, - 0xe76be76f, 0xe47af54c, 0xf62cf634, 0xf639f73a, 0xf048f945, 0xfc45fb4a, - 0xf7560242, 0xf7220120, 0x0b1f0534, 0xfe37fe43, 0x0049f859, 0x03340704, - 0x0a081108, 0x10130325, 0xff3dfb49, 0xff46fc4e, 0x0000eb7e, 0xe97cec6e, - 0xe67ee77c, 0xef69e579, 0xe575ef66, 0xe675e574, 0xdf7af65f, 0xf264f85f, - 0xef6fe472, 0xfa59fe50, 0xfc52f755, 0xf851ff48, 0x05400143, 0x09380045, - 0x01450745, 0xf945fa43, 0xf04dfe40, 0x023dfa43, 0xfd400239, 0xfd41fd42, - 0x003e0933, 0xff42fe47, 0xfe4bff46, 0xf7480e3c, 0x1025002f, 0x12230b25, - 0x0c290a29, 0x02300c29, 0x0d29003b, 0x03321328, 0x03421232, 0x13fa12fa, - 0x0e001af4, 0x1ff021e7, 0x21ea25e4, 0x27e22ae2, 0x2fd62ddc, 0x31de29ef, - 0x200945b9, 0x3fc142c0, 0x4db636d9, 0x34dd29f6, 0x240028ff, 0x1e0e1c1a, - 0x17250c37, 0x0b4125df, 0x27dc28db, 0x26e22edf, 0x2ae228e8, 0x31e326f4, - 0x28f626fd, 0x2efb1f14, 0x1d1e192c, 0x0c300b31, 0x1a2d1616, 0x17161b15, - 0x21141a1c, 0x1e181b22, 0x122a1927, 0x12320c46, 0x15360e47, 0x0b531920, - 0x15311536, 0xfb55fa51, 0xf64df951, 0xef50ee49, 0xfc4af653, 0xf747f743, - 0xff3df842, 0xf242003b, 0x023b11f6, 0x20f32af7, 0x31fb3500, 0x4003440a, - 0x421b2f39, 0xfb470018, 0xff24fe2a, 0xfe34f739, 0xfa3ffc41, 0xfc43f952, - 0xfd51fd4c, 0xf948fa4e, 0xf448f244, 0xfd46fa4c, 0xfb42fb3e, 0x0039fc3d, - 0xf73c0136, 0x023a11f6, 0x20f32af7, 0x31fb3500, 0x4003440a, 0x421b2f39, - 0x14f10236, 0x034a14f1, 0x0236034a, 0xe47fe968, 0xfa35ff36, 0x07331d10, - 0x19000e00, 0xf633fd3e, 0xe5631a10, 0xfc55e866, 0x05390639, 0xef490e39, - 0x1428140a, 0x1d003600, 0x252a0c61, 0xe07fea75, 0xfe4afc55, 0xe8660539, - 0xfa5df258, 0xfa2c0437, 0xf559f167, 0xeb741339, 0x143a0454, 0x0660013f, - 0xfb55f36a, 0x053f064b, 0xfd5aff65, 0x0337fc4f, 0xfe4bf461, 0xf932013c, - 0x0029003f, 0x003f003f, 0xf7530456, 0x0061f948, 0x0d29033e, 0x0722f758, - 0xec7fdc7f, 0xef5bf25f, 0xe754e756, 0xf459ef5b, 0xe17ff24c, 0xee67f35a, - 0xdb7f0b50, 0x054c0254, 0x054efa37, 0x043df253, 0xdb7ffb4f, 0xf568f55b, - 0xe27f0041, 0xfe4f0048, 0xfc5cfa38, 0x0344f847, 0xf362fc56, 0xf458fb52, - 0xfd48fc43, 0xf848f059, 0xf745ff3b, 0x05420439, 0xfc47fe47, 0x023aff4a, - 0xfc2cff45, 0x003ef933, 0xfc2ffa2a, 0xfd29fa35, 0x084cf74e, 0xf5530934, - 0x0043fb5a, 0x0143f148, 0xfb4bf850, 0xeb53eb40, 0xf31fe740, 0xe35e094b, - 0x113ff84a, 0xfb23fe1b, 0x0d5b0341, 0xf945084d, 0xf642033e, 0xfd44ec51, - 0x001e0107, 0xfd17eb4a, 0x1042e97c, 0x11252cee, 0x32deea7f, 0x0427002a, - 0x07220b1d, 0x081f0625, 0x072a0328, 0x08210d2b, 0x0d24042f, 0x0337023a, - 0x063c082c, 0x0b2c0e2a, 0x07300438, 0x04340d25, 0x0931133a, 0x0a300c2d, - 0x00451421, 0x083f23ee, 0x21e71cfd, 0x180a1b00, 0x22f234d4, 0x27e81311, - 0x1f19241d, 0x1821220f, 0x1e141649, 0x1422131f, 0x1b2c1310, 0x0f240f24, - 0x151c1915, 0x1e141f0c, 0x1b10182a, 0x005d0e38, 0x0f391a26, 0xe87fe873, - 0xea52f73e, 0x0035003b, 0xf255f359, 0xf35ef55c, 0xe37feb64, 0xf239f443, - 0xf547f64d, 0xeb55f058, 0xe968f162, 0xdb7ff652, 0xf830f83d, 0xf842f946, - 0xf24bf64f, 0xf753f45c, 0xee6cfc4f, 0xea45f04b, 0xfe3a013a, 0xf34ef753, - 0xfc51f363, 0xf351fa26, 0xf33efa3a, 0xfe3bf049, 0xf64cf356, 0xf753f657, - 0x0000ea7f, 0xe77fe778, 0xe57fed72, 0xe975e776, 0xe675e871, 0xe476e178, - 0xdb7cf65e, 0xf166f663, 0xf36ace7f, 0xfb5c1139, 0xfb56f35e, 0xf45bfe4d, - 0x0047ff49, 0x0440f951, 0x05400f39, 0x01430044, 0xf6430144, 0x004d0240, - 0x0044fb4e, 0x0737053b, 0x02410e36, 0x0f2c053c, 0x0246fe4c, 0xee560c46, - 0x0540f446, 0x0b370538, 0x00450241, 0xfa4a0536, 0x0736fa4c, 0xf552fe4d, - 0xfe4d192a, 0x11f310f7, 0x11f41beb, 0x25e229d8, 0x2ad730d1, 0x27e02ed8, - 0x34cd2ed7, 0x34d92bed, 0x200b3dc9, 0x38d23ece, 0x51bd2dec, 0x23fe1c0f, - 0x22012701, 0x1e111426, 0x122d0f36, 0x004f24f0, 0x25f225ef, 0x2001220f, - 0x1d0f1819, 0x22161f10, 0x23121f1c, 0x2129241c, 0x1b2f153e, 0x121f131a, - 0x24181817, 0x1b10181e, 0x1f1d1629, 0x162a103c, 0x0f340e3c, 0x034ef07b, - 0x15351638, 0x193d1521, 0x1332113d, 0xfd4ef84a, 0xf748f648, 0xee4bf447, - 0xf53ffb46, 0xef4bf248, 0xf043f835, 0xf23bf734, 0xf54409fe, 0x1ef61ffc, - 0x21ff2107, 0x1f0c2517, 0x1f261440, 0xf747f925, 0xf82cf531, 0xf638f43b, - 0xf83ff743, 0xfa44f64f, 0xfd4ef84a, 0xf748f648, 0xee4bf447, 0xf53ffb46, - 0xef4bf248, 0xf043f835, 0xf23bf734, 0xf54409fe, 0x1ef61ffc, 0x21ff2107, - 0x1f0c2517, 0x1f261440 -}; - -static void -assemble_scaling_list(struct hantro_ctx *ctx) -{ - const struct hantro_h264_dec_ctrls *ctrls = &ctx->h264_dec.ctrls; - const struct v4l2_ctrl_h264_scaling_matrix *scaling = ctrls->scaling; - const struct v4l2_ctrl_h264_pps *pps = ctrls->pps; - const size_t num_list_4x4 = ARRAY_SIZE(scaling->scaling_list_4x4); - const size_t list_len_4x4 = ARRAY_SIZE(scaling->scaling_list_4x4[0]); - const size_t list_len_8x8 = ARRAY_SIZE(scaling->scaling_list_8x8[0]); - struct hantro_h264_dec_priv_tbl *tbl = ctx->h264_dec.priv.cpu; - u32 *dst = (u32 *)tbl->scaling_list; - const u32 *src; - int i, j; - - if (!(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT)) - return; - - for (i = 0; i < num_list_4x4; i++) { - src = (u32 *)&scaling->scaling_list_4x4[i]; - for (j = 0; j < list_len_4x4 / 4; j++) - *dst++ = swab32(src[j]); - } - - /* Only Intra/Inter Y lists */ - for (i = 0; i < 2; i++) { - src = (u32 *)&scaling->scaling_list_8x8[i]; - for (j = 0; j < list_len_8x8 / 4; j++) - *dst++ = swab32(src[j]); - } -} - -static void prepare_table(struct hantro_ctx *ctx) -{ - const struct hantro_h264_dec_ctrls *ctrls = &ctx->h264_dec.ctrls; - const struct v4l2_ctrl_h264_decode_params *dec_param = ctrls->decode; - const struct v4l2_ctrl_h264_sps *sps = ctrls->sps; - struct hantro_h264_dec_priv_tbl *tbl = ctx->h264_dec.priv.cpu; - const struct v4l2_h264_dpb_entry *dpb = ctx->h264_dec.dpb; - u32 dpb_longterm = 0; - u32 dpb_valid = 0; - int i; - - for (i = 0; i < HANTRO_H264_DPB_SIZE; ++i) { - tbl->poc[i * 2] = dpb[i].top_field_order_cnt; - tbl->poc[i * 2 + 1] = dpb[i].bottom_field_order_cnt; - - if (!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_VALID)) - continue; - - /* - * Set up bit maps of valid and long term DPBs. - * NOTE: The bits are reversed, i.e. MSb is DPB 0. For frame - * decoding, bit 31 to 15 are used, while for field decoding, - * all bits are used, with bit 31 being a top field, 30 a bottom - * field and so on. - */ - if (dec_param->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC) { - if (dpb[i].fields & V4L2_H264_TOP_FIELD_REF) - dpb_valid |= REF_BIT(i * 2); - - if (dpb[i].fields & V4L2_H264_BOTTOM_FIELD_REF) - dpb_valid |= REF_BIT(i * 2 + 1); - - if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM) { - dpb_longterm |= REF_BIT(i * 2); - dpb_longterm |= REF_BIT(i * 2 + 1); - } - } else { - dpb_valid |= REF_BIT(i); - - if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM) - dpb_longterm |= REF_BIT(i); - } - } - ctx->h264_dec.dpb_valid = dpb_valid; - ctx->h264_dec.dpb_longterm = dpb_longterm; - - if ((dec_param->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC) || - !(sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD)) { - tbl->poc[32] = ctx->h264_dec.cur_poc; - tbl->poc[33] = 0; - } else { - tbl->poc[32] = dec_param->top_field_order_cnt; - tbl->poc[33] = dec_param->bottom_field_order_cnt; - } - - assemble_scaling_list(ctx); -} - -static bool dpb_entry_match(const struct v4l2_h264_dpb_entry *a, - const struct v4l2_h264_dpb_entry *b) -{ - return a->reference_ts == b->reference_ts; -} - -static void update_dpb(struct hantro_ctx *ctx) -{ - const struct v4l2_ctrl_h264_decode_params *dec_param; - DECLARE_BITMAP(new, ARRAY_SIZE(dec_param->dpb)) = { 0, }; - DECLARE_BITMAP(used, ARRAY_SIZE(dec_param->dpb)) = { 0, }; - unsigned int i, j; - - dec_param = ctx->h264_dec.ctrls.decode; - - /* Disable all entries by default. */ - for (i = 0; i < ARRAY_SIZE(ctx->h264_dec.dpb); i++) - ctx->h264_dec.dpb[i].flags = 0; - - /* Try to match new DPB entries with existing ones by their POCs. */ - for (i = 0; i < ARRAY_SIZE(dec_param->dpb); i++) { - const struct v4l2_h264_dpb_entry *ndpb = &dec_param->dpb[i]; - - if (!(ndpb->flags & V4L2_H264_DPB_ENTRY_FLAG_VALID)) - continue; - - /* - * To cut off some comparisons, iterate only on target DPB - * entries which are not used yet. - */ - for_each_clear_bit(j, used, ARRAY_SIZE(ctx->h264_dec.dpb)) { - struct v4l2_h264_dpb_entry *cdpb; - - cdpb = &ctx->h264_dec.dpb[j]; - if (!dpb_entry_match(cdpb, ndpb)) - continue; - - *cdpb = *ndpb; - set_bit(j, used); - break; - } - - if (j == ARRAY_SIZE(ctx->h264_dec.dpb)) - set_bit(i, new); - } - - /* For entries that could not be matched, use remaining free slots. */ - for_each_set_bit(i, new, ARRAY_SIZE(dec_param->dpb)) { - const struct v4l2_h264_dpb_entry *ndpb = &dec_param->dpb[i]; - struct v4l2_h264_dpb_entry *cdpb; - - /* - * Both arrays are of the same sizes, so there is no way - * we can end up with no space in target array, unless - * something is buggy. - */ - j = find_first_zero_bit(used, ARRAY_SIZE(ctx->h264_dec.dpb)); - if (WARN_ON(j >= ARRAY_SIZE(ctx->h264_dec.dpb))) - return; - - cdpb = &ctx->h264_dec.dpb[j]; - *cdpb = *ndpb; - set_bit(j, used); - } -} - -dma_addr_t hantro_h264_get_ref_buf(struct hantro_ctx *ctx, - unsigned int dpb_idx) -{ - struct v4l2_h264_dpb_entry *dpb = ctx->h264_dec.dpb; - dma_addr_t dma_addr = 0; - s32 cur_poc = ctx->h264_dec.cur_poc; - u32 flags; - - if (dpb[dpb_idx].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE) - dma_addr = hantro_get_ref(ctx, dpb[dpb_idx].reference_ts); - - if (!dma_addr) { - struct vb2_v4l2_buffer *dst_buf; - struct vb2_buffer *buf; - - /* - * If a DPB entry is unused or invalid, address of current - * destination buffer is returned. - */ - dst_buf = hantro_get_dst_buf(ctx); - buf = &dst_buf->vb2_buf; - dma_addr = hantro_get_dec_buf_addr(ctx, buf); - } - - flags = dpb[dpb_idx].flags & V4L2_H264_DPB_ENTRY_FLAG_FIELD ? 0x2 : 0; - flags |= abs(dpb[dpb_idx].top_field_order_cnt - cur_poc) < - abs(dpb[dpb_idx].bottom_field_order_cnt - cur_poc) ? - 0x1 : 0; - - return dma_addr | flags; -} - -u16 hantro_h264_get_ref_nbr(struct hantro_ctx *ctx, unsigned int dpb_idx) -{ - const struct v4l2_h264_dpb_entry *dpb = &ctx->h264_dec.dpb[dpb_idx]; - - if (!(dpb->flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) - return 0; - return dpb->frame_num; -} - -/* - * Removes all references with the same parity as the current picture from the - * reference list. The remaining list will have references with the opposite - * parity. This is effectively a deduplication of references since each buffer - * stores two fields. For this reason, each buffer is found twice in the - * reference list. - * - * This technique has been chosen through trial and error. This simple approach - * resulted in the highest conformance score. Note that this method may suffer - * worse quality in the case an opposite reference frame has been lost. If this - * becomes a problem in the future, it should be possible to add a preprocessing - * to identify un-paired fields and avoid removing them. - */ -static void deduplicate_reflist(struct v4l2_h264_reflist_builder *b, - struct v4l2_h264_reference *reflist) -{ - int write_idx = 0; - int i; - - if (b->cur_pic_fields == V4L2_H264_FRAME_REF) { - write_idx = b->num_valid; - goto done; - } - - for (i = 0; i < b->num_valid; i++) { - if (!(b->cur_pic_fields == reflist[i].fields)) { - reflist[write_idx++] = reflist[i]; - continue; - } - } - -done: - /* Should not happen unless we have a bug in the reflist builder. */ - if (WARN_ON(write_idx > 16)) - write_idx = 16; - - /* Clear the remaining, some streams fails otherwise */ - for (; write_idx < 16; write_idx++) - reflist[write_idx].index = 15; -} - -int hantro_h264_dec_prepare_run(struct hantro_ctx *ctx) -{ - struct hantro_h264_dec_hw_ctx *h264_ctx = &ctx->h264_dec; - struct hantro_h264_dec_ctrls *ctrls = &h264_ctx->ctrls; - struct v4l2_h264_reflist_builder reflist_builder; - - hantro_start_prepare_run(ctx); - - ctrls->scaling = - hantro_get_ctrl(ctx, V4L2_CID_STATELESS_H264_SCALING_MATRIX); - if (WARN_ON(!ctrls->scaling)) - return -EINVAL; - - ctrls->decode = - hantro_get_ctrl(ctx, V4L2_CID_STATELESS_H264_DECODE_PARAMS); - if (WARN_ON(!ctrls->decode)) - return -EINVAL; - - ctrls->sps = - hantro_get_ctrl(ctx, V4L2_CID_STATELESS_H264_SPS); - if (WARN_ON(!ctrls->sps)) - return -EINVAL; - - ctrls->pps = - hantro_get_ctrl(ctx, V4L2_CID_STATELESS_H264_PPS); - if (WARN_ON(!ctrls->pps)) - return -EINVAL; - - /* Update the DPB with new refs. */ - update_dpb(ctx); - - /* Build the P/B{0,1} ref lists. */ - v4l2_h264_init_reflist_builder(&reflist_builder, ctrls->decode, - ctrls->sps, ctx->h264_dec.dpb); - h264_ctx->cur_poc = reflist_builder.cur_pic_order_count; - - /* Prepare data in memory. */ - prepare_table(ctx); - - v4l2_h264_build_p_ref_list(&reflist_builder, h264_ctx->reflists.p); - v4l2_h264_build_b_ref_lists(&reflist_builder, h264_ctx->reflists.b0, - h264_ctx->reflists.b1); - - /* - * Reduce ref lists to at most 16 entries, Hantro hardware will deduce - * the actual picture lists in field through the dpb_valid, - * dpb_longterm bitmap along with the current frame parity. - */ - if (reflist_builder.cur_pic_fields != V4L2_H264_FRAME_REF) { - deduplicate_reflist(&reflist_builder, h264_ctx->reflists.p); - deduplicate_reflist(&reflist_builder, h264_ctx->reflists.b0); - deduplicate_reflist(&reflist_builder, h264_ctx->reflists.b1); - } - - return 0; -} - -void hantro_h264_dec_exit(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - struct hantro_h264_dec_hw_ctx *h264_dec = &ctx->h264_dec; - struct hantro_aux_buf *priv = &h264_dec->priv; - - dma_free_coherent(vpu->dev, priv->size, priv->cpu, priv->dma); -} - -int hantro_h264_dec_init(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - struct hantro_h264_dec_hw_ctx *h264_dec = &ctx->h264_dec; - struct hantro_aux_buf *priv = &h264_dec->priv; - struct hantro_h264_dec_priv_tbl *tbl; - - priv->cpu = dma_alloc_coherent(vpu->dev, sizeof(*tbl), &priv->dma, - GFP_KERNEL); - if (!priv->cpu) - return -ENOMEM; - - priv->size = sizeof(*tbl); - tbl = priv->cpu; - memcpy(tbl->cabac_table, h264_cabac_table, sizeof(tbl->cabac_table)); - - return 0; -} diff --git a/drivers/staging/media/hantro/hantro_hevc.c b/drivers/staging/media/hantro/hantro_hevc.c deleted file mode 100644 index b990bc98164c..000000000000 --- a/drivers/staging/media/hantro/hantro_hevc.c +++ /dev/null @@ -1,284 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VPU HEVC codec driver - * - * Copyright (C) 2020 Safran Passenger Innovations LLC - */ - -#include <linux/types.h> -#include <media/v4l2-mem2mem.h> - -#include "hantro.h" -#include "hantro_hw.h" - -#define VERT_FILTER_RAM_SIZE 8 /* bytes per pixel row */ -/* - * BSD control data of current picture at tile border - * 128 bits per 4x4 tile = 128/(8*4) bytes per row - */ -#define BSD_CTRL_RAM_SIZE 4 /* bytes per pixel row */ -/* tile border coefficients of filter */ -#define VERT_SAO_RAM_SIZE 48 /* bytes per pixel */ - -#define SCALING_LIST_SIZE (16 * 64) - -#define MAX_TILE_COLS 20 -#define MAX_TILE_ROWS 22 - -void hantro_hevc_ref_init(struct hantro_ctx *ctx) -{ - struct hantro_hevc_dec_hw_ctx *hevc_dec = &ctx->hevc_dec; - - hevc_dec->ref_bufs_used = 0; -} - -dma_addr_t hantro_hevc_get_ref_buf(struct hantro_ctx *ctx, - s32 poc) -{ - struct hantro_hevc_dec_hw_ctx *hevc_dec = &ctx->hevc_dec; - int i; - - /* Find the reference buffer in already known ones */ - for (i = 0; i < NUM_REF_PICTURES; i++) { - if (hevc_dec->ref_bufs_poc[i] == poc) { - hevc_dec->ref_bufs_used |= 1 << i; - return hevc_dec->ref_bufs[i].dma; - } - } - - return 0; -} - -int hantro_hevc_add_ref_buf(struct hantro_ctx *ctx, int poc, dma_addr_t addr) -{ - struct hantro_hevc_dec_hw_ctx *hevc_dec = &ctx->hevc_dec; - int i; - - /* Add a new reference buffer */ - for (i = 0; i < NUM_REF_PICTURES; i++) { - if (!(hevc_dec->ref_bufs_used & 1 << i)) { - hevc_dec->ref_bufs_used |= 1 << i; - hevc_dec->ref_bufs_poc[i] = poc; - hevc_dec->ref_bufs[i].dma = addr; - return 0; - } - } - - return -EINVAL; -} - -static int tile_buffer_reallocate(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - struct hantro_hevc_dec_hw_ctx *hevc_dec = &ctx->hevc_dec; - const struct hantro_hevc_dec_ctrls *ctrls = &ctx->hevc_dec.ctrls; - const struct v4l2_ctrl_hevc_pps *pps = ctrls->pps; - const struct v4l2_ctrl_hevc_sps *sps = ctrls->sps; - unsigned int num_tile_cols = pps->num_tile_columns_minus1 + 1; - unsigned int height64 = (sps->pic_height_in_luma_samples + 63) & ~63; - unsigned int size; - - if (num_tile_cols <= 1 || - num_tile_cols <= hevc_dec->num_tile_cols_allocated) - return 0; - - /* Need to reallocate due to tiles passed via PPS */ - if (hevc_dec->tile_filter.cpu) { - dma_free_coherent(vpu->dev, hevc_dec->tile_filter.size, - hevc_dec->tile_filter.cpu, - hevc_dec->tile_filter.dma); - hevc_dec->tile_filter.cpu = NULL; - } - - if (hevc_dec->tile_sao.cpu) { - dma_free_coherent(vpu->dev, hevc_dec->tile_sao.size, - hevc_dec->tile_sao.cpu, - hevc_dec->tile_sao.dma); - hevc_dec->tile_sao.cpu = NULL; - } - - if (hevc_dec->tile_bsd.cpu) { - dma_free_coherent(vpu->dev, hevc_dec->tile_bsd.size, - hevc_dec->tile_bsd.cpu, - hevc_dec->tile_bsd.dma); - hevc_dec->tile_bsd.cpu = NULL; - } - - size = VERT_FILTER_RAM_SIZE * height64 * (num_tile_cols - 1); - hevc_dec->tile_filter.cpu = dma_alloc_coherent(vpu->dev, size, - &hevc_dec->tile_filter.dma, - GFP_KERNEL); - if (!hevc_dec->tile_filter.cpu) - goto err_free_tile_buffers; - hevc_dec->tile_filter.size = size; - - size = VERT_SAO_RAM_SIZE * height64 * (num_tile_cols - 1); - hevc_dec->tile_sao.cpu = dma_alloc_coherent(vpu->dev, size, - &hevc_dec->tile_sao.dma, - GFP_KERNEL); - if (!hevc_dec->tile_sao.cpu) - goto err_free_tile_buffers; - hevc_dec->tile_sao.size = size; - - size = BSD_CTRL_RAM_SIZE * height64 * (num_tile_cols - 1); - hevc_dec->tile_bsd.cpu = dma_alloc_coherent(vpu->dev, size, - &hevc_dec->tile_bsd.dma, - GFP_KERNEL); - if (!hevc_dec->tile_bsd.cpu) - goto err_free_tile_buffers; - hevc_dec->tile_bsd.size = size; - - hevc_dec->num_tile_cols_allocated = num_tile_cols; - - return 0; - -err_free_tile_buffers: - if (hevc_dec->tile_filter.cpu) - dma_free_coherent(vpu->dev, hevc_dec->tile_filter.size, - hevc_dec->tile_filter.cpu, - hevc_dec->tile_filter.dma); - hevc_dec->tile_filter.cpu = NULL; - - if (hevc_dec->tile_sao.cpu) - dma_free_coherent(vpu->dev, hevc_dec->tile_sao.size, - hevc_dec->tile_sao.cpu, - hevc_dec->tile_sao.dma); - hevc_dec->tile_sao.cpu = NULL; - - if (hevc_dec->tile_bsd.cpu) - dma_free_coherent(vpu->dev, hevc_dec->tile_bsd.size, - hevc_dec->tile_bsd.cpu, - hevc_dec->tile_bsd.dma); - hevc_dec->tile_bsd.cpu = NULL; - - return -ENOMEM; -} - -static int hantro_hevc_validate_sps(struct hantro_ctx *ctx, const struct v4l2_ctrl_hevc_sps *sps) -{ - /* - * for tile pixel format check if the width and height match - * hardware constraints - */ - if (ctx->vpu_dst_fmt->fourcc == V4L2_PIX_FMT_NV12_4L4) { - if (ctx->dst_fmt.width != - ALIGN(sps->pic_width_in_luma_samples, ctx->vpu_dst_fmt->frmsize.step_width)) - return -EINVAL; - - if (ctx->dst_fmt.height != - ALIGN(sps->pic_height_in_luma_samples, ctx->vpu_dst_fmt->frmsize.step_height)) - return -EINVAL; - } - - return 0; -} - -int hantro_hevc_dec_prepare_run(struct hantro_ctx *ctx) -{ - struct hantro_hevc_dec_hw_ctx *hevc_ctx = &ctx->hevc_dec; - struct hantro_hevc_dec_ctrls *ctrls = &hevc_ctx->ctrls; - int ret; - - hantro_start_prepare_run(ctx); - - ctrls->decode_params = - hantro_get_ctrl(ctx, V4L2_CID_STATELESS_HEVC_DECODE_PARAMS); - if (WARN_ON(!ctrls->decode_params)) - return -EINVAL; - - ctrls->scaling = - hantro_get_ctrl(ctx, V4L2_CID_STATELESS_HEVC_SCALING_MATRIX); - if (WARN_ON(!ctrls->scaling)) - return -EINVAL; - - ctrls->sps = - hantro_get_ctrl(ctx, V4L2_CID_STATELESS_HEVC_SPS); - if (WARN_ON(!ctrls->sps)) - return -EINVAL; - - ret = hantro_hevc_validate_sps(ctx, ctrls->sps); - if (ret) - return ret; - - ctrls->pps = - hantro_get_ctrl(ctx, V4L2_CID_STATELESS_HEVC_PPS); - if (WARN_ON(!ctrls->pps)) - return -EINVAL; - - ret = tile_buffer_reallocate(ctx); - if (ret) - return ret; - - return 0; -} - -void hantro_hevc_dec_exit(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - struct hantro_hevc_dec_hw_ctx *hevc_dec = &ctx->hevc_dec; - - if (hevc_dec->tile_sizes.cpu) - dma_free_coherent(vpu->dev, hevc_dec->tile_sizes.size, - hevc_dec->tile_sizes.cpu, - hevc_dec->tile_sizes.dma); - hevc_dec->tile_sizes.cpu = NULL; - - if (hevc_dec->scaling_lists.cpu) - dma_free_coherent(vpu->dev, hevc_dec->scaling_lists.size, - hevc_dec->scaling_lists.cpu, - hevc_dec->scaling_lists.dma); - hevc_dec->scaling_lists.cpu = NULL; - - if (hevc_dec->tile_filter.cpu) - dma_free_coherent(vpu->dev, hevc_dec->tile_filter.size, - hevc_dec->tile_filter.cpu, - hevc_dec->tile_filter.dma); - hevc_dec->tile_filter.cpu = NULL; - - if (hevc_dec->tile_sao.cpu) - dma_free_coherent(vpu->dev, hevc_dec->tile_sao.size, - hevc_dec->tile_sao.cpu, - hevc_dec->tile_sao.dma); - hevc_dec->tile_sao.cpu = NULL; - - if (hevc_dec->tile_bsd.cpu) - dma_free_coherent(vpu->dev, hevc_dec->tile_bsd.size, - hevc_dec->tile_bsd.cpu, - hevc_dec->tile_bsd.dma); - hevc_dec->tile_bsd.cpu = NULL; -} - -int hantro_hevc_dec_init(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - struct hantro_hevc_dec_hw_ctx *hevc_dec = &ctx->hevc_dec; - unsigned int size; - - memset(hevc_dec, 0, sizeof(*hevc_dec)); - - /* - * Maximum number of tiles times width and height (2 bytes each), - * rounding up to next 16 bytes boundary + one extra 16 byte - * chunk (HW guys wanted to have this). - */ - size = round_up(MAX_TILE_COLS * MAX_TILE_ROWS * 4 * sizeof(u16) + 16, 16); - hevc_dec->tile_sizes.cpu = dma_alloc_coherent(vpu->dev, size, - &hevc_dec->tile_sizes.dma, - GFP_KERNEL); - if (!hevc_dec->tile_sizes.cpu) - return -ENOMEM; - - hevc_dec->tile_sizes.size = size; - - hevc_dec->scaling_lists.cpu = dma_alloc_coherent(vpu->dev, SCALING_LIST_SIZE, - &hevc_dec->scaling_lists.dma, - GFP_KERNEL); - if (!hevc_dec->scaling_lists.cpu) - return -ENOMEM; - - hevc_dec->scaling_lists.size = SCALING_LIST_SIZE; - - hantro_hevc_ref_init(ctx); - - return 0; -} diff --git a/drivers/staging/media/hantro/hantro_hw.h b/drivers/staging/media/hantro/hantro_hw.h deleted file mode 100644 index e83f0c523a30..000000000000 --- a/drivers/staging/media/hantro/hantro_hw.h +++ /dev/null @@ -1,441 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Hantro VPU codec driver - * - * Copyright 2018 Google LLC. - * Tomasz Figa <tfiga@chromium.org> - */ - -#ifndef HANTRO_HW_H_ -#define HANTRO_HW_H_ - -#include <linux/interrupt.h> -#include <linux/v4l2-controls.h> -#include <media/v4l2-ctrls.h> -#include <media/v4l2-vp9.h> -#include <media/videobuf2-core.h> - -#define DEC_8190_ALIGN_MASK 0x07U - -#define MB_DIM 16 -#define TILE_MB_DIM 4 -#define MB_WIDTH(w) DIV_ROUND_UP(w, MB_DIM) -#define MB_HEIGHT(h) DIV_ROUND_UP(h, MB_DIM) - -#define FMT_MIN_WIDTH 48 -#define FMT_MIN_HEIGHT 48 -#define FMT_HD_WIDTH 1280 -#define FMT_HD_HEIGHT 720 -#define FMT_FHD_WIDTH 1920 -#define FMT_FHD_HEIGHT 1088 -#define FMT_UHD_WIDTH 3840 -#define FMT_UHD_HEIGHT 2160 -#define FMT_4K_WIDTH 4096 -#define FMT_4K_HEIGHT 2304 - -#define NUM_REF_PICTURES (V4L2_HEVC_DPB_ENTRIES_NUM_MAX + 1) - -struct hantro_dev; -struct hantro_ctx; -struct hantro_buf; -struct hantro_variant; - -/** - * struct hantro_aux_buf - auxiliary DMA buffer for hardware data - * - * @cpu: CPU pointer to the buffer. - * @dma: DMA address of the buffer. - * @size: Size of the buffer. - * @attrs: Attributes of the DMA mapping. - */ -struct hantro_aux_buf { - void *cpu; - dma_addr_t dma; - size_t size; - unsigned long attrs; -}; - -/* Max. number of entries in the DPB (HW limitation). */ -#define HANTRO_H264_DPB_SIZE 16 - -/** - * struct hantro_h264_dec_ctrls - * - * @decode: Decode params - * @scaling: Scaling info - * @sps: SPS info - * @pps: PPS info - */ -struct hantro_h264_dec_ctrls { - const struct v4l2_ctrl_h264_decode_params *decode; - const struct v4l2_ctrl_h264_scaling_matrix *scaling; - const struct v4l2_ctrl_h264_sps *sps; - const struct v4l2_ctrl_h264_pps *pps; -}; - -/** - * struct hantro_h264_dec_reflists - * - * @p: P reflist - * @b0: B0 reflist - * @b1: B1 reflist - */ -struct hantro_h264_dec_reflists { - struct v4l2_h264_reference p[V4L2_H264_REF_LIST_LEN]; - struct v4l2_h264_reference b0[V4L2_H264_REF_LIST_LEN]; - struct v4l2_h264_reference b1[V4L2_H264_REF_LIST_LEN]; -}; - -/** - * struct hantro_h264_dec_hw_ctx - * - * @priv: Private auxiliary buffer for hardware. - * @dpb: DPB - * @reflists: P/B0/B1 reflists - * @ctrls: V4L2 controls attached to a run - * @dpb_longterm: DPB long-term - * @dpb_valid: DPB valid - * @cur_poc: Current picture order count - */ -struct hantro_h264_dec_hw_ctx { - struct hantro_aux_buf priv; - struct v4l2_h264_dpb_entry dpb[HANTRO_H264_DPB_SIZE]; - struct hantro_h264_dec_reflists reflists; - struct hantro_h264_dec_ctrls ctrls; - u32 dpb_longterm; - u32 dpb_valid; - s32 cur_poc; -}; - -/** - * struct hantro_hevc_dec_ctrls - * @decode_params: Decode params - * @scaling: Scaling matrix - * @sps: SPS info - * @pps: PPS info - * @hevc_hdr_skip_length: the number of data (in bits) to skip in the - * slice segment header syntax after 'slice type' - * token - */ -struct hantro_hevc_dec_ctrls { - const struct v4l2_ctrl_hevc_decode_params *decode_params; - const struct v4l2_ctrl_hevc_scaling_matrix *scaling; - const struct v4l2_ctrl_hevc_sps *sps; - const struct v4l2_ctrl_hevc_pps *pps; - u32 hevc_hdr_skip_length; -}; - -/** - * struct hantro_hevc_dec_hw_ctx - * @tile_sizes: Tile sizes buffer - * @tile_filter: Tile vertical filter buffer - * @tile_sao: Tile SAO buffer - * @tile_bsd: Tile BSD control buffer - * @ref_bufs: Internal reference buffers - * @scaling_lists: Scaling lists buffer - * @ref_bufs_poc: Internal reference buffers picture order count - * @ref_bufs_used: Bitfield of used reference buffers - * @ctrls: V4L2 controls attached to a run - * @num_tile_cols_allocated: number of allocated tiles - */ -struct hantro_hevc_dec_hw_ctx { - struct hantro_aux_buf tile_sizes; - struct hantro_aux_buf tile_filter; - struct hantro_aux_buf tile_sao; - struct hantro_aux_buf tile_bsd; - struct hantro_aux_buf ref_bufs[NUM_REF_PICTURES]; - struct hantro_aux_buf scaling_lists; - s32 ref_bufs_poc[NUM_REF_PICTURES]; - u32 ref_bufs_used; - struct hantro_hevc_dec_ctrls ctrls; - unsigned int num_tile_cols_allocated; -}; - -/** - * struct hantro_mpeg2_dec_hw_ctx - * - * @qtable: Quantization table - */ -struct hantro_mpeg2_dec_hw_ctx { - struct hantro_aux_buf qtable; -}; - -/** - * struct hantro_vp8_dec_hw_ctx - * - * @segment_map: Segment map buffer. - * @prob_tbl: Probability table buffer. - */ -struct hantro_vp8_dec_hw_ctx { - struct hantro_aux_buf segment_map; - struct hantro_aux_buf prob_tbl; -}; - -/** - * struct hantro_vp9_frame_info - * - * @valid: frame info valid flag - * @frame_context_idx: index of frame context - * @reference_mode: inter prediction type - * @tx_mode: transform mode - * @interpolation_filter: filter selection for inter prediction - * @flags: frame flags - * @timestamp: frame timestamp - */ -struct hantro_vp9_frame_info { - u32 valid : 1; - u32 frame_context_idx : 2; - u32 reference_mode : 2; - u32 tx_mode : 3; - u32 interpolation_filter : 3; - u32 flags; - u64 timestamp; -}; - -#define MAX_SB_COLS 64 -#define MAX_SB_ROWS 34 - -/** - * struct hantro_vp9_dec_hw_ctx - * - * @tile_edge: auxiliary DMA buffer for tile edge processing - * @segment_map: auxiliary DMA buffer for segment map - * @misc: auxiliary DMA buffer for tile info, probabilities and hw counters - * @cnts: vp9 library struct for abstracting hw counters access - * @probability_tables: VP9 probability tables implied by the spec - * @frame_context: VP9 frame contexts - * @cur: current frame information - * @last: last frame information - * @bsd_ctrl_offset: bsd offset into tile_edge - * @segment_map_size: size of segment map - * @ctx_counters_offset: hw counters offset into misc - * @tile_info_offset: tile info offset into misc - * @tile_r_info: per-tile information array - * @tile_c_info: per-tile information array - * @last_tile_r: last number of tile rows - * @last_tile_c: last number of tile cols - * @last_sbs_r: last number of superblock rows - * @last_sbs_c: last number of superblock cols - * @active_segment: number of active segment (alternating between 0 and 1) - * @feature_enabled: segmentation feature enabled flags - * @feature_data: segmentation feature data - */ -struct hantro_vp9_dec_hw_ctx { - struct hantro_aux_buf tile_edge; - struct hantro_aux_buf segment_map; - struct hantro_aux_buf misc; - struct v4l2_vp9_frame_symbol_counts cnts; - struct v4l2_vp9_frame_context probability_tables; - struct v4l2_vp9_frame_context frame_context[4]; - struct hantro_vp9_frame_info cur; - struct hantro_vp9_frame_info last; - - unsigned int bsd_ctrl_offset; - unsigned int segment_map_size; - unsigned int ctx_counters_offset; - unsigned int tile_info_offset; - - unsigned short tile_r_info[MAX_SB_ROWS]; - unsigned short tile_c_info[MAX_SB_COLS]; - unsigned int last_tile_r; - unsigned int last_tile_c; - unsigned int last_sbs_r; - unsigned int last_sbs_c; - - unsigned int active_segment; - u8 feature_enabled[8]; - s16 feature_data[8][4]; -}; - -/** - * struct hantro_postproc_ctx - * - * @dec_q: References buffers, in decoder format. - */ -struct hantro_postproc_ctx { - struct hantro_aux_buf dec_q[VB2_MAX_FRAME]; -}; - -/** - * struct hantro_postproc_ops - post-processor operations - * - * @enable: Enable the post-processor block. Optional. - * @disable: Disable the post-processor block. Optional. - * @enum_framesizes: Enumerate possible scaled output formats. - * Returns zero if OK, a negative value in error cases. - * Optional. - */ -struct hantro_postproc_ops { - void (*enable)(struct hantro_ctx *ctx); - void (*disable)(struct hantro_ctx *ctx); - int (*enum_framesizes)(struct hantro_ctx *ctx, struct v4l2_frmsizeenum *fsize); -}; - -/** - * struct hantro_codec_ops - codec mode specific operations - * - * @init: If needed, can be used for initialization. - * Optional and called from process context. - * @exit: If needed, can be used to undo the .init phase. - * Optional and called from process context. - * @run: Start single {en,de)coding job. Called from atomic context - * to indicate that a pair of buffers is ready and the hardware - * should be programmed and started. Returns zero if OK, a - * negative value in error cases. - * @done: Read back processing results and additional data from hardware. - * @reset: Reset the hardware in case of a timeout. - */ -struct hantro_codec_ops { - int (*init)(struct hantro_ctx *ctx); - void (*exit)(struct hantro_ctx *ctx); - int (*run)(struct hantro_ctx *ctx); - void (*done)(struct hantro_ctx *ctx); - void (*reset)(struct hantro_ctx *ctx); -}; - -/** - * enum hantro_enc_fmt - source format ID for hardware registers. - * - * @ROCKCHIP_VPU_ENC_FMT_YUV420P: Y/CbCr 4:2:0 planar format - * @ROCKCHIP_VPU_ENC_FMT_YUV420SP: Y/CbCr 4:2:0 semi-planar format - * @ROCKCHIP_VPU_ENC_FMT_YUYV422: YUV 4:2:2 packed format (YUYV) - * @ROCKCHIP_VPU_ENC_FMT_UYVY422: YUV 4:2:2 packed format (UYVY) - */ -enum hantro_enc_fmt { - ROCKCHIP_VPU_ENC_FMT_YUV420P = 0, - ROCKCHIP_VPU_ENC_FMT_YUV420SP = 1, - ROCKCHIP_VPU_ENC_FMT_YUYV422 = 2, - ROCKCHIP_VPU_ENC_FMT_UYVY422 = 3, -}; - -extern const struct hantro_variant imx8mm_vpu_g1_variant; -extern const struct hantro_variant imx8mq_vpu_g1_variant; -extern const struct hantro_variant imx8mq_vpu_g2_variant; -extern const struct hantro_variant imx8mq_vpu_variant; -extern const struct hantro_variant px30_vpu_variant; -extern const struct hantro_variant rk3036_vpu_variant; -extern const struct hantro_variant rk3066_vpu_variant; -extern const struct hantro_variant rk3288_vpu_variant; -extern const struct hantro_variant rk3328_vpu_variant; -extern const struct hantro_variant rk3399_vpu_variant; -extern const struct hantro_variant rk3568_vepu_variant; -extern const struct hantro_variant rk3568_vpu_variant; -extern const struct hantro_variant sama5d4_vdec_variant; -extern const struct hantro_variant sunxi_vpu_variant; - -extern const struct hantro_postproc_ops hantro_g1_postproc_ops; -extern const struct hantro_postproc_ops hantro_g2_postproc_ops; - -extern const u32 hantro_vp8_dec_mc_filter[8][6]; - -void hantro_watchdog(struct work_struct *work); -void hantro_run(struct hantro_ctx *ctx); -void hantro_irq_done(struct hantro_dev *vpu, - enum vb2_buffer_state result); -void hantro_start_prepare_run(struct hantro_ctx *ctx); -void hantro_end_prepare_run(struct hantro_ctx *ctx); - -irqreturn_t hantro_g1_irq(int irq, void *dev_id); -void hantro_g1_reset(struct hantro_ctx *ctx); - -int hantro_h1_jpeg_enc_run(struct hantro_ctx *ctx); -int rockchip_vpu2_jpeg_enc_run(struct hantro_ctx *ctx); -void hantro_h1_jpeg_enc_done(struct hantro_ctx *ctx); -void rockchip_vpu2_jpeg_enc_done(struct hantro_ctx *ctx); - -dma_addr_t hantro_h264_get_ref_buf(struct hantro_ctx *ctx, - unsigned int dpb_idx); -u16 hantro_h264_get_ref_nbr(struct hantro_ctx *ctx, - unsigned int dpb_idx); -int hantro_h264_dec_prepare_run(struct hantro_ctx *ctx); -int rockchip_vpu2_h264_dec_run(struct hantro_ctx *ctx); -int hantro_g1_h264_dec_run(struct hantro_ctx *ctx); -int hantro_h264_dec_init(struct hantro_ctx *ctx); -void hantro_h264_dec_exit(struct hantro_ctx *ctx); - -int hantro_hevc_dec_init(struct hantro_ctx *ctx); -void hantro_hevc_dec_exit(struct hantro_ctx *ctx); -int hantro_g2_hevc_dec_run(struct hantro_ctx *ctx); -int hantro_hevc_dec_prepare_run(struct hantro_ctx *ctx); -void hantro_hevc_ref_init(struct hantro_ctx *ctx); -dma_addr_t hantro_hevc_get_ref_buf(struct hantro_ctx *ctx, s32 poc); -int hantro_hevc_add_ref_buf(struct hantro_ctx *ctx, int poc, dma_addr_t addr); - - -static inline unsigned short hantro_vp9_num_sbs(unsigned short dimension) -{ - return (dimension + 63) / 64; -} - -static inline size_t -hantro_vp9_mv_size(unsigned int width, unsigned int height) -{ - int num_ctbs; - - /* - * There can be up to (CTBs x 64) number of blocks, - * and the motion vector for each block needs 16 bytes. - */ - num_ctbs = hantro_vp9_num_sbs(width) * hantro_vp9_num_sbs(height); - return (num_ctbs * 64) * 16; -} - -static inline size_t -hantro_h264_mv_size(unsigned int width, unsigned int height) -{ - /* - * A decoded 8-bit 4:2:0 NV12 frame may need memory for up to - * 448 bytes per macroblock with additional 32 bytes on - * multi-core variants. - * - * The H264 decoder needs extra space on the output buffers - * to store motion vectors. This is needed for reference - * frames and only if the format is non-post-processed NV12. - * - * Memory layout is as follow: - * - * +---------------------------+ - * | Y-plane 256 bytes x MBs | - * +---------------------------+ - * | UV-plane 128 bytes x MBs | - * +---------------------------+ - * | MV buffer 64 bytes x MBs | - * +---------------------------+ - * | MC sync 32 bytes | - * +---------------------------+ - */ - return 64 * MB_WIDTH(width) * MB_WIDTH(height) + 32; -} - -static inline size_t -hantro_hevc_mv_size(unsigned int width, unsigned int height) -{ - /* - * A CTB can be 64x64, 32x32 or 16x16. - * Allocated memory for the "worse" case: 16x16 - */ - return width * height / 16; -} - -int hantro_g1_mpeg2_dec_run(struct hantro_ctx *ctx); -int rockchip_vpu2_mpeg2_dec_run(struct hantro_ctx *ctx); -void hantro_mpeg2_dec_copy_qtable(u8 *qtable, - const struct v4l2_ctrl_mpeg2_quantisation *ctrl); -int hantro_mpeg2_dec_init(struct hantro_ctx *ctx); -void hantro_mpeg2_dec_exit(struct hantro_ctx *ctx); - -int hantro_g1_vp8_dec_run(struct hantro_ctx *ctx); -int rockchip_vpu2_vp8_dec_run(struct hantro_ctx *ctx); -int hantro_vp8_dec_init(struct hantro_ctx *ctx); -void hantro_vp8_dec_exit(struct hantro_ctx *ctx); -void hantro_vp8_prob_update(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp8_frame *hdr); - -int hantro_g2_vp9_dec_run(struct hantro_ctx *ctx); -void hantro_g2_vp9_dec_done(struct hantro_ctx *ctx); -int hantro_vp9_dec_init(struct hantro_ctx *ctx); -void hantro_vp9_dec_exit(struct hantro_ctx *ctx); -void hantro_g2_check_idle(struct hantro_dev *vpu); -irqreturn_t hantro_g2_irq(int irq, void *dev_id); - -#endif /* HANTRO_HW_H_ */ diff --git a/drivers/staging/media/hantro/hantro_jpeg.c b/drivers/staging/media/hantro/hantro_jpeg.c deleted file mode 100644 index d07b1b449b61..000000000000 --- a/drivers/staging/media/hantro/hantro_jpeg.c +++ /dev/null @@ -1,348 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) Collabora, Ltd. - * - * Based on GSPCA and CODA drivers: - * Copyright (C) Jean-Francois Moine (http://moinejf.free.fr) - * Copyright (C) 2014 Philipp Zabel, Pengutronix - */ - -#include <linux/align.h> -#include <linux/build_bug.h> -#include <linux/kernel.h> -#include <linux/string.h> -#include "hantro_jpeg.h" -#include "hantro.h" - -#define LUMA_QUANT_OFF 25 -#define CHROMA_QUANT_OFF 90 -#define HEIGHT_OFF 159 -#define WIDTH_OFF 161 - -#define HUFF_LUMA_DC_OFF 178 -#define HUFF_LUMA_AC_OFF 211 -#define HUFF_CHROMA_DC_OFF 394 -#define HUFF_CHROMA_AC_OFF 427 - -/* Default tables from JPEG ITU-T.81 - * (ISO/IEC 10918-1) Annex K, tables K.1 and K.2 - */ -static const unsigned char luma_q_table[] = { - 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, 0x63 -}; - -static const unsigned char chroma_q_table[] = { - 0x11, 0x12, 0x18, 0x2f, 0x63, 0x63, 0x63, 0x63, - 0x12, 0x15, 0x1a, 0x42, 0x63, 0x63, 0x63, 0x63, - 0x18, 0x1a, 0x38, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x2f, 0x42, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63 -}; - -static const unsigned char zigzag[] = { - 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[] = { - 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, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, -}; - -static const unsigned char chroma_dc_table[] = { - 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, -}; - -static const unsigned char luma_ac_table[] = { - 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, - 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, - 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, - 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, - 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, - 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, - 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, - 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, - 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, - 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, - 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, - 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, - 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, - 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, - 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, - 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, - 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, - 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, - 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, - 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, - 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, - 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, - 0xf9, 0xfa, -}; - -static const unsigned char chroma_ac_table[] = { - 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, - 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, - 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, - 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, - 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, - 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, - 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, - 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, - 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, - 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, - 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, - 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, - 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, - 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, - 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, - 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, - 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, - 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, - 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, - 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, - 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, - 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, - 0xf9, 0xfa, -}; - -/* For simplicity, we keep a pre-formatted JPEG header, - * and we'll use fixed offsets to change the width, height - * quantization tables, etc. - */ -static const unsigned char hantro_jpeg_header[] = { - /* SOI */ - 0xff, 0xd8, - - /* JFIF-APP0 */ - 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, - 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, - 0x00, 0x00, - - /* DQT */ - 0xff, 0xdb, 0x00, 0x84, - - 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - /* SOF */ - 0xff, 0xc0, 0x00, 0x11, 0x08, 0x00, 0xf0, 0x01, - 0x40, 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, - 0x03, 0x11, 0x01, - - /* DHT */ - 0xff, 0xc4, 0x00, 0x1f, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - - /* DHT */ - 0xff, 0xc4, 0x00, 0xb5, 0x10, - - 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - /* DHT */ - 0xff, 0xc4, 0x00, 0x1f, 0x01, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - - /* DHT */ - 0xff, 0xc4, 0x00, 0xb5, 0x11, - - 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - /* COM */ - 0xff, 0xfe, 0x00, 0x03, 0x00, - - /* SOS */ - 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, - 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, -}; - -/* - * JPEG_HEADER_SIZE is used in other parts of the driver in lieu of - * "sizeof(hantro_jpeg_header)". The two must be equal. - */ -static_assert(sizeof(hantro_jpeg_header) == JPEG_HEADER_SIZE); - -/* - * hantro_jpeg_header is padded with a COM segment, so that the payload - * of the SOS segment (the entropy-encoded image scan), which should - * trail the whole header, is 8-byte aligned for the hardware to write - * to directly. - */ -static_assert(IS_ALIGNED(sizeof(hantro_jpeg_header), 8), - "Hantro JPEG header size needs to be 8-byte aligned."); - -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 *file_q_tab, - unsigned char *reordered_q_tab, - const unsigned char *tab, int scale) -{ - int i; - - BUILD_BUG_ON(ARRAY_SIZE(zigzag) != JPEG_QUANT_SIZE); - BUILD_BUG_ON(ARRAY_SIZE(hw_reorder) != JPEG_QUANT_SIZE); - - for (i = 0; i < JPEG_QUANT_SIZE; i++) { - file_q_tab[i] = jpeg_scale_qp(tab[zigzag[i]], scale); - reordered_q_tab[i] = jpeg_scale_qp(tab[hw_reorder[i]], scale); - } -} - -static void jpeg_set_quality(struct hantro_jpeg_ctx *ctx) -{ - int scale; - - /* - * Non-linear scaling factor: - * [5,50] -> [1000..100], [51,100] -> [98..0] - */ - if (ctx->quality < 50) - scale = 5000 / ctx->quality; - else - scale = 200 - 2 * ctx->quality; - - BUILD_BUG_ON(ARRAY_SIZE(luma_q_table) != JPEG_QUANT_SIZE); - BUILD_BUG_ON(ARRAY_SIZE(chroma_q_table) != JPEG_QUANT_SIZE); - BUILD_BUG_ON(ARRAY_SIZE(ctx->hw_luma_qtable) != JPEG_QUANT_SIZE); - BUILD_BUG_ON(ARRAY_SIZE(ctx->hw_chroma_qtable) != JPEG_QUANT_SIZE); - - jpeg_scale_quant_table(ctx->buffer + LUMA_QUANT_OFF, - ctx->hw_luma_qtable, luma_q_table, scale); - jpeg_scale_quant_table(ctx->buffer + CHROMA_QUANT_OFF, - ctx->hw_chroma_qtable, chroma_q_table, scale); -} - -void hantro_jpeg_header_assemble(struct hantro_jpeg_ctx *ctx) -{ - char *buf = ctx->buffer; - - memcpy(buf, hantro_jpeg_header, - sizeof(hantro_jpeg_header)); - - buf[HEIGHT_OFF + 0] = ctx->height >> 8; - buf[HEIGHT_OFF + 1] = ctx->height; - buf[WIDTH_OFF + 0] = ctx->width >> 8; - buf[WIDTH_OFF + 1] = ctx->width; - - memcpy(buf + HUFF_LUMA_DC_OFF, luma_dc_table, sizeof(luma_dc_table)); - memcpy(buf + HUFF_LUMA_AC_OFF, luma_ac_table, sizeof(luma_ac_table)); - memcpy(buf + HUFF_CHROMA_DC_OFF, chroma_dc_table, - sizeof(chroma_dc_table)); - memcpy(buf + HUFF_CHROMA_AC_OFF, chroma_ac_table, - sizeof(chroma_ac_table)); - - jpeg_set_quality(ctx); -} diff --git a/drivers/staging/media/hantro/hantro_jpeg.h b/drivers/staging/media/hantro/hantro_jpeg.h deleted file mode 100644 index 0b49d0b82caa..000000000000 --- a/drivers/staging/media/hantro/hantro_jpeg.h +++ /dev/null @@ -1,15 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ - -#define JPEG_HEADER_SIZE 624 -#define JPEG_QUANT_SIZE 64 - -struct hantro_jpeg_ctx { - int width; - int height; - int quality; - unsigned char *buffer; - unsigned char hw_luma_qtable[JPEG_QUANT_SIZE]; - unsigned char hw_chroma_qtable[JPEG_QUANT_SIZE]; -}; - -void hantro_jpeg_header_assemble(struct hantro_jpeg_ctx *ctx); diff --git a/drivers/staging/media/hantro/hantro_mpeg2.c b/drivers/staging/media/hantro/hantro_mpeg2.c deleted file mode 100644 index 04e545eb0a83..000000000000 --- a/drivers/staging/media/hantro/hantro_mpeg2.c +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VPU codec driver - * - * Copyright (C) 2018 Rockchip Electronics Co., Ltd. - */ - -#include "hantro.h" - -static const u8 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 -}; - -void hantro_mpeg2_dec_copy_qtable(u8 *qtable, - const struct v4l2_ctrl_mpeg2_quantisation *ctrl) -{ - int i, n; - - if (!qtable || !ctrl) - return; - - for (i = 0; i < ARRAY_SIZE(zigzag); i++) { - n = zigzag[i]; - qtable[n + 0] = ctrl->intra_quantiser_matrix[i]; - qtable[n + 64] = ctrl->non_intra_quantiser_matrix[i]; - qtable[n + 128] = ctrl->chroma_intra_quantiser_matrix[i]; - qtable[n + 192] = ctrl->chroma_non_intra_quantiser_matrix[i]; - } -} - -int hantro_mpeg2_dec_init(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - - ctx->mpeg2_dec.qtable.size = ARRAY_SIZE(zigzag) * 4; - ctx->mpeg2_dec.qtable.cpu = - dma_alloc_coherent(vpu->dev, - ctx->mpeg2_dec.qtable.size, - &ctx->mpeg2_dec.qtable.dma, - GFP_KERNEL); - if (!ctx->mpeg2_dec.qtable.cpu) - return -ENOMEM; - return 0; -} - -void hantro_mpeg2_dec_exit(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - - dma_free_coherent(vpu->dev, - ctx->mpeg2_dec.qtable.size, - ctx->mpeg2_dec.qtable.cpu, - ctx->mpeg2_dec.qtable.dma); -} diff --git a/drivers/staging/media/hantro/hantro_postproc.c b/drivers/staging/media/hantro/hantro_postproc.c deleted file mode 100644 index a0928c508434..000000000000 --- a/drivers/staging/media/hantro/hantro_postproc.c +++ /dev/null @@ -1,279 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro G1 post-processor support - * - * Copyright (C) 2019 Collabora, Ltd. - */ - -#include <linux/dma-mapping.h> -#include <linux/types.h> - -#include "hantro.h" -#include "hantro_hw.h" -#include "hantro_g1_regs.h" -#include "hantro_g2_regs.h" -#include "hantro_v4l2.h" - -#define HANTRO_PP_REG_WRITE(vpu, reg_name, val) \ -{ \ - hantro_reg_write(vpu, \ - &hantro_g1_postproc_regs.reg_name, \ - val); \ -} - -#define HANTRO_PP_REG_WRITE_S(vpu, reg_name, val) \ -{ \ - hantro_reg_write_s(vpu, \ - &hantro_g1_postproc_regs.reg_name, \ - val); \ -} - -#define VPU_PP_IN_YUYV 0x0 -#define VPU_PP_IN_NV12 0x1 -#define VPU_PP_IN_YUV420 0x2 -#define VPU_PP_IN_YUV240_TILED 0x5 -#define VPU_PP_OUT_RGB 0x0 -#define VPU_PP_OUT_YUYV 0x3 - -static const struct hantro_postproc_regs hantro_g1_postproc_regs = { - .pipeline_en = {G1_REG_PP_INTERRUPT, 1, 0x1}, - .max_burst = {G1_REG_PP_DEV_CONFIG, 0, 0x1f}, - .clk_gate = {G1_REG_PP_DEV_CONFIG, 1, 0x1}, - .out_swap32 = {G1_REG_PP_DEV_CONFIG, 5, 0x1}, - .out_endian = {G1_REG_PP_DEV_CONFIG, 6, 0x1}, - .out_luma_base = {G1_REG_PP_OUT_LUMA_BASE, 0, 0xffffffff}, - .input_width = {G1_REG_PP_INPUT_SIZE, 0, 0x1ff}, - .input_height = {G1_REG_PP_INPUT_SIZE, 9, 0x1ff}, - .output_width = {G1_REG_PP_CONTROL, 4, 0x7ff}, - .output_height = {G1_REG_PP_CONTROL, 15, 0x7ff}, - .input_fmt = {G1_REG_PP_CONTROL, 29, 0x7}, - .output_fmt = {G1_REG_PP_CONTROL, 26, 0x7}, - .orig_width = {G1_REG_PP_MASK1_ORIG_WIDTH, 23, 0x1ff}, - .display_width = {G1_REG_PP_DISPLAY_WIDTH, 0, 0xfff}, -}; - -bool hantro_needs_postproc(const struct hantro_ctx *ctx, - const struct hantro_fmt *fmt) -{ - if (ctx->is_encoder) - return false; - return fmt->postprocessed; -} - -static void hantro_postproc_g1_enable(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - struct vb2_v4l2_buffer *dst_buf; - u32 src_pp_fmt, dst_pp_fmt; - dma_addr_t dst_dma; - - /* Turn on pipeline mode. Must be done first. */ - HANTRO_PP_REG_WRITE_S(vpu, pipeline_en, 0x1); - - src_pp_fmt = VPU_PP_IN_NV12; - - switch (ctx->vpu_dst_fmt->fourcc) { - case V4L2_PIX_FMT_YUYV: - dst_pp_fmt = VPU_PP_OUT_YUYV; - break; - default: - WARN(1, "output format %d not supported by the post-processor, this wasn't expected.", - ctx->vpu_dst_fmt->fourcc); - dst_pp_fmt = 0; - break; - } - - dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); - dst_dma = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); - - HANTRO_PP_REG_WRITE(vpu, clk_gate, 0x1); - HANTRO_PP_REG_WRITE(vpu, out_endian, 0x1); - HANTRO_PP_REG_WRITE(vpu, out_swap32, 0x1); - HANTRO_PP_REG_WRITE(vpu, max_burst, 16); - HANTRO_PP_REG_WRITE(vpu, out_luma_base, dst_dma); - HANTRO_PP_REG_WRITE(vpu, input_width, MB_WIDTH(ctx->dst_fmt.width)); - HANTRO_PP_REG_WRITE(vpu, input_height, MB_HEIGHT(ctx->dst_fmt.height)); - HANTRO_PP_REG_WRITE(vpu, input_fmt, src_pp_fmt); - HANTRO_PP_REG_WRITE(vpu, output_fmt, dst_pp_fmt); - HANTRO_PP_REG_WRITE(vpu, output_width, ctx->dst_fmt.width); - HANTRO_PP_REG_WRITE(vpu, output_height, ctx->dst_fmt.height); - HANTRO_PP_REG_WRITE(vpu, orig_width, MB_WIDTH(ctx->dst_fmt.width)); - HANTRO_PP_REG_WRITE(vpu, display_width, ctx->dst_fmt.width); -} - -static int down_scale_factor(struct hantro_ctx *ctx) -{ - if (ctx->src_fmt.width == ctx->dst_fmt.width) - return 0; - - return DIV_ROUND_CLOSEST(ctx->src_fmt.width, ctx->dst_fmt.width); -} - -static void hantro_postproc_g2_enable(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - struct vb2_v4l2_buffer *dst_buf; - int down_scale = down_scale_factor(ctx); - size_t chroma_offset; - dma_addr_t dst_dma; - - dst_buf = hantro_get_dst_buf(ctx); - dst_dma = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); - chroma_offset = ctx->dst_fmt.plane_fmt[0].bytesperline * - ctx->dst_fmt.height; - - if (down_scale) { - hantro_reg_write(vpu, &g2_down_scale_e, 1); - hantro_reg_write(vpu, &g2_down_scale_y, down_scale >> 2); - hantro_reg_write(vpu, &g2_down_scale_x, down_scale >> 2); - hantro_write_addr(vpu, G2_DS_DST, dst_dma); - hantro_write_addr(vpu, G2_DS_DST_CHR, dst_dma + (chroma_offset >> down_scale)); - } else { - hantro_write_addr(vpu, G2_RS_OUT_LUMA_ADDR, dst_dma); - hantro_write_addr(vpu, G2_RS_OUT_CHROMA_ADDR, dst_dma + chroma_offset); - } - if (ctx->dev->variant->legacy_regs) { - int out_depth = hantro_get_format_depth(ctx->dst_fmt.pixelformat); - u8 pp_shift = 0; - - if (out_depth > 8) - pp_shift = 16 - out_depth; - - hantro_reg_write(ctx->dev, &g2_rs_out_bit_depth, out_depth); - hantro_reg_write(ctx->dev, &g2_pp_pix_shift, pp_shift); - } - hantro_reg_write(vpu, &g2_out_rs_e, 1); -} - -static int hantro_postproc_g2_enum_framesizes(struct hantro_ctx *ctx, - struct v4l2_frmsizeenum *fsize) -{ - /** - * G2 scaler can scale down by 0, 2, 4 or 8 - * use fsize->index has power of 2 diviser - **/ - if (fsize->index > 3) - return -EINVAL; - - if (!ctx->src_fmt.width || !ctx->src_fmt.height) - return -EINVAL; - - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - fsize->discrete.width = ctx->src_fmt.width >> fsize->index; - fsize->discrete.height = ctx->src_fmt.height >> fsize->index; - - return 0; -} - -void hantro_postproc_free(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - unsigned int i; - - for (i = 0; i < VB2_MAX_FRAME; ++i) { - struct hantro_aux_buf *priv = &ctx->postproc.dec_q[i]; - - if (priv->cpu) { - dma_free_attrs(vpu->dev, priv->size, priv->cpu, - priv->dma, priv->attrs); - priv->cpu = NULL; - } - } -} - -int hantro_postproc_alloc(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx; - struct vb2_queue *cap_queue = &m2m_ctx->cap_q_ctx.q; - unsigned int num_buffers = cap_queue->num_buffers; - struct v4l2_pix_format_mplane pix_mp; - const struct hantro_fmt *fmt; - unsigned int i, buf_size; - - /* this should always pick native format */ - fmt = hantro_get_default_fmt(ctx, false); - if (!fmt) - return -EINVAL; - v4l2_fill_pixfmt_mp(&pix_mp, fmt->fourcc, ctx->src_fmt.width, - ctx->src_fmt.height); - - buf_size = pix_mp.plane_fmt[0].sizeimage; - if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_H264_SLICE) - buf_size += hantro_h264_mv_size(pix_mp.width, - pix_mp.height); - else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_VP9_FRAME) - buf_size += hantro_vp9_mv_size(pix_mp.width, - pix_mp.height); - else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_HEVC_SLICE) - buf_size += hantro_hevc_mv_size(pix_mp.width, - pix_mp.height); - - for (i = 0; i < num_buffers; ++i) { - struct hantro_aux_buf *priv = &ctx->postproc.dec_q[i]; - - /* - * The buffers on this queue are meant as intermediate - * buffers for the decoder, so no mapping is needed. - */ - priv->attrs = DMA_ATTR_NO_KERNEL_MAPPING; - priv->cpu = dma_alloc_attrs(vpu->dev, buf_size, &priv->dma, - GFP_KERNEL, priv->attrs); - if (!priv->cpu) - return -ENOMEM; - priv->size = buf_size; - } - return 0; -} - -static void hantro_postproc_g1_disable(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - - HANTRO_PP_REG_WRITE_S(vpu, pipeline_en, 0x0); -} - -static void hantro_postproc_g2_disable(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - - hantro_reg_write(vpu, &g2_out_rs_e, 0); -} - -void hantro_postproc_disable(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - - if (vpu->variant->postproc_ops && vpu->variant->postproc_ops->disable) - vpu->variant->postproc_ops->disable(ctx); -} - -void hantro_postproc_enable(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - - if (vpu->variant->postproc_ops && vpu->variant->postproc_ops->enable) - vpu->variant->postproc_ops->enable(ctx); -} - -int hanto_postproc_enum_framesizes(struct hantro_ctx *ctx, - struct v4l2_frmsizeenum *fsize) -{ - struct hantro_dev *vpu = ctx->dev; - - if (vpu->variant->postproc_ops && vpu->variant->postproc_ops->enum_framesizes) - return vpu->variant->postproc_ops->enum_framesizes(ctx, fsize); - - return -EINVAL; -} - -const struct hantro_postproc_ops hantro_g1_postproc_ops = { - .enable = hantro_postproc_g1_enable, - .disable = hantro_postproc_g1_disable, -}; - -const struct hantro_postproc_ops hantro_g2_postproc_ops = { - .enable = hantro_postproc_g2_enable, - .disable = hantro_postproc_g2_disable, - .enum_framesizes = hantro_postproc_g2_enum_framesizes, -}; diff --git a/drivers/staging/media/hantro/hantro_v4l2.c b/drivers/staging/media/hantro/hantro_v4l2.c deleted file mode 100644 index 2c7a805289e7..000000000000 --- a/drivers/staging/media/hantro/hantro_v4l2.c +++ /dev/null @@ -1,990 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VPU codec driver - * - * Copyright (C) 2018 Collabora, Ltd. - * Copyright (C) 2018 Rockchip Electronics Co., Ltd. - * Alpha Lin <Alpha.Lin@rock-chips.com> - * Jeffy Chen <jeffy.chen@rock-chips.com> - * - * Copyright 2018 Google LLC. - * Tomasz Figa <tfiga@chromium.org> - * - * Based on s5p-mfc driver by Samsung Electronics Co., Ltd. - * Copyright (C) 2010-2011 Samsung Electronics Co., Ltd. - */ - -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/module.h> -#include <linux/pm_runtime.h> -#include <linux/videodev2.h> -#include <linux/workqueue.h> -#include <media/v4l2-ctrls.h> -#include <media/v4l2-event.h> -#include <media/v4l2-mem2mem.h> - -#include "hantro.h" -#include "hantro_hw.h" -#include "hantro_v4l2.h" - -static int hantro_set_fmt_out(struct hantro_ctx *ctx, - struct v4l2_pix_format_mplane *pix_mp); -static int hantro_set_fmt_cap(struct hantro_ctx *ctx, - struct v4l2_pix_format_mplane *pix_mp); - -static const struct hantro_fmt * -hantro_get_formats(const struct hantro_ctx *ctx, unsigned int *num_fmts) -{ - const struct hantro_fmt *formats; - - if (ctx->is_encoder) { - formats = ctx->dev->variant->enc_fmts; - *num_fmts = ctx->dev->variant->num_enc_fmts; - } else { - formats = ctx->dev->variant->dec_fmts; - *num_fmts = ctx->dev->variant->num_dec_fmts; - } - - return formats; -} - -static const struct hantro_fmt * -hantro_get_postproc_formats(const struct hantro_ctx *ctx, - unsigned int *num_fmts) -{ - struct hantro_dev *vpu = ctx->dev; - - if (ctx->is_encoder || !vpu->variant->postproc_fmts) { - *num_fmts = 0; - return NULL; - } - - *num_fmts = ctx->dev->variant->num_postproc_fmts; - return ctx->dev->variant->postproc_fmts; -} - -int hantro_get_format_depth(u32 fourcc) -{ - switch (fourcc) { - case V4L2_PIX_FMT_P010: - case V4L2_PIX_FMT_P010_4L4: - return 10; - default: - return 8; - } -} - -static bool -hantro_check_depth_match(const struct hantro_ctx *ctx, - const struct hantro_fmt *fmt) -{ - int fmt_depth, ctx_depth = 8; - - if (!fmt->match_depth && !fmt->postprocessed) - return true; - - /* 0 means default depth, which is 8 */ - if (ctx->bit_depth) - ctx_depth = ctx->bit_depth; - - fmt_depth = hantro_get_format_depth(fmt->fourcc); - - /* - * Allow only downconversion for postproc formats for now. - * It may be possible to relax that on some HW. - */ - if (!fmt->match_depth) - return fmt_depth <= ctx_depth; - - return fmt_depth == ctx_depth; -} - -static const struct hantro_fmt * -hantro_find_format(const struct hantro_ctx *ctx, u32 fourcc) -{ - const struct hantro_fmt *formats; - unsigned int i, num_fmts; - - formats = hantro_get_formats(ctx, &num_fmts); - for (i = 0; i < num_fmts; i++) - if (formats[i].fourcc == fourcc) - return &formats[i]; - - formats = hantro_get_postproc_formats(ctx, &num_fmts); - for (i = 0; i < num_fmts; i++) - if (formats[i].fourcc == fourcc) - return &formats[i]; - return NULL; -} - -const struct hantro_fmt * -hantro_get_default_fmt(const struct hantro_ctx *ctx, bool bitstream) -{ - const struct hantro_fmt *formats; - unsigned int i, num_fmts; - - formats = hantro_get_formats(ctx, &num_fmts); - for (i = 0; i < num_fmts; i++) { - if (bitstream == (formats[i].codec_mode != - HANTRO_MODE_NONE) && - hantro_check_depth_match(ctx, &formats[i])) - return &formats[i]; - } - return NULL; -} - -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct hantro_dev *vpu = video_drvdata(file); - struct video_device *vdev = video_devdata(file); - - strscpy(cap->driver, vpu->dev->driver->name, sizeof(cap->driver)); - strscpy(cap->card, vdev->name, sizeof(cap->card)); - snprintf(cap->bus_info, sizeof(cap->bus_info), "platform: %s", - vpu->dev->driver->name); - return 0; -} - -static int vidioc_enum_framesizes(struct file *file, void *priv, - struct v4l2_frmsizeenum *fsize) -{ - struct hantro_ctx *ctx = fh_to_ctx(priv); - const struct hantro_fmt *fmt; - - fmt = hantro_find_format(ctx, fsize->pixel_format); - if (!fmt) { - vpu_debug(0, "unsupported bitstream format (%08x)\n", - fsize->pixel_format); - return -EINVAL; - } - - /* For non-coded formats check if postprocessing scaling is possible */ - if (fmt->codec_mode == HANTRO_MODE_NONE && hantro_needs_postproc(ctx, fmt)) { - return hanto_postproc_enum_framesizes(ctx, fsize); - } else if (fsize->index != 0) { - vpu_debug(0, "invalid frame size index (expected 0, got %d)\n", - fsize->index); - return -EINVAL; - } - - fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; - fsize->stepwise = fmt->frmsize; - - return 0; -} - -static int vidioc_enum_fmt(struct file *file, void *priv, - struct v4l2_fmtdesc *f, bool capture) - -{ - struct hantro_ctx *ctx = fh_to_ctx(priv); - const struct hantro_fmt *fmt, *formats; - unsigned int num_fmts, i, j = 0; - bool skip_mode_none; - - /* - * When dealing with an encoder: - * - on the capture side we want to filter out all MODE_NONE formats. - * - on the output side we want to filter out all formats that are - * not MODE_NONE. - * When dealing with a decoder: - * - on the capture side we want to filter out all formats that are - * not MODE_NONE. - * - on the output side we want to filter out all MODE_NONE formats. - */ - skip_mode_none = capture == ctx->is_encoder; - - formats = hantro_get_formats(ctx, &num_fmts); - for (i = 0; i < num_fmts; i++) { - bool mode_none = formats[i].codec_mode == HANTRO_MODE_NONE; - fmt = &formats[i]; - - if (skip_mode_none == mode_none) - continue; - if (!hantro_check_depth_match(ctx, fmt)) - continue; - if (j == f->index) { - f->pixelformat = fmt->fourcc; - return 0; - } - ++j; - } - - /* - * Enumerate post-processed formats. As per the specification, - * we enumerated these formats after natively decoded formats - * as a hint for applications on what's the preferred fomat. - */ - if (!capture) - return -EINVAL; - formats = hantro_get_postproc_formats(ctx, &num_fmts); - for (i = 0; i < num_fmts; i++) { - fmt = &formats[i]; - - if (!hantro_check_depth_match(ctx, fmt)) - continue; - if (j == f->index) { - f->pixelformat = fmt->fourcc; - return 0; - } - ++j; - } - - return -EINVAL; -} - -static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - return vidioc_enum_fmt(file, priv, f, true); -} - -static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - return vidioc_enum_fmt(file, priv, f, false); -} - -static int vidioc_g_fmt_out_mplane(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; - struct hantro_ctx *ctx = fh_to_ctx(priv); - - vpu_debug(4, "f->type = %d\n", f->type); - - *pix_mp = ctx->src_fmt; - - return 0; -} - -static int vidioc_g_fmt_cap_mplane(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; - struct hantro_ctx *ctx = fh_to_ctx(priv); - - vpu_debug(4, "f->type = %d\n", f->type); - - *pix_mp = ctx->dst_fmt; - - return 0; -} - -static int hantro_try_fmt(const struct hantro_ctx *ctx, - struct v4l2_pix_format_mplane *pix_mp, - enum v4l2_buf_type type) -{ - const struct hantro_fmt *fmt, *vpu_fmt; - bool capture = V4L2_TYPE_IS_CAPTURE(type); - bool coded; - - coded = capture == ctx->is_encoder; - - vpu_debug(4, "trying format %c%c%c%c\n", - (pix_mp->pixelformat & 0x7f), - (pix_mp->pixelformat >> 8) & 0x7f, - (pix_mp->pixelformat >> 16) & 0x7f, - (pix_mp->pixelformat >> 24) & 0x7f); - - fmt = hantro_find_format(ctx, pix_mp->pixelformat); - if (!fmt) { - fmt = hantro_get_default_fmt(ctx, coded); - pix_mp->pixelformat = fmt->fourcc; - } - - if (coded) { - pix_mp->num_planes = 1; - vpu_fmt = fmt; - } else if (ctx->is_encoder) { - vpu_fmt = ctx->vpu_dst_fmt; - } else { - vpu_fmt = fmt; - /* - * Width/height on the CAPTURE end of a decoder are ignored and - * replaced by the OUTPUT ones. - */ - pix_mp->width = ctx->src_fmt.width; - pix_mp->height = ctx->src_fmt.height; - } - - pix_mp->field = V4L2_FIELD_NONE; - - v4l2_apply_frmsize_constraints(&pix_mp->width, &pix_mp->height, - &vpu_fmt->frmsize); - - if (!coded) { - /* Fill remaining fields */ - v4l2_fill_pixfmt_mp(pix_mp, fmt->fourcc, pix_mp->width, - pix_mp->height); - if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_H264_SLICE && - !hantro_needs_postproc(ctx, fmt)) - pix_mp->plane_fmt[0].sizeimage += - hantro_h264_mv_size(pix_mp->width, - pix_mp->height); - else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_VP9_FRAME && - !hantro_needs_postproc(ctx, fmt)) - pix_mp->plane_fmt[0].sizeimage += - hantro_vp9_mv_size(pix_mp->width, - pix_mp->height); - else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_HEVC_SLICE && - !hantro_needs_postproc(ctx, fmt)) - pix_mp->plane_fmt[0].sizeimage += - hantro_hevc_mv_size(pix_mp->width, - pix_mp->height); - } else if (!pix_mp->plane_fmt[0].sizeimage) { - /* - * For coded formats the application can specify - * sizeimage. If the application passes a zero sizeimage, - * let's default to the maximum frame size. - */ - pix_mp->plane_fmt[0].sizeimage = fmt->header_size + - pix_mp->width * pix_mp->height * fmt->max_depth; - } - - return 0; -} - -static int vidioc_try_fmt_cap_mplane(struct file *file, void *priv, - struct v4l2_format *f) -{ - return hantro_try_fmt(fh_to_ctx(priv), &f->fmt.pix_mp, f->type); -} - -static int vidioc_try_fmt_out_mplane(struct file *file, void *priv, - struct v4l2_format *f) -{ - return hantro_try_fmt(fh_to_ctx(priv), &f->fmt.pix_mp, f->type); -} - -static void -hantro_reset_fmt(struct v4l2_pix_format_mplane *fmt, - const struct hantro_fmt *vpu_fmt) -{ - memset(fmt, 0, sizeof(*fmt)); - - fmt->pixelformat = vpu_fmt->fourcc; - fmt->field = V4L2_FIELD_NONE; - fmt->colorspace = V4L2_COLORSPACE_JPEG; - fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; - fmt->quantization = V4L2_QUANTIZATION_DEFAULT; - fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; -} - -static void -hantro_reset_encoded_fmt(struct hantro_ctx *ctx) -{ - const struct hantro_fmt *vpu_fmt; - struct v4l2_pix_format_mplane *fmt; - - vpu_fmt = hantro_get_default_fmt(ctx, true); - - if (ctx->is_encoder) { - ctx->vpu_dst_fmt = vpu_fmt; - fmt = &ctx->dst_fmt; - } else { - ctx->vpu_src_fmt = vpu_fmt; - fmt = &ctx->src_fmt; - } - - hantro_reset_fmt(fmt, vpu_fmt); - fmt->width = vpu_fmt->frmsize.min_width; - fmt->height = vpu_fmt->frmsize.min_height; - if (ctx->is_encoder) - hantro_set_fmt_cap(ctx, fmt); - else - hantro_set_fmt_out(ctx, fmt); -} - -static void -hantro_reset_raw_fmt(struct hantro_ctx *ctx) -{ - const struct hantro_fmt *raw_vpu_fmt; - struct v4l2_pix_format_mplane *raw_fmt, *encoded_fmt; - - raw_vpu_fmt = hantro_get_default_fmt(ctx, false); - - if (ctx->is_encoder) { - ctx->vpu_src_fmt = raw_vpu_fmt; - raw_fmt = &ctx->src_fmt; - encoded_fmt = &ctx->dst_fmt; - } else { - ctx->vpu_dst_fmt = raw_vpu_fmt; - raw_fmt = &ctx->dst_fmt; - encoded_fmt = &ctx->src_fmt; - } - - hantro_reset_fmt(raw_fmt, raw_vpu_fmt); - raw_fmt->width = encoded_fmt->width; - raw_fmt->height = encoded_fmt->height; - if (ctx->is_encoder) - hantro_set_fmt_out(ctx, raw_fmt); - else - hantro_set_fmt_cap(ctx, raw_fmt); -} - -void hantro_reset_fmts(struct hantro_ctx *ctx) -{ - hantro_reset_encoded_fmt(ctx); - hantro_reset_raw_fmt(ctx); -} - -static void -hantro_update_requires_request(struct hantro_ctx *ctx, u32 fourcc) -{ - switch (fourcc) { - case V4L2_PIX_FMT_JPEG: - ctx->fh.m2m_ctx->out_q_ctx.q.requires_requests = false; - break; - case V4L2_PIX_FMT_MPEG2_SLICE: - case V4L2_PIX_FMT_VP8_FRAME: - case V4L2_PIX_FMT_H264_SLICE: - case V4L2_PIX_FMT_HEVC_SLICE: - case V4L2_PIX_FMT_VP9_FRAME: - ctx->fh.m2m_ctx->out_q_ctx.q.requires_requests = true; - break; - default: - break; - } -} - -static void -hantro_update_requires_hold_capture_buf(struct hantro_ctx *ctx, u32 fourcc) -{ - struct vb2_queue *vq; - - vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, - V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - - switch (fourcc) { - case V4L2_PIX_FMT_JPEG: - case V4L2_PIX_FMT_MPEG2_SLICE: - case V4L2_PIX_FMT_VP8_FRAME: - case V4L2_PIX_FMT_HEVC_SLICE: - case V4L2_PIX_FMT_VP9_FRAME: - vq->subsystem_flags &= ~(VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF); - break; - case V4L2_PIX_FMT_H264_SLICE: - vq->subsystem_flags |= VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF; - break; - default: - break; - } -} - -static int hantro_set_fmt_out(struct hantro_ctx *ctx, - struct v4l2_pix_format_mplane *pix_mp) -{ - struct vb2_queue *vq; - int ret; - - vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, - V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - ret = hantro_try_fmt(ctx, pix_mp, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - if (ret) - return ret; - - if (!ctx->is_encoder) { - struct vb2_queue *peer_vq; - - /* - * In order to support dynamic resolution change, - * the decoder admits a resolution change, as long - * as the pixelformat remains. Can't be done if streaming. - */ - if (vb2_is_streaming(vq) || (vb2_is_busy(vq) && - pix_mp->pixelformat != ctx->src_fmt.pixelformat)) - return -EBUSY; - /* - * Since format change on the OUTPUT queue will reset - * the CAPTURE queue, we can't allow doing so - * when the CAPTURE queue has buffers allocated. - */ - peer_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, - V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); - if (vb2_is_busy(peer_vq)) - return -EBUSY; - } else { - /* - * The encoder doesn't admit a format change if - * there are OUTPUT buffers allocated. - */ - if (vb2_is_busy(vq)) - return -EBUSY; - } - - ctx->vpu_src_fmt = hantro_find_format(ctx, pix_mp->pixelformat); - ctx->src_fmt = *pix_mp; - - /* - * Current raw format might have become invalid with newly - * selected codec, so reset it to default just to be safe and - * keep internal driver state sane. User is mandated to set - * the raw format again after we return, so we don't need - * anything smarter. - * Note that hantro_reset_raw_fmt() also propagates size - * changes to the raw format. - */ - if (!ctx->is_encoder) - hantro_reset_raw_fmt(ctx); - - /* Colorimetry information are always propagated. */ - ctx->dst_fmt.colorspace = pix_mp->colorspace; - ctx->dst_fmt.ycbcr_enc = pix_mp->ycbcr_enc; - ctx->dst_fmt.xfer_func = pix_mp->xfer_func; - ctx->dst_fmt.quantization = pix_mp->quantization; - - hantro_update_requires_request(ctx, pix_mp->pixelformat); - hantro_update_requires_hold_capture_buf(ctx, pix_mp->pixelformat); - - vpu_debug(0, "OUTPUT codec mode: %d\n", ctx->vpu_src_fmt->codec_mode); - vpu_debug(0, "fmt - w: %d, h: %d\n", - pix_mp->width, pix_mp->height); - return 0; -} - -static int hantro_set_fmt_cap(struct hantro_ctx *ctx, - struct v4l2_pix_format_mplane *pix_mp) -{ - struct vb2_queue *vq; - int ret; - - /* Change not allowed if queue is busy. */ - vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, - V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); - if (vb2_is_busy(vq)) - return -EBUSY; - - if (ctx->is_encoder) { - struct vb2_queue *peer_vq; - - /* - * Since format change on the CAPTURE queue will reset - * the OUTPUT queue, we can't allow doing so - * when the OUTPUT queue has buffers allocated. - */ - peer_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, - V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - if (vb2_is_busy(peer_vq) && - (pix_mp->pixelformat != ctx->dst_fmt.pixelformat || - pix_mp->height != ctx->dst_fmt.height || - pix_mp->width != ctx->dst_fmt.width)) - return -EBUSY; - } - - ret = hantro_try_fmt(ctx, pix_mp, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); - if (ret) - return ret; - - ctx->vpu_dst_fmt = hantro_find_format(ctx, pix_mp->pixelformat); - ctx->dst_fmt = *pix_mp; - - /* - * Current raw format might have become invalid with newly - * selected codec, so reset it to default just to be safe and - * keep internal driver state sane. User is mandated to set - * the raw format again after we return, so we don't need - * anything smarter. - * Note that hantro_reset_raw_fmt() also propagates size - * changes to the raw format. - */ - if (ctx->is_encoder) - hantro_reset_raw_fmt(ctx); - - /* Colorimetry information are always propagated. */ - ctx->src_fmt.colorspace = pix_mp->colorspace; - ctx->src_fmt.ycbcr_enc = pix_mp->ycbcr_enc; - ctx->src_fmt.xfer_func = pix_mp->xfer_func; - ctx->src_fmt.quantization = pix_mp->quantization; - - vpu_debug(0, "CAPTURE codec mode: %d\n", ctx->vpu_dst_fmt->codec_mode); - vpu_debug(0, "fmt - w: %d, h: %d\n", - pix_mp->width, pix_mp->height); - - hantro_update_requires_request(ctx, pix_mp->pixelformat); - - return 0; -} - -static int -vidioc_s_fmt_out_mplane(struct file *file, void *priv, struct v4l2_format *f) -{ - return hantro_set_fmt_out(fh_to_ctx(priv), &f->fmt.pix_mp); -} - -static int -vidioc_s_fmt_cap_mplane(struct file *file, void *priv, struct v4l2_format *f) -{ - return hantro_set_fmt_cap(fh_to_ctx(priv), &f->fmt.pix_mp); -} - -static int vidioc_g_selection(struct file *file, void *priv, - struct v4l2_selection *sel) -{ - struct hantro_ctx *ctx = fh_to_ctx(priv); - - /* Crop only supported on source. */ - if (!ctx->is_encoder || - sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) - return -EINVAL; - - switch (sel->target) { - case V4L2_SEL_TGT_CROP_DEFAULT: - case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.top = 0; - sel->r.left = 0; - sel->r.width = ctx->src_fmt.width; - sel->r.height = ctx->src_fmt.height; - break; - case V4L2_SEL_TGT_CROP: - sel->r.top = 0; - sel->r.left = 0; - sel->r.width = ctx->dst_fmt.width; - sel->r.height = ctx->dst_fmt.height; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int vidioc_s_selection(struct file *file, void *priv, - struct v4l2_selection *sel) -{ - struct hantro_ctx *ctx = fh_to_ctx(priv); - struct v4l2_rect *rect = &sel->r; - struct vb2_queue *vq; - - /* Crop only supported on source. */ - if (!ctx->is_encoder || - sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) - return -EINVAL; - - /* Change not allowed if the queue is streaming. */ - vq = v4l2_m2m_get_src_vq(ctx->fh.m2m_ctx); - if (vb2_is_streaming(vq)) - return -EBUSY; - - if (sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - /* - * We do not support offsets, and we can crop only inside - * right-most or bottom-most macroblocks. - */ - if (rect->left != 0 || rect->top != 0 || - round_up(rect->width, MB_DIM) != ctx->src_fmt.width || - round_up(rect->height, MB_DIM) != ctx->src_fmt.height) { - /* Default to full frame for incorrect settings. */ - rect->left = 0; - rect->top = 0; - rect->width = ctx->src_fmt.width; - rect->height = ctx->src_fmt.height; - } else { - /* We support widths aligned to 4 pixels and arbitrary heights. */ - rect->width = round_up(rect->width, 4); - } - - ctx->dst_fmt.width = rect->width; - ctx->dst_fmt.height = rect->height; - - return 0; -} - -static const struct v4l2_event hantro_eos_event = { - .type = V4L2_EVENT_EOS -}; - -static int vidioc_encoder_cmd(struct file *file, void *priv, - struct v4l2_encoder_cmd *ec) -{ - struct hantro_ctx *ctx = fh_to_ctx(priv); - int ret; - - ret = v4l2_m2m_ioctl_try_encoder_cmd(file, priv, ec); - if (ret < 0) - return ret; - - if (!vb2_is_streaming(v4l2_m2m_get_src_vq(ctx->fh.m2m_ctx)) || - !vb2_is_streaming(v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx))) - return 0; - - ret = v4l2_m2m_ioctl_encoder_cmd(file, priv, ec); - if (ret < 0) - return ret; - - if (ec->cmd == V4L2_ENC_CMD_STOP && - v4l2_m2m_has_stopped(ctx->fh.m2m_ctx)) - v4l2_event_queue_fh(&ctx->fh, &hantro_eos_event); - - if (ec->cmd == V4L2_ENC_CMD_START) - vb2_clear_last_buffer_dequeued(&ctx->fh.m2m_ctx->cap_q_ctx.q); - - return 0; -} - -const struct v4l2_ioctl_ops hantro_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_enum_framesizes = vidioc_enum_framesizes, - - .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_cap_mplane, - .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt_out_mplane, - .vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt_out_mplane, - .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt_cap_mplane, - .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt_out_mplane, - .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt_cap_mplane, - .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - - .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, - .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, - .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, - .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, - .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, - .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, - .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, - - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, - - .vidioc_streamon = v4l2_m2m_ioctl_streamon, - .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, - - .vidioc_g_selection = vidioc_g_selection, - .vidioc_s_selection = vidioc_s_selection, - - .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd, - .vidioc_encoder_cmd = vidioc_encoder_cmd, -}; - -static int -hantro_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers, - unsigned int *num_planes, unsigned int sizes[], - struct device *alloc_devs[]) -{ - struct hantro_ctx *ctx = vb2_get_drv_priv(vq); - struct v4l2_pix_format_mplane *pixfmt; - int i; - - switch (vq->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - pixfmt = &ctx->dst_fmt; - break; - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - pixfmt = &ctx->src_fmt; - break; - default: - vpu_err("invalid queue type: %d\n", vq->type); - return -EINVAL; - } - - if (*num_planes) { - if (*num_planes != pixfmt->num_planes) - return -EINVAL; - for (i = 0; i < pixfmt->num_planes; ++i) - if (sizes[i] < pixfmt->plane_fmt[i].sizeimage) - return -EINVAL; - return 0; - } - - *num_planes = pixfmt->num_planes; - for (i = 0; i < pixfmt->num_planes; ++i) - sizes[i] = pixfmt->plane_fmt[i].sizeimage; - return 0; -} - -static int -hantro_buf_plane_check(struct vb2_buffer *vb, - struct v4l2_pix_format_mplane *pixfmt) -{ - unsigned int sz; - int i; - - for (i = 0; i < pixfmt->num_planes; ++i) { - sz = pixfmt->plane_fmt[i].sizeimage; - vpu_debug(4, "plane %d size: %ld, sizeimage: %u\n", - i, vb2_plane_size(vb, i), sz); - if (vb2_plane_size(vb, i) < sz) { - vpu_err("plane %d is too small for output\n", i); - return -EINVAL; - } - } - return 0; -} - -static int hantro_buf_prepare(struct vb2_buffer *vb) -{ - struct vb2_queue *vq = vb->vb2_queue; - struct hantro_ctx *ctx = vb2_get_drv_priv(vq); - struct v4l2_pix_format_mplane *pix_fmt; - int ret; - - if (V4L2_TYPE_IS_OUTPUT(vq->type)) - pix_fmt = &ctx->src_fmt; - else - pix_fmt = &ctx->dst_fmt; - ret = hantro_buf_plane_check(vb, pix_fmt); - if (ret) - return ret; - /* - * Buffer's bytesused must be written by driver for CAPTURE buffers. - * (for OUTPUT buffers, if userspace passes 0 bytesused, v4l2-core sets - * it to buffer length). - */ - if (V4L2_TYPE_IS_CAPTURE(vq->type)) { - if (ctx->is_encoder) - vb2_set_plane_payload(vb, 0, 0); - else - vb2_set_plane_payload(vb, 0, pix_fmt->plane_fmt[0].sizeimage); - } - - return 0; -} - -static void hantro_buf_queue(struct vb2_buffer *vb) -{ - struct hantro_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - - if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type) && - vb2_is_streaming(vb->vb2_queue) && - v4l2_m2m_dst_buf_is_last(ctx->fh.m2m_ctx)) { - unsigned int i; - - for (i = 0; i < vb->num_planes; i++) - vb2_set_plane_payload(vb, i, 0); - - vbuf->field = V4L2_FIELD_NONE; - vbuf->sequence = ctx->sequence_cap++; - - v4l2_m2m_last_buffer_done(ctx->fh.m2m_ctx, vbuf); - v4l2_event_queue_fh(&ctx->fh, &hantro_eos_event); - return; - } - - v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); -} - -static bool hantro_vq_is_coded(struct vb2_queue *q) -{ - struct hantro_ctx *ctx = vb2_get_drv_priv(q); - - return ctx->is_encoder != V4L2_TYPE_IS_OUTPUT(q->type); -} - -static int hantro_start_streaming(struct vb2_queue *q, unsigned int count) -{ - struct hantro_ctx *ctx = vb2_get_drv_priv(q); - int ret = 0; - - v4l2_m2m_update_start_streaming_state(ctx->fh.m2m_ctx, q); - - if (V4L2_TYPE_IS_OUTPUT(q->type)) - ctx->sequence_out = 0; - else - ctx->sequence_cap = 0; - - if (hantro_vq_is_coded(q)) { - enum hantro_codec_mode codec_mode; - - if (V4L2_TYPE_IS_OUTPUT(q->type)) - codec_mode = ctx->vpu_src_fmt->codec_mode; - else - codec_mode = ctx->vpu_dst_fmt->codec_mode; - - vpu_debug(4, "Codec mode = %d\n", codec_mode); - ctx->codec_ops = &ctx->dev->variant->codec_ops[codec_mode]; - if (ctx->codec_ops->init) { - ret = ctx->codec_ops->init(ctx); - if (ret) - return ret; - } - - if (hantro_needs_postproc(ctx, ctx->vpu_dst_fmt)) { - ret = hantro_postproc_alloc(ctx); - if (ret) - goto err_codec_exit; - } - } - return ret; - -err_codec_exit: - if (ctx->codec_ops->exit) - ctx->codec_ops->exit(ctx); - return ret; -} - -static void -hantro_return_bufs(struct vb2_queue *q, - struct vb2_v4l2_buffer *(*buf_remove)(struct v4l2_m2m_ctx *)) -{ - struct hantro_ctx *ctx = vb2_get_drv_priv(q); - - for (;;) { - struct vb2_v4l2_buffer *vbuf; - - vbuf = buf_remove(ctx->fh.m2m_ctx); - if (!vbuf) - break; - v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req, - &ctx->ctrl_handler); - v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); - } -} - -static void hantro_stop_streaming(struct vb2_queue *q) -{ - struct hantro_ctx *ctx = vb2_get_drv_priv(q); - - if (hantro_vq_is_coded(q)) { - hantro_postproc_free(ctx); - if (ctx->codec_ops && ctx->codec_ops->exit) - ctx->codec_ops->exit(ctx); - } - - /* - * The mem2mem framework calls v4l2_m2m_cancel_job before - * .stop_streaming, so there isn't any job running and - * it is safe to return all the buffers. - */ - if (V4L2_TYPE_IS_OUTPUT(q->type)) - hantro_return_bufs(q, v4l2_m2m_src_buf_remove); - else - hantro_return_bufs(q, v4l2_m2m_dst_buf_remove); - - v4l2_m2m_update_stop_streaming_state(ctx->fh.m2m_ctx, q); - - if (V4L2_TYPE_IS_OUTPUT(q->type) && - v4l2_m2m_has_stopped(ctx->fh.m2m_ctx)) - v4l2_event_queue_fh(&ctx->fh, &hantro_eos_event); -} - -static void hantro_buf_request_complete(struct vb2_buffer *vb) -{ - struct hantro_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - - v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->ctrl_handler); -} - -static int hantro_buf_out_validate(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - - vbuf->field = V4L2_FIELD_NONE; - return 0; -} - -const struct vb2_ops hantro_queue_ops = { - .queue_setup = hantro_queue_setup, - .buf_prepare = hantro_buf_prepare, - .buf_queue = hantro_buf_queue, - .buf_out_validate = hantro_buf_out_validate, - .buf_request_complete = hantro_buf_request_complete, - .start_streaming = hantro_start_streaming, - .stop_streaming = hantro_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; diff --git a/drivers/staging/media/hantro/hantro_v4l2.h b/drivers/staging/media/hantro/hantro_v4l2.h deleted file mode 100644 index 64f6f57e9d7a..000000000000 --- a/drivers/staging/media/hantro/hantro_v4l2.h +++ /dev/null @@ -1,29 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Hantro VPU codec driver - * - * Copyright (C) 2018 Rockchip Electronics Co., Ltd. - * Alpha Lin <Alpha.Lin@rock-chips.com> - * Jeffy Chen <jeffy.chen@rock-chips.com> - * - * Copyright 2018 Google LLC. - * Tomasz Figa <tfiga@chromium.org> - * - * Based on s5p-mfc driver by Samsung Electronics Co., Ltd. - * Copyright (C) 2011 Samsung Electronics Co., Ltd. - */ - -#ifndef HANTRO_V4L2_H_ -#define HANTRO_V4L2_H_ - -#include "hantro.h" - -extern const struct v4l2_ioctl_ops hantro_ioctl_ops; -extern const struct vb2_ops hantro_queue_ops; - -void hantro_reset_fmts(struct hantro_ctx *ctx); -int hantro_get_format_depth(u32 fourcc); -const struct hantro_fmt * -hantro_get_default_fmt(const struct hantro_ctx *ctx, bool bitstream); - -#endif /* HANTRO_V4L2_H_ */ diff --git a/drivers/staging/media/hantro/hantro_vp8.c b/drivers/staging/media/hantro/hantro_vp8.c deleted file mode 100644 index 381bc1d3bfda..000000000000 --- a/drivers/staging/media/hantro/hantro_vp8.c +++ /dev/null @@ -1,201 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VPU codec driver - * - * Copyright (C) 2018 Rockchip Electronics Co., Ltd. - */ - -#include "hantro.h" - -/* - * probs table with packed - */ -struct vp8_prob_tbl_packed { - u8 prob_mb_skip_false; - u8 prob_intra; - u8 prob_ref_last; - u8 prob_ref_golden; - u8 prob_segment[3]; - u8 padding0; - - u8 prob_luma_16x16_pred_mode[4]; - u8 prob_chroma_pred_mode[3]; - u8 padding1; - - /* mv prob */ - u8 prob_mv_context[2][V4L2_VP8_MV_PROB_CNT]; - u8 padding2[2]; - - /* coeff probs */ - u8 prob_coeffs[4][8][3][V4L2_VP8_COEFF_PROB_CNT]; - u8 padding3[96]; -}; - -/* - * filter taps taken to 7-bit precision, - * reference RFC6386#Page-16, filters[8][6] - */ -const u32 hantro_vp8_dec_mc_filter[8][6] = { - { 0, 0, 128, 0, 0, 0 }, - { 0, -6, 123, 12, -1, 0 }, - { 2, -11, 108, 36, -8, 1 }, - { 0, -9, 93, 50, -6, 0 }, - { 3, -16, 77, 77, -16, 3 }, - { 0, -6, 50, 93, -9, 0 }, - { 1, -8, 36, 108, -11, 2 }, - { 0, -1, 12, 123, -6, 0 } -}; - -void hantro_vp8_prob_update(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp8_frame *hdr) -{ - const struct v4l2_vp8_entropy *entropy = &hdr->entropy; - u32 i, j, k; - u8 *dst; - - /* first probs */ - dst = ctx->vp8_dec.prob_tbl.cpu; - - dst[0] = hdr->prob_skip_false; - dst[1] = hdr->prob_intra; - dst[2] = hdr->prob_last; - dst[3] = hdr->prob_gf; - dst[4] = hdr->segment.segment_probs[0]; - dst[5] = hdr->segment.segment_probs[1]; - dst[6] = hdr->segment.segment_probs[2]; - dst[7] = 0; - - dst += 8; - dst[0] = entropy->y_mode_probs[0]; - dst[1] = entropy->y_mode_probs[1]; - dst[2] = entropy->y_mode_probs[2]; - dst[3] = entropy->y_mode_probs[3]; - dst[4] = entropy->uv_mode_probs[0]; - dst[5] = entropy->uv_mode_probs[1]; - dst[6] = entropy->uv_mode_probs[2]; - dst[7] = 0; /*unused */ - - /* mv probs */ - dst += 8; - dst[0] = entropy->mv_probs[0][0]; /* is short */ - dst[1] = entropy->mv_probs[1][0]; - dst[2] = entropy->mv_probs[0][1]; /* sign */ - dst[3] = entropy->mv_probs[1][1]; - dst[4] = entropy->mv_probs[0][8 + 9]; - dst[5] = entropy->mv_probs[0][9 + 9]; - dst[6] = entropy->mv_probs[1][8 + 9]; - dst[7] = entropy->mv_probs[1][9 + 9]; - dst += 8; - for (i = 0; i < 2; ++i) { - for (j = 0; j < 8; j += 4) { - dst[0] = entropy->mv_probs[i][j + 9 + 0]; - dst[1] = entropy->mv_probs[i][j + 9 + 1]; - dst[2] = entropy->mv_probs[i][j + 9 + 2]; - dst[3] = entropy->mv_probs[i][j + 9 + 3]; - dst += 4; - } - } - for (i = 0; i < 2; ++i) { - dst[0] = entropy->mv_probs[i][0 + 2]; - dst[1] = entropy->mv_probs[i][1 + 2]; - dst[2] = entropy->mv_probs[i][2 + 2]; - dst[3] = entropy->mv_probs[i][3 + 2]; - dst[4] = entropy->mv_probs[i][4 + 2]; - dst[5] = entropy->mv_probs[i][5 + 2]; - dst[6] = entropy->mv_probs[i][6 + 2]; - dst[7] = 0; /*unused */ - dst += 8; - } - - /* coeff probs (header part) */ - dst = ctx->vp8_dec.prob_tbl.cpu; - dst += (8 * 7); - for (i = 0; i < 4; ++i) { - for (j = 0; j < 8; ++j) { - for (k = 0; k < 3; ++k) { - dst[0] = entropy->coeff_probs[i][j][k][0]; - dst[1] = entropy->coeff_probs[i][j][k][1]; - dst[2] = entropy->coeff_probs[i][j][k][2]; - dst[3] = entropy->coeff_probs[i][j][k][3]; - dst += 4; - } - } - } - - /* coeff probs (footer part) */ - dst = ctx->vp8_dec.prob_tbl.cpu; - dst += (8 * 55); - for (i = 0; i < 4; ++i) { - for (j = 0; j < 8; ++j) { - for (k = 0; k < 3; ++k) { - dst[0] = entropy->coeff_probs[i][j][k][4]; - dst[1] = entropy->coeff_probs[i][j][k][5]; - dst[2] = entropy->coeff_probs[i][j][k][6]; - dst[3] = entropy->coeff_probs[i][j][k][7]; - dst[4] = entropy->coeff_probs[i][j][k][8]; - dst[5] = entropy->coeff_probs[i][j][k][9]; - dst[6] = entropy->coeff_probs[i][j][k][10]; - dst[7] = 0; /*unused */ - dst += 8; - } - } - } -} - -int hantro_vp8_dec_init(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - struct hantro_aux_buf *aux_buf; - unsigned int mb_width, mb_height; - size_t segment_map_size; - int ret; - - /* segment map table size calculation */ - mb_width = DIV_ROUND_UP(ctx->dst_fmt.width, 16); - mb_height = DIV_ROUND_UP(ctx->dst_fmt.height, 16); - segment_map_size = round_up(DIV_ROUND_UP(mb_width * mb_height, 4), 64); - - /* - * In context init the dma buffer for segment map must be allocated. - * And the data in segment map buffer must be set to all zero. - */ - aux_buf = &ctx->vp8_dec.segment_map; - aux_buf->size = segment_map_size; - aux_buf->cpu = dma_alloc_coherent(vpu->dev, aux_buf->size, - &aux_buf->dma, GFP_KERNEL); - if (!aux_buf->cpu) - return -ENOMEM; - - /* - * Allocate probability table buffer, - * total 1208 bytes, 4K page is far enough. - */ - aux_buf = &ctx->vp8_dec.prob_tbl; - aux_buf->size = sizeof(struct vp8_prob_tbl_packed); - aux_buf->cpu = dma_alloc_coherent(vpu->dev, aux_buf->size, - &aux_buf->dma, GFP_KERNEL); - if (!aux_buf->cpu) { - ret = -ENOMEM; - goto err_free_seg_map; - } - - return 0; - -err_free_seg_map: - dma_free_coherent(vpu->dev, ctx->vp8_dec.segment_map.size, - ctx->vp8_dec.segment_map.cpu, - ctx->vp8_dec.segment_map.dma); - - return ret; -} - -void hantro_vp8_dec_exit(struct hantro_ctx *ctx) -{ - struct hantro_vp8_dec_hw_ctx *vp8_dec = &ctx->vp8_dec; - struct hantro_dev *vpu = ctx->dev; - - dma_free_coherent(vpu->dev, vp8_dec->segment_map.size, - vp8_dec->segment_map.cpu, vp8_dec->segment_map.dma); - dma_free_coherent(vpu->dev, vp8_dec->prob_tbl.size, - vp8_dec->prob_tbl.cpu, vp8_dec->prob_tbl.dma); -} diff --git a/drivers/staging/media/hantro/hantro_vp9.c b/drivers/staging/media/hantro/hantro_vp9.c deleted file mode 100644 index 566cd376c097..000000000000 --- a/drivers/staging/media/hantro/hantro_vp9.c +++ /dev/null @@ -1,240 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VP9 codec driver - * - * Copyright (C) 2021 Collabora Ltd. - */ - -#include <linux/types.h> -#include <media/v4l2-mem2mem.h> - -#include "hantro.h" -#include "hantro_hw.h" -#include "hantro_vp9.h" - -#define POW2(x) (1 << (x)) - -#define MAX_LOG2_TILE_COLUMNS 6 -#define MAX_NUM_TILE_COLS POW2(MAX_LOG2_TILE_COLUMNS) -#define MAX_TILE_COLS 20 -#define MAX_TILE_ROWS 22 - -static size_t hantro_vp9_tile_filter_size(unsigned int height) -{ - u32 h, height32, size; - - h = roundup(height, 8); - - height32 = roundup(h, 64); - size = 24 * height32 * (MAX_NUM_TILE_COLS - 1); /* luma: 8, chroma: 8 + 8 */ - - return size; -} - -static size_t hantro_vp9_bsd_control_size(unsigned int height) -{ - u32 h, height32; - - h = roundup(height, 8); - height32 = roundup(h, 64); - - return 16 * (height32 / 4) * (MAX_NUM_TILE_COLS - 1); -} - -static size_t hantro_vp9_segment_map_size(unsigned int width, unsigned int height) -{ - u32 w, h; - int num_ctbs; - - w = roundup(width, 8); - h = roundup(height, 8); - num_ctbs = ((w + 63) / 64) * ((h + 63) / 64); - - return num_ctbs * 32; -} - -static inline size_t hantro_vp9_prob_tab_size(void) -{ - return roundup(sizeof(struct hantro_g2_all_probs), 16); -} - -static inline size_t hantro_vp9_count_tab_size(void) -{ - return roundup(sizeof(struct symbol_counts), 16); -} - -static inline size_t hantro_vp9_tile_info_size(void) -{ - return roundup((MAX_TILE_COLS * MAX_TILE_ROWS * 4 * sizeof(u16) + 15 + 16) & ~0xf, 16); -} - -static void *get_coeffs_arr(struct symbol_counts *cnts, int i, int j, int k, int l, int m) -{ - if (i == 0) - return &cnts->count_coeffs[j][k][l][m]; - - if (i == 1) - return &cnts->count_coeffs8x8[j][k][l][m]; - - if (i == 2) - return &cnts->count_coeffs16x16[j][k][l][m]; - - if (i == 3) - return &cnts->count_coeffs32x32[j][k][l][m]; - - return NULL; -} - -static void *get_eobs1(struct symbol_counts *cnts, int i, int j, int k, int l, int m) -{ - if (i == 0) - return &cnts->count_coeffs[j][k][l][m][3]; - - if (i == 1) - return &cnts->count_coeffs8x8[j][k][l][m][3]; - - if (i == 2) - return &cnts->count_coeffs16x16[j][k][l][m][3]; - - if (i == 3) - return &cnts->count_coeffs32x32[j][k][l][m][3]; - - return NULL; -} - -#define INNER_LOOP \ - do { \ - for (m = 0; m < ARRAY_SIZE(vp9_ctx->cnts.coeff[i][0][0][0]); ++m) { \ - vp9_ctx->cnts.coeff[i][j][k][l][m] = \ - get_coeffs_arr(cnts, i, j, k, l, m); \ - vp9_ctx->cnts.eob[i][j][k][l][m][0] = \ - &cnts->count_eobs[i][j][k][l][m]; \ - vp9_ctx->cnts.eob[i][j][k][l][m][1] = \ - get_eobs1(cnts, i, j, k, l, m); \ - } \ - } while (0) - -static void init_v4l2_vp9_count_tbl(struct hantro_ctx *ctx) -{ - struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec; - struct symbol_counts *cnts = vp9_ctx->misc.cpu + vp9_ctx->ctx_counters_offset; - int i, j, k, l, m; - - vp9_ctx->cnts.partition = &cnts->partition_counts; - vp9_ctx->cnts.skip = &cnts->mbskip_count; - vp9_ctx->cnts.intra_inter = &cnts->intra_inter_count; - vp9_ctx->cnts.tx32p = &cnts->tx32x32_count; - /* - * g2 hardware uses tx16x16_count[2][3], while the api - * expects tx16p[2][4], so this must be explicitly copied - * into vp9_ctx->cnts.tx16p when passing the data to the - * vp9 library function - */ - vp9_ctx->cnts.tx8p = &cnts->tx8x8_count; - - vp9_ctx->cnts.y_mode = &cnts->sb_ymode_counts; - vp9_ctx->cnts.uv_mode = &cnts->uv_mode_counts; - vp9_ctx->cnts.comp = &cnts->comp_inter_count; - vp9_ctx->cnts.comp_ref = &cnts->comp_ref_count; - vp9_ctx->cnts.single_ref = &cnts->single_ref_count; - vp9_ctx->cnts.filter = &cnts->switchable_interp_counts; - vp9_ctx->cnts.mv_joint = &cnts->mv_counts.joints; - vp9_ctx->cnts.sign = &cnts->mv_counts.sign; - vp9_ctx->cnts.classes = &cnts->mv_counts.classes; - vp9_ctx->cnts.class0 = &cnts->mv_counts.class0; - vp9_ctx->cnts.bits = &cnts->mv_counts.bits; - vp9_ctx->cnts.class0_fp = &cnts->mv_counts.class0_fp; - vp9_ctx->cnts.fp = &cnts->mv_counts.fp; - vp9_ctx->cnts.class0_hp = &cnts->mv_counts.class0_hp; - vp9_ctx->cnts.hp = &cnts->mv_counts.hp; - - for (i = 0; i < ARRAY_SIZE(vp9_ctx->cnts.coeff); ++i) - for (j = 0; j < ARRAY_SIZE(vp9_ctx->cnts.coeff[i]); ++j) - for (k = 0; k < ARRAY_SIZE(vp9_ctx->cnts.coeff[i][0]); ++k) - for (l = 0; l < ARRAY_SIZE(vp9_ctx->cnts.coeff[i][0][0]); ++l) - INNER_LOOP; -} - -int hantro_vp9_dec_init(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - const struct hantro_variant *variant = vpu->variant; - struct hantro_vp9_dec_hw_ctx *vp9_dec = &ctx->vp9_dec; - struct hantro_aux_buf *tile_edge = &vp9_dec->tile_edge; - struct hantro_aux_buf *segment_map = &vp9_dec->segment_map; - struct hantro_aux_buf *misc = &vp9_dec->misc; - u32 i, max_width, max_height, size; - - if (variant->num_dec_fmts < 1) - return -EINVAL; - - for (i = 0; i < variant->num_dec_fmts; ++i) - if (variant->dec_fmts[i].fourcc == V4L2_PIX_FMT_VP9_FRAME) - break; - - if (i == variant->num_dec_fmts) - return -EINVAL; - - max_width = vpu->variant->dec_fmts[i].frmsize.max_width; - max_height = vpu->variant->dec_fmts[i].frmsize.max_height; - - size = hantro_vp9_tile_filter_size(max_height); - vp9_dec->bsd_ctrl_offset = size; - size += hantro_vp9_bsd_control_size(max_height); - - tile_edge->cpu = dma_alloc_coherent(vpu->dev, size, &tile_edge->dma, GFP_KERNEL); - if (!tile_edge->cpu) - return -ENOMEM; - - tile_edge->size = size; - memset(tile_edge->cpu, 0, size); - - size = hantro_vp9_segment_map_size(max_width, max_height); - vp9_dec->segment_map_size = size; - size *= 2; /* we need two areas of this size, used alternately */ - - segment_map->cpu = dma_alloc_coherent(vpu->dev, size, &segment_map->dma, GFP_KERNEL); - if (!segment_map->cpu) - goto err_segment_map; - - segment_map->size = size; - memset(segment_map->cpu, 0, size); - - size = hantro_vp9_prob_tab_size(); - vp9_dec->ctx_counters_offset = size; - size += hantro_vp9_count_tab_size(); - vp9_dec->tile_info_offset = size; - size += hantro_vp9_tile_info_size(); - - misc->cpu = dma_alloc_coherent(vpu->dev, size, &misc->dma, GFP_KERNEL); - if (!misc->cpu) - goto err_misc; - - misc->size = size; - memset(misc->cpu, 0, size); - - init_v4l2_vp9_count_tbl(ctx); - - return 0; - -err_misc: - dma_free_coherent(vpu->dev, segment_map->size, segment_map->cpu, segment_map->dma); - -err_segment_map: - dma_free_coherent(vpu->dev, tile_edge->size, tile_edge->cpu, tile_edge->dma); - - return -ENOMEM; -} - -void hantro_vp9_dec_exit(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - struct hantro_vp9_dec_hw_ctx *vp9_dec = &ctx->vp9_dec; - struct hantro_aux_buf *tile_edge = &vp9_dec->tile_edge; - struct hantro_aux_buf *segment_map = &vp9_dec->segment_map; - struct hantro_aux_buf *misc = &vp9_dec->misc; - - dma_free_coherent(vpu->dev, misc->size, misc->cpu, misc->dma); - dma_free_coherent(vpu->dev, segment_map->size, segment_map->cpu, segment_map->dma); - dma_free_coherent(vpu->dev, tile_edge->size, tile_edge->cpu, tile_edge->dma); -} diff --git a/drivers/staging/media/hantro/hantro_vp9.h b/drivers/staging/media/hantro/hantro_vp9.h deleted file mode 100644 index 26b69275f098..000000000000 --- a/drivers/staging/media/hantro/hantro_vp9.h +++ /dev/null @@ -1,102 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Hantro VP9 codec driver - * - * Copyright (C) 2021 Collabora Ltd. - */ - -struct hantro_g2_mv_probs { - u8 joint[3]; - u8 sign[2]; - u8 class0_bit[2][1]; - u8 fr[2][3]; - u8 class0_hp[2]; - u8 hp[2]; - u8 classes[2][10]; - u8 class0_fr[2][2][3]; - u8 bits[2][10]; -}; - -struct hantro_g2_probs { - u8 inter_mode[7][4]; - u8 is_inter[4]; - u8 uv_mode[10][8]; - u8 tx8[2][1]; - u8 tx16[2][2]; - u8 tx32[2][3]; - u8 y_mode_tail[4][1]; - u8 y_mode[4][8]; - u8 partition[2][16][4]; /* [keyframe][][], [inter][][] */ - u8 uv_mode_tail[10][1]; - u8 interp_filter[4][2]; - u8 comp_mode[5]; - u8 skip[3]; - - u8 pad1[1]; - - struct hantro_g2_mv_probs mv; - - u8 single_ref[5][2]; - u8 comp_ref[5]; - - u8 pad2[17]; - - u8 coef[4][2][2][6][6][4]; -}; - -struct hantro_g2_all_probs { - u8 kf_y_mode_prob[10][10][8]; - - u8 kf_y_mode_prob_tail[10][10][1]; - u8 ref_pred_probs[3]; - u8 mb_segment_tree_probs[7]; - u8 segment_pred_probs[3]; - u8 ref_scores[4]; - u8 prob_comppred[2]; - - u8 pad1[9]; - - u8 kf_uv_mode_prob[10][8]; - u8 kf_uv_mode_prob_tail[10][1]; - - u8 pad2[6]; - - struct hantro_g2_probs probs; -}; - -struct mv_counts { - u32 joints[4]; - u32 sign[2][2]; - u32 classes[2][11]; - u32 class0[2][2]; - u32 bits[2][10][2]; - u32 class0_fp[2][2][4]; - u32 fp[2][4]; - u32 class0_hp[2][2]; - u32 hp[2][2]; -}; - -struct symbol_counts { - u32 inter_mode_counts[7][3][2]; - u32 sb_ymode_counts[4][10]; - u32 uv_mode_counts[10][10]; - u32 partition_counts[16][4]; - u32 switchable_interp_counts[4][3]; - u32 intra_inter_count[4][2]; - u32 comp_inter_count[5][2]; - u32 single_ref_count[5][2][2]; - u32 comp_ref_count[5][2]; - u32 tx32x32_count[2][4]; - u32 tx16x16_count[2][3]; - u32 tx8x8_count[2][2]; - u32 mbskip_count[3][2]; - - struct mv_counts mv_counts; - - u32 count_coeffs[2][2][6][6][4]; - u32 count_coeffs8x8[2][2][6][6][4]; - u32 count_coeffs16x16[2][2][6][6][4]; - u32 count_coeffs32x32[2][2][6][6][4]; - - u32 count_eobs[4][2][2][6][6]; -}; diff --git a/drivers/staging/media/hantro/imx8m_vpu_hw.c b/drivers/staging/media/hantro/imx8m_vpu_hw.c deleted file mode 100644 index 77f574fdfa77..000000000000 --- a/drivers/staging/media/hantro/imx8m_vpu_hw.c +++ /dev/null @@ -1,373 +0,0 @@ -// 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" -#include "hantro_g2_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, - .postprocessed = true, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_UHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_UHD_HEIGHT, - .step_height = MB_DIM, - }, - }, -}; - -static const struct hantro_fmt imx8m_vpu_dec_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_NV12, - .codec_mode = HANTRO_MODE_NONE, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_UHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_UHD_HEIGHT, - .step_height = MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_MPEG2_SLICE, - .codec_mode = HANTRO_MODE_MPEG2_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_FHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_FHD_HEIGHT, - .step_height = MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_VP8_FRAME, - .codec_mode = HANTRO_MODE_VP8_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_UHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_UHD_HEIGHT, - .step_height = MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_H264_SLICE, - .codec_mode = HANTRO_MODE_H264_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_UHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_UHD_HEIGHT, - .step_height = MB_DIM, - }, - }, -}; - -static const struct hantro_fmt imx8m_vpu_g2_postproc_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_NV12, - .codec_mode = HANTRO_MODE_NONE, - .postprocessed = true, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_UHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_UHD_HEIGHT, - .step_height = MB_DIM, - }, - }, -}; - -static const struct hantro_fmt imx8m_vpu_g2_dec_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_NV12_4L4, - .codec_mode = HANTRO_MODE_NONE, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_UHD_WIDTH, - .step_width = TILE_MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_UHD_HEIGHT, - .step_height = TILE_MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_HEVC_SLICE, - .codec_mode = HANTRO_MODE_HEVC_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_UHD_WIDTH, - .step_width = TILE_MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_UHD_HEIGHT, - .step_height = TILE_MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_VP9_FRAME, - .codec_mode = HANTRO_MODE_VP9_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_UHD_WIDTH, - .step_width = TILE_MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_UHD_HEIGHT, - .step_height = TILE_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, state); - - return IRQ_HANDLED; -} - -static int imx8mq_vpu_hw_init(struct hantro_dev *vpu) -{ - 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, - }, -}; - -static const struct hantro_codec_ops imx8mq_vpu_g1_codec_ops[] = { - [HANTRO_MODE_MPEG2_DEC] = { - .run = hantro_g1_mpeg2_dec_run, - .init = hantro_mpeg2_dec_init, - .exit = hantro_mpeg2_dec_exit, - }, - [HANTRO_MODE_VP8_DEC] = { - .run = hantro_g1_vp8_dec_run, - .init = hantro_vp8_dec_init, - .exit = hantro_vp8_dec_exit, - }, - [HANTRO_MODE_H264_DEC] = { - .run = hantro_g1_h264_dec_run, - .init = hantro_h264_dec_init, - .exit = hantro_h264_dec_exit, - }, -}; - -static const struct hantro_codec_ops imx8mq_vpu_g2_codec_ops[] = { - [HANTRO_MODE_HEVC_DEC] = { - .run = hantro_g2_hevc_dec_run, - .init = hantro_hevc_dec_init, - .exit = hantro_hevc_dec_exit, - }, - [HANTRO_MODE_VP9_DEC] = { - .run = hantro_g2_vp9_dec_run, - .done = hantro_g2_vp9_dec_done, - .init = hantro_vp9_dec_init, - .exit = hantro_vp9_dec_exit, - }, -}; - -/* - * VPU variants. - */ - -static const struct hantro_irq imx8mq_irqs[] = { - { "g1", imx8m_vpu_g1_irq }, -}; - -static const struct hantro_irq imx8mq_g2_irqs[] = { - { "g2", hantro_g2_irq }, -}; - -static const char * const imx8mq_clk_names[] = { "g1", "g2", "bus" }; -static const char * const imx8mq_reg_names[] = { "g1", "g2", "ctrl" }; -static const char * const imx8mq_g1_clk_names[] = { "g1" }; -static const char * const imx8mq_g2_clk_names[] = { "g2" }; - -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_ops = &hantro_g1_postproc_ops, - .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) -}; - -const struct hantro_variant imx8mq_vpu_g1_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_ops = &hantro_g1_postproc_ops, - .codec = HANTRO_MPEG2_DECODER | HANTRO_VP8_DECODER | - HANTRO_H264_DECODER, - .codec_ops = imx8mq_vpu_g1_codec_ops, - .irqs = imx8mq_irqs, - .num_irqs = ARRAY_SIZE(imx8mq_irqs), - .clk_names = imx8mq_g1_clk_names, - .num_clocks = ARRAY_SIZE(imx8mq_g1_clk_names), -}; - -const struct hantro_variant imx8mq_vpu_g2_variant = { - .dec_offset = 0x0, - .dec_fmts = imx8m_vpu_g2_dec_fmts, - .num_dec_fmts = ARRAY_SIZE(imx8m_vpu_g2_dec_fmts), - .postproc_fmts = imx8m_vpu_g2_postproc_fmts, - .num_postproc_fmts = ARRAY_SIZE(imx8m_vpu_g2_postproc_fmts), - .postproc_ops = &hantro_g2_postproc_ops, - .codec = HANTRO_HEVC_DECODER | HANTRO_VP9_DECODER, - .codec_ops = imx8mq_vpu_g2_codec_ops, - .irqs = imx8mq_g2_irqs, - .num_irqs = ARRAY_SIZE(imx8mq_g2_irqs), - .clk_names = imx8mq_g2_clk_names, - .num_clocks = ARRAY_SIZE(imx8mq_g2_clk_names), -}; - -const struct hantro_variant imx8mm_vpu_g1_variant = { - .dec_fmts = imx8m_vpu_dec_fmts, - .num_dec_fmts = ARRAY_SIZE(imx8m_vpu_dec_fmts), - .codec = HANTRO_MPEG2_DECODER | HANTRO_VP8_DECODER | - HANTRO_H264_DECODER, - .codec_ops = imx8mq_vpu_g1_codec_ops, - .irqs = imx8mq_irqs, - .num_irqs = ARRAY_SIZE(imx8mq_irqs), - .clk_names = imx8mq_g1_clk_names, - .num_clocks = ARRAY_SIZE(imx8mq_g1_clk_names), -}; diff --git a/drivers/staging/media/hantro/rockchip_vpu2_hw_h264_dec.c b/drivers/staging/media/hantro/rockchip_vpu2_hw_h264_dec.c deleted file mode 100644 index 46c1a83bcc4e..000000000000 --- a/drivers/staging/media/hantro/rockchip_vpu2_hw_h264_dec.c +++ /dev/null @@ -1,491 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VPU codec driver - * - * Copyright (c) 2014 Rockchip Electronics Co., Ltd. - * Hertz Wong <hertz.wong@rock-chips.com> - * Herman Chen <herman.chen@rock-chips.com> - * - * Copyright (C) 2014 Google, Inc. - * Tomasz Figa <tfiga@chromium.org> - */ - -#include <linux/types.h> -#include <linux/sort.h> - -#include <media/v4l2-mem2mem.h> - -#include "hantro_hw.h" -#include "hantro_v4l2.h" - -#define VDPU_SWREG(nr) ((nr) * 4) - -#define VDPU_REG_DEC_OUT_BASE VDPU_SWREG(63) -#define VDPU_REG_RLC_VLC_BASE VDPU_SWREG(64) -#define VDPU_REG_QTABLE_BASE VDPU_SWREG(61) -#define VDPU_REG_DIR_MV_BASE VDPU_SWREG(62) -#define VDPU_REG_REFER_BASE(i) (VDPU_SWREG(84 + (i))) -#define VDPU_REG_DEC_E(v) ((v) ? BIT(0) : 0) - -#define VDPU_REG_DEC_ADV_PRE_DIS(v) ((v) ? BIT(11) : 0) -#define VDPU_REG_DEC_SCMD_DIS(v) ((v) ? BIT(10) : 0) -#define VDPU_REG_FILTERING_DIS(v) ((v) ? BIT(8) : 0) -#define VDPU_REG_PIC_FIXED_QUANT(v) ((v) ? BIT(7) : 0) -#define VDPU_REG_DEC_LATENCY(v) (((v) << 1) & GENMASK(6, 1)) - -#define VDPU_REG_INIT_QP(v) (((v) << 25) & GENMASK(30, 25)) -#define VDPU_REG_STREAM_LEN(v) (((v) << 0) & GENMASK(23, 0)) - -#define VDPU_REG_APF_THRESHOLD(v) (((v) << 17) & GENMASK(30, 17)) -#define VDPU_REG_STARTMB_X(v) (((v) << 8) & GENMASK(16, 8)) -#define VDPU_REG_STARTMB_Y(v) (((v) << 0) & GENMASK(7, 0)) - -#define VDPU_REG_DEC_MODE(v) (((v) << 0) & GENMASK(3, 0)) - -#define VDPU_REG_DEC_STRENDIAN_E(v) ((v) ? BIT(5) : 0) -#define VDPU_REG_DEC_STRSWAP32_E(v) ((v) ? BIT(4) : 0) -#define VDPU_REG_DEC_OUTSWAP32_E(v) ((v) ? BIT(3) : 0) -#define VDPU_REG_DEC_INSWAP32_E(v) ((v) ? BIT(2) : 0) -#define VDPU_REG_DEC_OUT_ENDIAN(v) ((v) ? BIT(1) : 0) -#define VDPU_REG_DEC_IN_ENDIAN(v) ((v) ? BIT(0) : 0) - -#define VDPU_REG_DEC_DATA_DISC_E(v) ((v) ? BIT(22) : 0) -#define VDPU_REG_DEC_MAX_BURST(v) (((v) << 16) & GENMASK(20, 16)) -#define VDPU_REG_DEC_AXI_WR_ID(v) (((v) << 8) & GENMASK(15, 8)) -#define VDPU_REG_DEC_AXI_RD_ID(v) (((v) << 0) & GENMASK(7, 0)) - -#define VDPU_REG_START_CODE_E(v) ((v) ? BIT(22) : 0) -#define VDPU_REG_CH_8PIX_ILEAV_E(v) ((v) ? BIT(21) : 0) -#define VDPU_REG_RLC_MODE_E(v) ((v) ? BIT(20) : 0) -#define VDPU_REG_PIC_INTERLACE_E(v) ((v) ? BIT(17) : 0) -#define VDPU_REG_PIC_FIELDMODE_E(v) ((v) ? BIT(16) : 0) -#define VDPU_REG_PIC_TOPFIELD_E(v) ((v) ? BIT(13) : 0) -#define VDPU_REG_WRITE_MVS_E(v) ((v) ? BIT(10) : 0) -#define VDPU_REG_SEQ_MBAFF_E(v) ((v) ? BIT(7) : 0) -#define VDPU_REG_PICORD_COUNT_E(v) ((v) ? BIT(6) : 0) -#define VDPU_REG_DEC_TIMEOUT_E(v) ((v) ? BIT(5) : 0) -#define VDPU_REG_DEC_CLK_GATE_E(v) ((v) ? BIT(4) : 0) - -#define VDPU_REG_PRED_BC_TAP_0_0(v) (((v) << 22) & GENMASK(31, 22)) -#define VDPU_REG_PRED_BC_TAP_0_1(v) (((v) << 12) & GENMASK(21, 12)) -#define VDPU_REG_PRED_BC_TAP_0_2(v) (((v) << 2) & GENMASK(11, 2)) - -#define VDPU_REG_REFBU_E(v) ((v) ? BIT(31) : 0) - -#define VDPU_REG_PINIT_RLIST_F9(v) (((v) << 25) & GENMASK(29, 25)) -#define VDPU_REG_PINIT_RLIST_F8(v) (((v) << 20) & GENMASK(24, 20)) -#define VDPU_REG_PINIT_RLIST_F7(v) (((v) << 15) & GENMASK(19, 15)) -#define VDPU_REG_PINIT_RLIST_F6(v) (((v) << 10) & GENMASK(14, 10)) -#define VDPU_REG_PINIT_RLIST_F5(v) (((v) << 5) & GENMASK(9, 5)) -#define VDPU_REG_PINIT_RLIST_F4(v) (((v) << 0) & GENMASK(4, 0)) - -#define VDPU_REG_PINIT_RLIST_F15(v) (((v) << 25) & GENMASK(29, 25)) -#define VDPU_REG_PINIT_RLIST_F14(v) (((v) << 20) & GENMASK(24, 20)) -#define VDPU_REG_PINIT_RLIST_F13(v) (((v) << 15) & GENMASK(19, 15)) -#define VDPU_REG_PINIT_RLIST_F12(v) (((v) << 10) & GENMASK(14, 10)) -#define VDPU_REG_PINIT_RLIST_F11(v) (((v) << 5) & GENMASK(9, 5)) -#define VDPU_REG_PINIT_RLIST_F10(v) (((v) << 0) & GENMASK(4, 0)) - -#define VDPU_REG_REFER1_NBR(v) (((v) << 16) & GENMASK(31, 16)) -#define VDPU_REG_REFER0_NBR(v) (((v) << 0) & GENMASK(15, 0)) - -#define VDPU_REG_REFER3_NBR(v) (((v) << 16) & GENMASK(31, 16)) -#define VDPU_REG_REFER2_NBR(v) (((v) << 0) & GENMASK(15, 0)) - -#define VDPU_REG_REFER5_NBR(v) (((v) << 16) & GENMASK(31, 16)) -#define VDPU_REG_REFER4_NBR(v) (((v) << 0) & GENMASK(15, 0)) - -#define VDPU_REG_REFER7_NBR(v) (((v) << 16) & GENMASK(31, 16)) -#define VDPU_REG_REFER6_NBR(v) (((v) << 0) & GENMASK(15, 0)) - -#define VDPU_REG_REFER9_NBR(v) (((v) << 16) & GENMASK(31, 16)) -#define VDPU_REG_REFER8_NBR(v) (((v) << 0) & GENMASK(15, 0)) - -#define VDPU_REG_REFER11_NBR(v) (((v) << 16) & GENMASK(31, 16)) -#define VDPU_REG_REFER10_NBR(v) (((v) << 0) & GENMASK(15, 0)) - -#define VDPU_REG_REFER13_NBR(v) (((v) << 16) & GENMASK(31, 16)) -#define VDPU_REG_REFER12_NBR(v) (((v) << 0) & GENMASK(15, 0)) - -#define VDPU_REG_REFER15_NBR(v) (((v) << 16) & GENMASK(31, 16)) -#define VDPU_REG_REFER14_NBR(v) (((v) << 0) & GENMASK(15, 0)) - -#define VDPU_REG_BINIT_RLIST_F5(v) (((v) << 25) & GENMASK(29, 25)) -#define VDPU_REG_BINIT_RLIST_F4(v) (((v) << 20) & GENMASK(24, 20)) -#define VDPU_REG_BINIT_RLIST_F3(v) (((v) << 15) & GENMASK(19, 15)) -#define VDPU_REG_BINIT_RLIST_F2(v) (((v) << 10) & GENMASK(14, 10)) -#define VDPU_REG_BINIT_RLIST_F1(v) (((v) << 5) & GENMASK(9, 5)) -#define VDPU_REG_BINIT_RLIST_F0(v) (((v) << 0) & GENMASK(4, 0)) - -#define VDPU_REG_BINIT_RLIST_F11(v) (((v) << 25) & GENMASK(29, 25)) -#define VDPU_REG_BINIT_RLIST_F10(v) (((v) << 20) & GENMASK(24, 20)) -#define VDPU_REG_BINIT_RLIST_F9(v) (((v) << 15) & GENMASK(19, 15)) -#define VDPU_REG_BINIT_RLIST_F8(v) (((v) << 10) & GENMASK(14, 10)) -#define VDPU_REG_BINIT_RLIST_F7(v) (((v) << 5) & GENMASK(9, 5)) -#define VDPU_REG_BINIT_RLIST_F6(v) (((v) << 0) & GENMASK(4, 0)) - -#define VDPU_REG_BINIT_RLIST_F15(v) (((v) << 15) & GENMASK(19, 15)) -#define VDPU_REG_BINIT_RLIST_F14(v) (((v) << 10) & GENMASK(14, 10)) -#define VDPU_REG_BINIT_RLIST_F13(v) (((v) << 5) & GENMASK(9, 5)) -#define VDPU_REG_BINIT_RLIST_F12(v) (((v) << 0) & GENMASK(4, 0)) - -#define VDPU_REG_BINIT_RLIST_B5(v) (((v) << 25) & GENMASK(29, 25)) -#define VDPU_REG_BINIT_RLIST_B4(v) (((v) << 20) & GENMASK(24, 20)) -#define VDPU_REG_BINIT_RLIST_B3(v) (((v) << 15) & GENMASK(19, 15)) -#define VDPU_REG_BINIT_RLIST_B2(v) (((v) << 10) & GENMASK(14, 10)) -#define VDPU_REG_BINIT_RLIST_B1(v) (((v) << 5) & GENMASK(9, 5)) -#define VDPU_REG_BINIT_RLIST_B0(v) (((v) << 0) & GENMASK(4, 0)) - -#define VDPU_REG_BINIT_RLIST_B11(v) (((v) << 25) & GENMASK(29, 25)) -#define VDPU_REG_BINIT_RLIST_B10(v) (((v) << 20) & GENMASK(24, 20)) -#define VDPU_REG_BINIT_RLIST_B9(v) (((v) << 15) & GENMASK(19, 15)) -#define VDPU_REG_BINIT_RLIST_B8(v) (((v) << 10) & GENMASK(14, 10)) -#define VDPU_REG_BINIT_RLIST_B7(v) (((v) << 5) & GENMASK(9, 5)) -#define VDPU_REG_BINIT_RLIST_B6(v) (((v) << 0) & GENMASK(4, 0)) - -#define VDPU_REG_BINIT_RLIST_B15(v) (((v) << 15) & GENMASK(19, 15)) -#define VDPU_REG_BINIT_RLIST_B14(v) (((v) << 10) & GENMASK(14, 10)) -#define VDPU_REG_BINIT_RLIST_B13(v) (((v) << 5) & GENMASK(9, 5)) -#define VDPU_REG_BINIT_RLIST_B12(v) (((v) << 0) & GENMASK(4, 0)) - -#define VDPU_REG_PINIT_RLIST_F3(v) (((v) << 15) & GENMASK(19, 15)) -#define VDPU_REG_PINIT_RLIST_F2(v) (((v) << 10) & GENMASK(14, 10)) -#define VDPU_REG_PINIT_RLIST_F1(v) (((v) << 5) & GENMASK(9, 5)) -#define VDPU_REG_PINIT_RLIST_F0(v) (((v) << 0) & GENMASK(4, 0)) - -#define VDPU_REG_REFER_LTERM_E(v) (((v) << 0) & GENMASK(31, 0)) - -#define VDPU_REG_REFER_VALID_E(v) (((v) << 0) & GENMASK(31, 0)) - -#define VDPU_REG_STRM_START_BIT(v) (((v) << 0) & GENMASK(5, 0)) - -#define VDPU_REG_CH_QP_OFFSET2(v) (((v) << 22) & GENMASK(26, 22)) -#define VDPU_REG_CH_QP_OFFSET(v) (((v) << 17) & GENMASK(21, 17)) -#define VDPU_REG_PIC_MB_HEIGHT_P(v) (((v) << 9) & GENMASK(16, 9)) -#define VDPU_REG_PIC_MB_WIDTH(v) (((v) << 0) & GENMASK(8, 0)) - -#define VDPU_REG_WEIGHT_BIPR_IDC(v) (((v) << 16) & GENMASK(17, 16)) -#define VDPU_REG_REF_FRAMES(v) (((v) << 0) & GENMASK(4, 0)) - -#define VDPU_REG_FILT_CTRL_PRES(v) ((v) ? BIT(31) : 0) -#define VDPU_REG_RDPIC_CNT_PRES(v) ((v) ? BIT(30) : 0) -#define VDPU_REG_FRAMENUM_LEN(v) (((v) << 16) & GENMASK(20, 16)) -#define VDPU_REG_FRAMENUM(v) (((v) << 0) & GENMASK(15, 0)) - -#define VDPU_REG_REFPIC_MK_LEN(v) (((v) << 16) & GENMASK(26, 16)) -#define VDPU_REG_IDR_PIC_ID(v) (((v) << 0) & GENMASK(15, 0)) - -#define VDPU_REG_PPS_ID(v) (((v) << 24) & GENMASK(31, 24)) -#define VDPU_REG_REFIDX1_ACTIVE(v) (((v) << 19) & GENMASK(23, 19)) -#define VDPU_REG_REFIDX0_ACTIVE(v) (((v) << 14) & GENMASK(18, 14)) -#define VDPU_REG_POC_LENGTH(v) (((v) << 0) & GENMASK(7, 0)) - -#define VDPU_REG_IDR_PIC_E(v) ((v) ? BIT(8) : 0) -#define VDPU_REG_DIR_8X8_INFER_E(v) ((v) ? BIT(7) : 0) -#define VDPU_REG_BLACKWHITE_E(v) ((v) ? BIT(6) : 0) -#define VDPU_REG_CABAC_E(v) ((v) ? BIT(5) : 0) -#define VDPU_REG_WEIGHT_PRED_E(v) ((v) ? BIT(4) : 0) -#define VDPU_REG_CONST_INTRA_E(v) ((v) ? BIT(3) : 0) -#define VDPU_REG_8X8TRANS_FLAG_E(v) ((v) ? BIT(2) : 0) -#define VDPU_REG_TYPE1_QUANT_E(v) ((v) ? BIT(1) : 0) -#define VDPU_REG_FIELDPIC_FLAG_E(v) ((v) ? BIT(0) : 0) - -static void set_params(struct hantro_ctx *ctx, struct vb2_v4l2_buffer *src_buf) -{ - const struct hantro_h264_dec_ctrls *ctrls = &ctx->h264_dec.ctrls; - const struct v4l2_ctrl_h264_decode_params *dec_param = ctrls->decode; - const struct v4l2_ctrl_h264_sps *sps = ctrls->sps; - const struct v4l2_ctrl_h264_pps *pps = ctrls->pps; - struct hantro_dev *vpu = ctx->dev; - u32 reg; - - reg = VDPU_REG_DEC_ADV_PRE_DIS(0) | - VDPU_REG_DEC_SCMD_DIS(0) | - VDPU_REG_FILTERING_DIS(0) | - VDPU_REG_PIC_FIXED_QUANT(0) | - VDPU_REG_DEC_LATENCY(0); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(50)); - - reg = VDPU_REG_INIT_QP(pps->pic_init_qp_minus26 + 26) | - VDPU_REG_STREAM_LEN(vb2_get_plane_payload(&src_buf->vb2_buf, 0)); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(51)); - - reg = VDPU_REG_APF_THRESHOLD(8) | - VDPU_REG_STARTMB_X(0) | - VDPU_REG_STARTMB_Y(0); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(52)); - - reg = VDPU_REG_DEC_MODE(0); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(53)); - - reg = VDPU_REG_DEC_STRENDIAN_E(1) | - VDPU_REG_DEC_STRSWAP32_E(1) | - VDPU_REG_DEC_OUTSWAP32_E(1) | - VDPU_REG_DEC_INSWAP32_E(1) | - VDPU_REG_DEC_OUT_ENDIAN(1) | - VDPU_REG_DEC_IN_ENDIAN(0); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(54)); - - reg = VDPU_REG_DEC_DATA_DISC_E(0) | - VDPU_REG_DEC_MAX_BURST(16) | - VDPU_REG_DEC_AXI_WR_ID(0) | - VDPU_REG_DEC_AXI_RD_ID(0xff); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(56)); - - reg = VDPU_REG_START_CODE_E(1) | - VDPU_REG_CH_8PIX_ILEAV_E(0) | - VDPU_REG_RLC_MODE_E(0) | - VDPU_REG_PIC_INTERLACE_E(!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY) && - (sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD || - dec_param->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC)) | - VDPU_REG_PIC_FIELDMODE_E(dec_param->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC) | - VDPU_REG_PIC_TOPFIELD_E(!(dec_param->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD)) | - VDPU_REG_WRITE_MVS_E((sps->profile_idc > 66) && dec_param->nal_ref_idc) | - VDPU_REG_SEQ_MBAFF_E(sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD) | - VDPU_REG_PICORD_COUNT_E(sps->profile_idc > 66) | - VDPU_REG_DEC_TIMEOUT_E(1) | - VDPU_REG_DEC_CLK_GATE_E(1); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(57)); - - reg = VDPU_REG_PRED_BC_TAP_0_0(1) | - VDPU_REG_PRED_BC_TAP_0_1((u32)-5) | - VDPU_REG_PRED_BC_TAP_0_2(20); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(59)); - - reg = VDPU_REG_REFBU_E(0); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(65)); - - reg = VDPU_REG_STRM_START_BIT(0); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(109)); - - reg = VDPU_REG_CH_QP_OFFSET2(pps->second_chroma_qp_index_offset) | - VDPU_REG_CH_QP_OFFSET(pps->chroma_qp_index_offset) | - VDPU_REG_PIC_MB_HEIGHT_P(MB_HEIGHT(ctx->src_fmt.height)) | - VDPU_REG_PIC_MB_WIDTH(MB_WIDTH(ctx->src_fmt.width)); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(110)); - - reg = VDPU_REG_WEIGHT_BIPR_IDC(pps->weighted_bipred_idc) | - VDPU_REG_REF_FRAMES(sps->max_num_ref_frames); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(111)); - - reg = VDPU_REG_FILT_CTRL_PRES(pps->flags & V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT) | - VDPU_REG_RDPIC_CNT_PRES(pps->flags & V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT) | - VDPU_REG_FRAMENUM_LEN(sps->log2_max_frame_num_minus4 + 4) | - VDPU_REG_FRAMENUM(dec_param->frame_num); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(112)); - - reg = VDPU_REG_REFPIC_MK_LEN(dec_param->dec_ref_pic_marking_bit_size) | - VDPU_REG_IDR_PIC_ID(dec_param->idr_pic_id); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(113)); - - reg = VDPU_REG_PPS_ID(pps->pic_parameter_set_id) | - VDPU_REG_REFIDX1_ACTIVE(pps->num_ref_idx_l1_default_active_minus1 + 1) | - VDPU_REG_REFIDX0_ACTIVE(pps->num_ref_idx_l0_default_active_minus1 + 1) | - VDPU_REG_POC_LENGTH(dec_param->pic_order_cnt_bit_size); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(114)); - - reg = VDPU_REG_IDR_PIC_E(dec_param->flags & V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC) | - VDPU_REG_DIR_8X8_INFER_E(sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE) | - VDPU_REG_BLACKWHITE_E(sps->profile_idc >= 100 && sps->chroma_format_idc == 0) | - VDPU_REG_CABAC_E(pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE) | - VDPU_REG_WEIGHT_PRED_E(pps->flags & V4L2_H264_PPS_FLAG_WEIGHTED_PRED) | - VDPU_REG_CONST_INTRA_E(pps->flags & V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED) | - VDPU_REG_8X8TRANS_FLAG_E(pps->flags & V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE) | - VDPU_REG_TYPE1_QUANT_E(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT) | - VDPU_REG_FIELDPIC_FLAG_E(!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY)); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(115)); -} - -static void set_ref(struct hantro_ctx *ctx) -{ - const struct v4l2_h264_reference *b0_reflist, *b1_reflist, *p_reflist; - struct hantro_dev *vpu = ctx->dev; - u32 reg; - int i; - - b0_reflist = ctx->h264_dec.reflists.b0; - b1_reflist = ctx->h264_dec.reflists.b1; - p_reflist = ctx->h264_dec.reflists.p; - - reg = VDPU_REG_PINIT_RLIST_F9(p_reflist[9].index) | - VDPU_REG_PINIT_RLIST_F8(p_reflist[8].index) | - VDPU_REG_PINIT_RLIST_F7(p_reflist[7].index) | - VDPU_REG_PINIT_RLIST_F6(p_reflist[6].index) | - VDPU_REG_PINIT_RLIST_F5(p_reflist[5].index) | - VDPU_REG_PINIT_RLIST_F4(p_reflist[4].index); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(74)); - - reg = VDPU_REG_PINIT_RLIST_F15(p_reflist[15].index) | - VDPU_REG_PINIT_RLIST_F14(p_reflist[14].index) | - VDPU_REG_PINIT_RLIST_F13(p_reflist[13].index) | - VDPU_REG_PINIT_RLIST_F12(p_reflist[12].index) | - VDPU_REG_PINIT_RLIST_F11(p_reflist[11].index) | - VDPU_REG_PINIT_RLIST_F10(p_reflist[10].index); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(75)); - - reg = VDPU_REG_REFER1_NBR(hantro_h264_get_ref_nbr(ctx, 1)) | - VDPU_REG_REFER0_NBR(hantro_h264_get_ref_nbr(ctx, 0)); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(76)); - - reg = VDPU_REG_REFER3_NBR(hantro_h264_get_ref_nbr(ctx, 3)) | - VDPU_REG_REFER2_NBR(hantro_h264_get_ref_nbr(ctx, 2)); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(77)); - - reg = VDPU_REG_REFER5_NBR(hantro_h264_get_ref_nbr(ctx, 5)) | - VDPU_REG_REFER4_NBR(hantro_h264_get_ref_nbr(ctx, 4)); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(78)); - - reg = VDPU_REG_REFER7_NBR(hantro_h264_get_ref_nbr(ctx, 7)) | - VDPU_REG_REFER6_NBR(hantro_h264_get_ref_nbr(ctx, 6)); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(79)); - - reg = VDPU_REG_REFER9_NBR(hantro_h264_get_ref_nbr(ctx, 9)) | - VDPU_REG_REFER8_NBR(hantro_h264_get_ref_nbr(ctx, 8)); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(80)); - - reg = VDPU_REG_REFER11_NBR(hantro_h264_get_ref_nbr(ctx, 11)) | - VDPU_REG_REFER10_NBR(hantro_h264_get_ref_nbr(ctx, 10)); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(81)); - - reg = VDPU_REG_REFER13_NBR(hantro_h264_get_ref_nbr(ctx, 13)) | - VDPU_REG_REFER12_NBR(hantro_h264_get_ref_nbr(ctx, 12)); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(82)); - - reg = VDPU_REG_REFER15_NBR(hantro_h264_get_ref_nbr(ctx, 15)) | - VDPU_REG_REFER14_NBR(hantro_h264_get_ref_nbr(ctx, 14)); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(83)); - - reg = VDPU_REG_BINIT_RLIST_F5(b0_reflist[5].index) | - VDPU_REG_BINIT_RLIST_F4(b0_reflist[4].index) | - VDPU_REG_BINIT_RLIST_F3(b0_reflist[3].index) | - VDPU_REG_BINIT_RLIST_F2(b0_reflist[2].index) | - VDPU_REG_BINIT_RLIST_F1(b0_reflist[1].index) | - VDPU_REG_BINIT_RLIST_F0(b0_reflist[0].index); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(100)); - - reg = VDPU_REG_BINIT_RLIST_F11(b0_reflist[11].index) | - VDPU_REG_BINIT_RLIST_F10(b0_reflist[10].index) | - VDPU_REG_BINIT_RLIST_F9(b0_reflist[9].index) | - VDPU_REG_BINIT_RLIST_F8(b0_reflist[8].index) | - VDPU_REG_BINIT_RLIST_F7(b0_reflist[7].index) | - VDPU_REG_BINIT_RLIST_F6(b0_reflist[6].index); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(101)); - - reg = VDPU_REG_BINIT_RLIST_F15(b0_reflist[15].index) | - VDPU_REG_BINIT_RLIST_F14(b0_reflist[14].index) | - VDPU_REG_BINIT_RLIST_F13(b0_reflist[13].index) | - VDPU_REG_BINIT_RLIST_F12(b0_reflist[12].index); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(102)); - - reg = VDPU_REG_BINIT_RLIST_B5(b1_reflist[5].index) | - VDPU_REG_BINIT_RLIST_B4(b1_reflist[4].index) | - VDPU_REG_BINIT_RLIST_B3(b1_reflist[3].index) | - VDPU_REG_BINIT_RLIST_B2(b1_reflist[2].index) | - VDPU_REG_BINIT_RLIST_B1(b1_reflist[1].index) | - VDPU_REG_BINIT_RLIST_B0(b1_reflist[0].index); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(103)); - - reg = VDPU_REG_BINIT_RLIST_B11(b1_reflist[11].index) | - VDPU_REG_BINIT_RLIST_B10(b1_reflist[10].index) | - VDPU_REG_BINIT_RLIST_B9(b1_reflist[9].index) | - VDPU_REG_BINIT_RLIST_B8(b1_reflist[8].index) | - VDPU_REG_BINIT_RLIST_B7(b1_reflist[7].index) | - VDPU_REG_BINIT_RLIST_B6(b1_reflist[6].index); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(104)); - - reg = VDPU_REG_BINIT_RLIST_B15(b1_reflist[15].index) | - VDPU_REG_BINIT_RLIST_B14(b1_reflist[14].index) | - VDPU_REG_BINIT_RLIST_B13(b1_reflist[13].index) | - VDPU_REG_BINIT_RLIST_B12(b1_reflist[12].index); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(105)); - - reg = VDPU_REG_PINIT_RLIST_F3(p_reflist[3].index) | - VDPU_REG_PINIT_RLIST_F2(p_reflist[2].index) | - VDPU_REG_PINIT_RLIST_F1(p_reflist[1].index) | - VDPU_REG_PINIT_RLIST_F0(p_reflist[0].index); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(106)); - - reg = VDPU_REG_REFER_LTERM_E(ctx->h264_dec.dpb_longterm); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(107)); - - reg = VDPU_REG_REFER_VALID_E(ctx->h264_dec.dpb_valid); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(108)); - - /* Set up addresses of DPB buffers. */ - for (i = 0; i < HANTRO_H264_DPB_SIZE; i++) { - dma_addr_t dma_addr = hantro_h264_get_ref_buf(ctx, i); - - vdpu_write_relaxed(vpu, dma_addr, VDPU_REG_REFER_BASE(i)); - } -} - -static void set_buffers(struct hantro_ctx *ctx, struct vb2_v4l2_buffer *src_buf) -{ - const struct hantro_h264_dec_ctrls *ctrls = &ctx->h264_dec.ctrls; - struct vb2_v4l2_buffer *dst_buf; - struct hantro_dev *vpu = ctx->dev; - dma_addr_t src_dma, dst_dma; - size_t offset = 0; - - /* Source (stream) buffer. */ - src_dma = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); - vdpu_write_relaxed(vpu, src_dma, VDPU_REG_RLC_VLC_BASE); - - /* Destination (decoded frame) buffer. */ - dst_buf = hantro_get_dst_buf(ctx); - dst_dma = hantro_get_dec_buf_addr(ctx, &dst_buf->vb2_buf); - /* Adjust dma addr to start at second line for bottom field */ - if (ctrls->decode->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD) - offset = ALIGN(ctx->src_fmt.width, MB_DIM); - vdpu_write_relaxed(vpu, dst_dma + offset, VDPU_REG_DEC_OUT_BASE); - - /* Higher profiles require DMV buffer appended to reference frames. */ - if (ctrls->sps->profile_idc > 66 && ctrls->decode->nal_ref_idc) { - unsigned int bytes_per_mb = 384; - - /* DMV buffer for monochrome start directly after Y-plane */ - if (ctrls->sps->profile_idc >= 100 && - ctrls->sps->chroma_format_idc == 0) - bytes_per_mb = 256; - offset = bytes_per_mb * MB_WIDTH(ctx->src_fmt.width) * - MB_HEIGHT(ctx->src_fmt.height); - - /* - * DMV buffer is split in two for field encoded frames, - * adjust offset for bottom field - */ - if (ctrls->decode->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD) - offset += 32 * MB_WIDTH(ctx->src_fmt.width) * - MB_HEIGHT(ctx->src_fmt.height); - vdpu_write_relaxed(vpu, dst_dma + offset, VDPU_REG_DIR_MV_BASE); - } - - /* Auxiliary buffer prepared in hantro_g1_h264_dec_prepare_table(). */ - vdpu_write_relaxed(vpu, ctx->h264_dec.priv.dma, VDPU_REG_QTABLE_BASE); -} - -int rockchip_vpu2_h264_dec_run(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - struct vb2_v4l2_buffer *src_buf; - u32 reg; - int ret; - - /* Prepare the H264 decoder context. */ - ret = hantro_h264_dec_prepare_run(ctx); - if (ret) - return ret; - - src_buf = hantro_get_src_buf(ctx); - set_params(ctx, src_buf); - set_ref(ctx); - set_buffers(ctx, src_buf); - - hantro_end_prepare_run(ctx); - - /* Start decoding! */ - reg = vdpu_read(vpu, VDPU_SWREG(57)) | VDPU_REG_DEC_E(1); - vdpu_write(vpu, reg, VDPU_SWREG(57)); - - return 0; -} diff --git a/drivers/staging/media/hantro/rockchip_vpu2_hw_jpeg_enc.c b/drivers/staging/media/hantro/rockchip_vpu2_hw_jpeg_enc.c deleted file mode 100644 index 8395c4d48dd0..000000000000 --- a/drivers/staging/media/hantro/rockchip_vpu2_hw_jpeg_enc.c +++ /dev/null @@ -1,197 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VPU codec driver - * - * Copyright (C) 2018 Rockchip Electronics Co., Ltd. - * - * JPEG encoder - * ------------ - * The VPU JPEG encoder produces JPEG baseline sequential format. - * The quantization coefficients are 8-bit values, complying with - * the baseline specification. Therefore, it requires - * luma and chroma quantization tables. The hardware does entropy - * encoding using internal Huffman tables, as specified in the JPEG - * specification. - * - * In other words, only the luma and chroma quantization tables are - * required for the encoding operation. - * - * 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. A special order is needed, neither - * zigzag, nor linear. - */ - -#include <asm/unaligned.h> -#include <media/v4l2-mem2mem.h> -#include "hantro_jpeg.h" -#include "hantro.h" -#include "hantro_v4l2.h" -#include "hantro_hw.h" -#include "rockchip_vpu2_regs.h" - -#define VEPU_JPEG_QUANT_TABLE_COUNT 16 - -static void rockchip_vpu2_set_src_img_ctrl(struct hantro_dev *vpu, - struct hantro_ctx *ctx) -{ - u32 overfill_r, overfill_b; - u32 reg; - - /* - * The format width and height are already macroblock aligned - * by .vidioc_s_fmt_vid_cap_mplane() callback. Destination - * format width and height can be further modified by - * .vidioc_s_selection(), and the width is 4-aligned. - */ - overfill_r = ctx->src_fmt.width - ctx->dst_fmt.width; - overfill_b = ctx->src_fmt.height - ctx->dst_fmt.height; - - reg = VEPU_REG_IN_IMG_CTRL_ROW_LEN(ctx->src_fmt.width); - vepu_write_relaxed(vpu, reg, VEPU_REG_INPUT_LUMA_INFO); - - reg = VEPU_REG_IN_IMG_CTRL_OVRFLR_D4(overfill_r / 4) | - VEPU_REG_IN_IMG_CTRL_OVRFLB(overfill_b); - /* - * This register controls the input crop, as the offset - * from the right/bottom within the last macroblock. The offset from the - * right must be divided by 4 and so the crop must be aligned to 4 pixels - * horizontally. - */ - vepu_write_relaxed(vpu, reg, VEPU_REG_ENC_OVER_FILL_STRM_OFFSET); - - reg = VEPU_REG_IN_IMG_CTRL_FMT(ctx->vpu_src_fmt->enc_fmt); - vepu_write_relaxed(vpu, reg, VEPU_REG_ENC_CTRL1); -} - -static void rockchip_vpu2_jpeg_enc_set_buffers(struct hantro_dev *vpu, - struct hantro_ctx *ctx, - struct vb2_buffer *src_buf, - struct vb2_buffer *dst_buf) -{ - struct v4l2_pix_format_mplane *pix_fmt = &ctx->src_fmt; - dma_addr_t src[3]; - u32 size_left; - - size_left = vb2_plane_size(dst_buf, 0) - ctx->vpu_dst_fmt->header_size; - if (WARN_ON(vb2_plane_size(dst_buf, 0) < ctx->vpu_dst_fmt->header_size)) - size_left = 0; - - WARN_ON(pix_fmt->num_planes > 3); - - vepu_write_relaxed(vpu, vb2_dma_contig_plane_dma_addr(dst_buf, 0) + - ctx->vpu_dst_fmt->header_size, - VEPU_REG_ADDR_OUTPUT_STREAM); - vepu_write_relaxed(vpu, size_left, VEPU_REG_STR_BUF_LIMIT); - - if (pix_fmt->num_planes == 1) { - src[0] = vb2_dma_contig_plane_dma_addr(src_buf, 0); - vepu_write_relaxed(vpu, src[0], VEPU_REG_ADDR_IN_PLANE_0); - } else if (pix_fmt->num_planes == 2) { - src[0] = vb2_dma_contig_plane_dma_addr(src_buf, 0); - src[1] = vb2_dma_contig_plane_dma_addr(src_buf, 1); - vepu_write_relaxed(vpu, src[0], VEPU_REG_ADDR_IN_PLANE_0); - vepu_write_relaxed(vpu, src[1], VEPU_REG_ADDR_IN_PLANE_1); - } else { - src[0] = vb2_dma_contig_plane_dma_addr(src_buf, 0); - src[1] = vb2_dma_contig_plane_dma_addr(src_buf, 1); - src[2] = vb2_dma_contig_plane_dma_addr(src_buf, 2); - vepu_write_relaxed(vpu, src[0], VEPU_REG_ADDR_IN_PLANE_0); - vepu_write_relaxed(vpu, src[1], VEPU_REG_ADDR_IN_PLANE_1); - vepu_write_relaxed(vpu, src[2], VEPU_REG_ADDR_IN_PLANE_2); - } -} - -static void -rockchip_vpu2_jpeg_enc_set_qtable(struct hantro_dev *vpu, - unsigned char *luma_qtable, - 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_p[i]); - vepu_write_relaxed(vpu, reg, VEPU_REG_JPEG_LUMA_QUAT(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)); - } -} - -int rockchip_vpu2_jpeg_enc_run(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - struct vb2_v4l2_buffer *src_buf, *dst_buf; - struct hantro_jpeg_ctx jpeg_ctx; - u32 reg; - - src_buf = hantro_get_src_buf(ctx); - dst_buf = hantro_get_dst_buf(ctx); - - hantro_start_prepare_run(ctx); - - memset(&jpeg_ctx, 0, sizeof(jpeg_ctx)); - jpeg_ctx.buffer = vb2_plane_vaddr(&dst_buf->vb2_buf, 0); - if (!jpeg_ctx.buffer) - return -ENOMEM; - - jpeg_ctx.width = ctx->dst_fmt.width; - jpeg_ctx.height = ctx->dst_fmt.height; - jpeg_ctx.quality = ctx->jpeg_quality; - hantro_jpeg_header_assemble(&jpeg_ctx); - - /* Switch to JPEG encoder mode before writing registers */ - vepu_write_relaxed(vpu, VEPU_REG_ENCODE_FORMAT_JPEG, - VEPU_REG_ENCODE_START); - - rockchip_vpu2_set_src_img_ctrl(vpu, ctx); - rockchip_vpu2_jpeg_enc_set_buffers(vpu, ctx, &src_buf->vb2_buf, - &dst_buf->vb2_buf); - rockchip_vpu2_jpeg_enc_set_qtable(vpu, jpeg_ctx.hw_luma_qtable, - jpeg_ctx.hw_chroma_qtable); - - reg = VEPU_REG_OUTPUT_SWAP32 - | VEPU_REG_OUTPUT_SWAP16 - | VEPU_REG_OUTPUT_SWAP8 - | VEPU_REG_INPUT_SWAP8 - | VEPU_REG_INPUT_SWAP16 - | VEPU_REG_INPUT_SWAP32; - /* Make sure that all registers are written at this point. */ - vepu_write(vpu, reg, VEPU_REG_DATA_ENDIAN); - - reg = VEPU_REG_AXI_CTRL_BURST_LEN(16); - vepu_write_relaxed(vpu, reg, VEPU_REG_AXI_CTRL); - - reg = VEPU_REG_MB_WIDTH(MB_WIDTH(ctx->src_fmt.width)) - | VEPU_REG_MB_HEIGHT(MB_HEIGHT(ctx->src_fmt.height)) - | VEPU_REG_FRAME_TYPE_INTRA - | VEPU_REG_ENCODE_FORMAT_JPEG - | VEPU_REG_ENCODE_ENABLE; - - /* Kick the watchdog and start encoding */ - hantro_end_prepare_run(ctx); - vepu_write(vpu, reg, VEPU_REG_ENCODE_START); - - return 0; -} - -void rockchip_vpu2_jpeg_enc_done(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - u32 bytesused = vepu_read(vpu, VEPU_REG_STR_BUF_LIMIT) / 8; - struct vb2_v4l2_buffer *dst_buf = hantro_get_dst_buf(ctx); - - vb2_set_plane_payload(&dst_buf->vb2_buf, 0, - ctx->vpu_dst_fmt->header_size + bytesused); -} diff --git a/drivers/staging/media/hantro/rockchip_vpu2_hw_mpeg2_dec.c b/drivers/staging/media/hantro/rockchip_vpu2_hw_mpeg2_dec.c deleted file mode 100644 index b66737fab46b..000000000000 --- a/drivers/staging/media/hantro/rockchip_vpu2_hw_mpeg2_dec.c +++ /dev/null @@ -1,248 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VPU codec driver - * - * Copyright (C) 2018 Rockchip Electronics Co., Ltd. - */ - -#include <asm/unaligned.h> -#include <linux/bitfield.h> -#include <media/v4l2-mem2mem.h> -#include "hantro.h" -#include "hantro_hw.h" - -#define VDPU_SWREG(nr) ((nr) * 4) - -#define VDPU_REG_DEC_OUT_BASE VDPU_SWREG(63) -#define VDPU_REG_RLC_VLC_BASE VDPU_SWREG(64) -#define VDPU_REG_QTABLE_BASE VDPU_SWREG(61) -#define VDPU_REG_REFER0_BASE VDPU_SWREG(131) -#define VDPU_REG_REFER2_BASE VDPU_SWREG(134) -#define VDPU_REG_REFER3_BASE VDPU_SWREG(135) -#define VDPU_REG_REFER1_BASE VDPU_SWREG(148) -#define VDPU_REG_DEC_E(v) ((v) ? BIT(0) : 0) - -#define VDPU_REG_DEC_ADV_PRE_DIS(v) ((v) ? BIT(11) : 0) -#define VDPU_REG_DEC_SCMD_DIS(v) ((v) ? BIT(10) : 0) -#define VDPU_REG_FILTERING_DIS(v) ((v) ? BIT(8) : 0) -#define VDPU_REG_DEC_LATENCY(v) (((v) << 1) & GENMASK(6, 1)) - -#define VDPU_REG_INIT_QP(v) (((v) << 25) & GENMASK(30, 25)) -#define VDPU_REG_STREAM_LEN(v) (((v) << 0) & GENMASK(23, 0)) - -#define VDPU_REG_APF_THRESHOLD(v) (((v) << 17) & GENMASK(30, 17)) -#define VDPU_REG_STARTMB_X(v) (((v) << 8) & GENMASK(16, 8)) -#define VDPU_REG_STARTMB_Y(v) (((v) << 0) & GENMASK(7, 0)) - -#define VDPU_REG_DEC_MODE(v) (((v) << 0) & GENMASK(3, 0)) - -#define VDPU_REG_DEC_STRENDIAN_E(v) ((v) ? BIT(5) : 0) -#define VDPU_REG_DEC_STRSWAP32_E(v) ((v) ? BIT(4) : 0) -#define VDPU_REG_DEC_OUTSWAP32_E(v) ((v) ? BIT(3) : 0) -#define VDPU_REG_DEC_INSWAP32_E(v) ((v) ? BIT(2) : 0) -#define VDPU_REG_DEC_OUT_ENDIAN(v) ((v) ? BIT(1) : 0) -#define VDPU_REG_DEC_IN_ENDIAN(v) ((v) ? BIT(0) : 0) - -#define VDPU_REG_DEC_DATA_DISC_E(v) ((v) ? BIT(22) : 0) -#define VDPU_REG_DEC_MAX_BURST(v) (((v) << 16) & GENMASK(20, 16)) -#define VDPU_REG_DEC_AXI_WR_ID(v) (((v) << 8) & GENMASK(15, 8)) -#define VDPU_REG_DEC_AXI_RD_ID(v) (((v) << 0) & GENMASK(7, 0)) - -#define VDPU_REG_RLC_MODE_E(v) ((v) ? BIT(20) : 0) -#define VDPU_REG_PIC_INTERLACE_E(v) ((v) ? BIT(17) : 0) -#define VDPU_REG_PIC_FIELDMODE_E(v) ((v) ? BIT(16) : 0) -#define VDPU_REG_PIC_B_E(v) ((v) ? BIT(15) : 0) -#define VDPU_REG_PIC_INTER_E(v) ((v) ? BIT(14) : 0) -#define VDPU_REG_PIC_TOPFIELD_E(v) ((v) ? BIT(13) : 0) -#define VDPU_REG_FWD_INTERLACE_E(v) ((v) ? BIT(12) : 0) -#define VDPU_REG_WRITE_MVS_E(v) ((v) ? BIT(10) : 0) -#define VDPU_REG_DEC_TIMEOUT_E(v) ((v) ? BIT(5) : 0) -#define VDPU_REG_DEC_CLK_GATE_E(v) ((v) ? BIT(4) : 0) - -#define VDPU_REG_PIC_MB_WIDTH(v) (((v) << 23) & GENMASK(31, 23)) -#define VDPU_REG_PIC_MB_HEIGHT_P(v) (((v) << 11) & GENMASK(18, 11)) -#define VDPU_REG_ALT_SCAN_E(v) ((v) ? BIT(6) : 0) -#define VDPU_REG_TOPFIELDFIRST_E(v) ((v) ? BIT(5) : 0) - -#define VDPU_REG_STRM_START_BIT(v) (((v) << 26) & GENMASK(31, 26)) -#define VDPU_REG_QSCALE_TYPE(v) ((v) ? BIT(24) : 0) -#define VDPU_REG_CON_MV_E(v) ((v) ? BIT(4) : 0) -#define VDPU_REG_INTRA_DC_PREC(v) (((v) << 2) & GENMASK(3, 2)) -#define VDPU_REG_INTRA_VLC_TAB(v) ((v) ? BIT(1) : 0) -#define VDPU_REG_FRAME_PRED_DCT(v) ((v) ? BIT(0) : 0) - -#define VDPU_REG_ALT_SCAN_FLAG_E(v) ((v) ? BIT(19) : 0) -#define VDPU_REG_FCODE_FWD_HOR(v) (((v) << 15) & GENMASK(18, 15)) -#define VDPU_REG_FCODE_FWD_VER(v) (((v) << 11) & GENMASK(14, 11)) -#define VDPU_REG_FCODE_BWD_HOR(v) (((v) << 7) & GENMASK(10, 7)) -#define VDPU_REG_FCODE_BWD_VER(v) (((v) << 3) & GENMASK(6, 3)) -#define VDPU_REG_MV_ACCURACY_FWD(v) ((v) ? BIT(2) : 0) -#define VDPU_REG_MV_ACCURACY_BWD(v) ((v) ? BIT(1) : 0) - -static void -rockchip_vpu2_mpeg2_dec_set_quantisation(struct hantro_dev *vpu, - struct hantro_ctx *ctx) -{ - struct v4l2_ctrl_mpeg2_quantisation *q; - - q = hantro_get_ctrl(ctx, V4L2_CID_STATELESS_MPEG2_QUANTISATION); - hantro_mpeg2_dec_copy_qtable(ctx->mpeg2_dec.qtable.cpu, q); - vdpu_write_relaxed(vpu, ctx->mpeg2_dec.qtable.dma, VDPU_REG_QTABLE_BASE); -} - -static void -rockchip_vpu2_mpeg2_dec_set_buffers(struct hantro_dev *vpu, - struct hantro_ctx *ctx, - struct vb2_buffer *src_buf, - struct vb2_buffer *dst_buf, - const struct v4l2_ctrl_mpeg2_sequence *seq, - const struct v4l2_ctrl_mpeg2_picture *pic) -{ - dma_addr_t forward_addr = 0, backward_addr = 0; - dma_addr_t current_addr, addr; - - switch (pic->picture_coding_type) { - case V4L2_MPEG2_PIC_CODING_TYPE_B: - backward_addr = hantro_get_ref(ctx, pic->backward_ref_ts); - fallthrough; - case V4L2_MPEG2_PIC_CODING_TYPE_P: - forward_addr = hantro_get_ref(ctx, pic->forward_ref_ts); - } - - /* Source bitstream buffer */ - addr = vb2_dma_contig_plane_dma_addr(src_buf, 0); - vdpu_write_relaxed(vpu, addr, VDPU_REG_RLC_VLC_BASE); - - /* Destination frame buffer */ - addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0); - current_addr = addr; - - if (pic->picture_structure == V4L2_MPEG2_PIC_BOTTOM_FIELD) - addr += ALIGN(ctx->dst_fmt.width, 16); - vdpu_write_relaxed(vpu, addr, VDPU_REG_DEC_OUT_BASE); - - if (!forward_addr) - forward_addr = current_addr; - if (!backward_addr) - backward_addr = current_addr; - - /* Set forward ref frame (top/bottom field) */ - if (pic->picture_structure == V4L2_MPEG2_PIC_FRAME || - pic->picture_coding_type == V4L2_MPEG2_PIC_CODING_TYPE_B || - (pic->picture_structure == V4L2_MPEG2_PIC_TOP_FIELD && - pic->flags & V4L2_MPEG2_PIC_TOP_FIELD) || - (pic->picture_structure == V4L2_MPEG2_PIC_BOTTOM_FIELD && - !(pic->flags & V4L2_MPEG2_PIC_TOP_FIELD))) { - vdpu_write_relaxed(vpu, forward_addr, VDPU_REG_REFER0_BASE); - vdpu_write_relaxed(vpu, forward_addr, VDPU_REG_REFER1_BASE); - } else if (pic->picture_structure == V4L2_MPEG2_PIC_TOP_FIELD) { - vdpu_write_relaxed(vpu, forward_addr, VDPU_REG_REFER0_BASE); - vdpu_write_relaxed(vpu, current_addr, VDPU_REG_REFER1_BASE); - } else if (pic->picture_structure == V4L2_MPEG2_PIC_BOTTOM_FIELD) { - vdpu_write_relaxed(vpu, current_addr, VDPU_REG_REFER0_BASE); - vdpu_write_relaxed(vpu, forward_addr, VDPU_REG_REFER1_BASE); - } - - /* Set backward ref frame (top/bottom field) */ - vdpu_write_relaxed(vpu, backward_addr, VDPU_REG_REFER2_BASE); - vdpu_write_relaxed(vpu, backward_addr, VDPU_REG_REFER3_BASE); -} - -int rockchip_vpu2_mpeg2_dec_run(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - struct vb2_v4l2_buffer *src_buf, *dst_buf; - const struct v4l2_ctrl_mpeg2_sequence *seq; - const struct v4l2_ctrl_mpeg2_picture *pic; - u32 reg; - - src_buf = hantro_get_src_buf(ctx); - dst_buf = hantro_get_dst_buf(ctx); - - hantro_start_prepare_run(ctx); - - seq = hantro_get_ctrl(ctx, - V4L2_CID_STATELESS_MPEG2_SEQUENCE); - pic = hantro_get_ctrl(ctx, - V4L2_CID_STATELESS_MPEG2_PICTURE); - - reg = VDPU_REG_DEC_ADV_PRE_DIS(0) | - VDPU_REG_DEC_SCMD_DIS(0) | - VDPU_REG_FILTERING_DIS(1) | - VDPU_REG_DEC_LATENCY(0); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(50)); - - reg = VDPU_REG_INIT_QP(1) | - VDPU_REG_STREAM_LEN(vb2_get_plane_payload(&src_buf->vb2_buf, 0)); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(51)); - - reg = VDPU_REG_APF_THRESHOLD(8) | - VDPU_REG_STARTMB_X(0) | - VDPU_REG_STARTMB_Y(0); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(52)); - - reg = VDPU_REG_DEC_MODE(5); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(53)); - - reg = VDPU_REG_DEC_STRENDIAN_E(1) | - VDPU_REG_DEC_STRSWAP32_E(1) | - VDPU_REG_DEC_OUTSWAP32_E(1) | - VDPU_REG_DEC_INSWAP32_E(1) | - VDPU_REG_DEC_OUT_ENDIAN(1) | - VDPU_REG_DEC_IN_ENDIAN(1); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(54)); - - reg = VDPU_REG_DEC_DATA_DISC_E(0) | - VDPU_REG_DEC_MAX_BURST(16) | - VDPU_REG_DEC_AXI_WR_ID(0) | - VDPU_REG_DEC_AXI_RD_ID(0); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(56)); - - reg = VDPU_REG_RLC_MODE_E(0) | - VDPU_REG_PIC_INTERLACE_E(!(seq->flags & V4L2_MPEG2_SEQ_FLAG_PROGRESSIVE)) | - VDPU_REG_PIC_FIELDMODE_E(pic->picture_structure != V4L2_MPEG2_PIC_FRAME) | - VDPU_REG_PIC_B_E(pic->picture_coding_type == V4L2_MPEG2_PIC_CODING_TYPE_B) | - VDPU_REG_PIC_INTER_E(pic->picture_coding_type != V4L2_MPEG2_PIC_CODING_TYPE_I) | - VDPU_REG_PIC_TOPFIELD_E(pic->picture_structure == V4L2_MPEG2_PIC_TOP_FIELD) | - VDPU_REG_FWD_INTERLACE_E(0) | - VDPU_REG_WRITE_MVS_E(0) | - VDPU_REG_DEC_TIMEOUT_E(1) | - VDPU_REG_DEC_CLK_GATE_E(1); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(57)); - - reg = VDPU_REG_PIC_MB_WIDTH(MB_WIDTH(ctx->dst_fmt.width)) | - VDPU_REG_PIC_MB_HEIGHT_P(MB_HEIGHT(ctx->dst_fmt.height)) | - VDPU_REG_ALT_SCAN_E(pic->flags & V4L2_MPEG2_PIC_FLAG_ALT_SCAN) | - VDPU_REG_TOPFIELDFIRST_E(pic->flags & V4L2_MPEG2_PIC_FLAG_TOP_FIELD_FIRST); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(120)); - - reg = VDPU_REG_STRM_START_BIT(0) | - VDPU_REG_QSCALE_TYPE(pic->flags & V4L2_MPEG2_PIC_FLAG_Q_SCALE_TYPE) | - VDPU_REG_CON_MV_E(pic->flags & V4L2_MPEG2_PIC_FLAG_CONCEALMENT_MV) | - VDPU_REG_INTRA_DC_PREC(pic->intra_dc_precision) | - VDPU_REG_INTRA_VLC_TAB(pic->flags & V4L2_MPEG2_PIC_FLAG_INTRA_VLC) | - VDPU_REG_FRAME_PRED_DCT(pic->flags & V4L2_MPEG2_PIC_FLAG_FRAME_PRED_DCT); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(122)); - - reg = VDPU_REG_ALT_SCAN_FLAG_E(pic->flags & V4L2_MPEG2_PIC_FLAG_ALT_SCAN) | - VDPU_REG_FCODE_FWD_HOR(pic->f_code[0][0]) | - VDPU_REG_FCODE_FWD_VER(pic->f_code[0][1]) | - VDPU_REG_FCODE_BWD_HOR(pic->f_code[1][0]) | - VDPU_REG_FCODE_BWD_VER(pic->f_code[1][1]) | - VDPU_REG_MV_ACCURACY_FWD(1) | - VDPU_REG_MV_ACCURACY_BWD(1); - vdpu_write_relaxed(vpu, reg, VDPU_SWREG(136)); - - rockchip_vpu2_mpeg2_dec_set_quantisation(vpu, ctx); - - rockchip_vpu2_mpeg2_dec_set_buffers(vpu, ctx, &src_buf->vb2_buf, - &dst_buf->vb2_buf, seq, pic); - - /* Kick the watchdog and start decoding */ - hantro_end_prepare_run(ctx); - - reg = vdpu_read(vpu, VDPU_SWREG(57)) | VDPU_REG_DEC_E(1); - vdpu_write(vpu, reg, VDPU_SWREG(57)); - - return 0; -} diff --git a/drivers/staging/media/hantro/rockchip_vpu2_hw_vp8_dec.c b/drivers/staging/media/hantro/rockchip_vpu2_hw_vp8_dec.c deleted file mode 100644 index d079075448c9..000000000000 --- a/drivers/staging/media/hantro/rockchip_vpu2_hw_vp8_dec.c +++ /dev/null @@ -1,600 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Rockchip VPU codec vp8 decode driver - * - * Copyright (C) 2014 Rockchip Electronics Co., Ltd. - * ZhiChao Yu <zhichao.yu@rock-chips.com> - * - * Copyright (C) 2014 Google LLC. - * Tomasz Figa <tfiga@chromium.org> - * - * Copyright (C) 2015 Rockchip Electronics Co., Ltd. - * Alpha Lin <alpha.lin@rock-chips.com> - */ - -#include <media/v4l2-mem2mem.h> - -#include "hantro_hw.h" -#include "hantro.h" -#include "hantro_g1_regs.h" - -#define VDPU_REG_DEC_CTRL0 0x0c8 -#define VDPU_REG_STREAM_LEN 0x0cc -#define VDPU_REG_DEC_FORMAT 0x0d4 -#define VDPU_REG_DEC_CTRL0_DEC_MODE(x) (((x) & 0xf) << 0) -#define VDPU_REG_DATA_ENDIAN 0x0d8 -#define VDPU_REG_CONFIG_DEC_STRENDIAN_E BIT(5) -#define VDPU_REG_CONFIG_DEC_STRSWAP32_E BIT(4) -#define VDPU_REG_CONFIG_DEC_OUTSWAP32_E BIT(3) -#define VDPU_REG_CONFIG_DEC_INSWAP32_E BIT(2) -#define VDPU_REG_CONFIG_DEC_OUT_ENDIAN BIT(1) -#define VDPU_REG_CONFIG_DEC_IN_ENDIAN BIT(0) -#define VDPU_REG_AXI_CTRL 0x0e0 -#define VDPU_REG_CONFIG_DEC_MAX_BURST(x) (((x) & 0x1f) << 16) -#define VDPU_REG_EN_FLAGS 0x0e4 -#define VDPU_REG_DEC_CTRL0_PIC_INTER_E BIT(14) -#define VDPU_REG_CONFIG_DEC_TIMEOUT_E BIT(5) -#define VDPU_REG_CONFIG_DEC_CLK_GATE_E BIT(4) -#define VDPU_REG_PRED_FLT 0x0ec -#define VDPU_REG_ADDR_QTABLE 0x0f4 -#define VDPU_REG_ADDR_DST 0x0fc -#define VDPU_REG_ADDR_STR 0x100 -#define VDPU_REG_VP8_PIC_MB_SIZE 0x1e0 -#define VDPU_REG_VP8_DCT_START_BIT 0x1e4 -#define VDPU_REG_DEC_CTRL4_VC1_HEIGHT_EXT BIT(13) -#define VDPU_REG_DEC_CTRL4_BILIN_MC_E BIT(12) -#define VDPU_REG_VP8_CTRL0 0x1e8 -#define VDPU_REG_VP8_DATA_VAL 0x1f0 -#define VDPU_REG_PRED_FLT7 0x1f4 -#define VDPU_REG_PRED_FLT8 0x1f8 -#define VDPU_REG_PRED_FLT9 0x1fc -#define VDPU_REG_PRED_FLT10 0x200 -#define VDPU_REG_FILTER_LEVEL 0x204 -#define VDPU_REG_VP8_QUANTER0 0x208 -#define VDPU_REG_VP8_ADDR_REF0 0x20c -#define VDPU_REG_FILTER_MB_ADJ 0x210 -#define VDPU_REG_REF_PIC_FILT_TYPE_E BIT(31) -#define VDPU_REG_REF_PIC_FILT_SHARPNESS(x) (((x) & 0x7) << 28) -#define VDPU_REG_FILTER_REF_ADJ 0x214 -#define VDPU_REG_VP8_ADDR_REF2_5(i) (0x218 + ((i) * 0x4)) -#define VDPU_REG_VP8_GREF_SIGN_BIAS BIT(0) -#define VDPU_REG_VP8_AREF_SIGN_BIAS BIT(0) -#define VDPU_REG_VP8_DCT_BASE(i) \ - (0x230 + ((((i) < 5) ? (i) : ((i) + 1)) * 0x4)) -#define VDPU_REG_VP8_ADDR_CTRL_PART 0x244 -#define VDPU_REG_VP8_SEGMENT_VAL 0x254 -#define VDPU_REG_FWD_PIC1_SEGMENT_BASE(x) ((x) << 0) -#define VDPU_REG_FWD_PIC1_SEGMENT_UPD_E BIT(1) -#define VDPU_REG_FWD_PIC1_SEGMENT_E BIT(0) -#define VDPU_REG_VP8_DCT_START_BIT2 0x258 -#define VDPU_REG_VP8_QUANTER1 0x25c -#define VDPU_REG_VP8_QUANTER2 0x260 -#define VDPU_REG_PRED_FLT1 0x264 -#define VDPU_REG_PRED_FLT2 0x268 -#define VDPU_REG_PRED_FLT3 0x26c -#define VDPU_REG_PRED_FLT4 0x270 -#define VDPU_REG_PRED_FLT5 0x274 -#define VDPU_REG_PRED_FLT6 0x278 - -static const struct hantro_reg vp8_dec_dct_base[8] = { - { VDPU_REG_ADDR_STR, 0, 0xffffffff }, - { VDPU_REG_VP8_DCT_BASE(0), 0, 0xffffffff }, - { VDPU_REG_VP8_DCT_BASE(1), 0, 0xffffffff }, - { VDPU_REG_VP8_DCT_BASE(2), 0, 0xffffffff }, - { VDPU_REG_VP8_DCT_BASE(3), 0, 0xffffffff }, - { VDPU_REG_VP8_DCT_BASE(4), 0, 0xffffffff }, - { VDPU_REG_VP8_DCT_BASE(5), 0, 0xffffffff }, - { VDPU_REG_VP8_DCT_BASE(6), 0, 0xffffffff }, -}; - -static const struct hantro_reg vp8_dec_lf_level[4] = { - { VDPU_REG_FILTER_LEVEL, 18, 0x3f }, - { VDPU_REG_FILTER_LEVEL, 12, 0x3f }, - { VDPU_REG_FILTER_LEVEL, 6, 0x3f }, - { VDPU_REG_FILTER_LEVEL, 0, 0x3f }, -}; - -static const struct hantro_reg vp8_dec_mb_adj[4] = { - { VDPU_REG_FILTER_MB_ADJ, 21, 0x7f }, - { VDPU_REG_FILTER_MB_ADJ, 14, 0x7f }, - { VDPU_REG_FILTER_MB_ADJ, 7, 0x7f }, - { VDPU_REG_FILTER_MB_ADJ, 0, 0x7f }, -}; - -static const struct hantro_reg vp8_dec_ref_adj[4] = { - { VDPU_REG_FILTER_REF_ADJ, 21, 0x7f }, - { VDPU_REG_FILTER_REF_ADJ, 14, 0x7f }, - { VDPU_REG_FILTER_REF_ADJ, 7, 0x7f }, - { VDPU_REG_FILTER_REF_ADJ, 0, 0x7f }, -}; - -static const struct hantro_reg vp8_dec_quant[4] = { - { VDPU_REG_VP8_QUANTER0, 11, 0x7ff }, - { VDPU_REG_VP8_QUANTER0, 0, 0x7ff }, - { VDPU_REG_VP8_QUANTER1, 11, 0x7ff }, - { VDPU_REG_VP8_QUANTER1, 0, 0x7ff }, -}; - -static const struct hantro_reg vp8_dec_quant_delta[5] = { - { VDPU_REG_VP8_QUANTER0, 27, 0x1f }, - { VDPU_REG_VP8_QUANTER0, 22, 0x1f }, - { VDPU_REG_VP8_QUANTER1, 27, 0x1f }, - { VDPU_REG_VP8_QUANTER1, 22, 0x1f }, - { VDPU_REG_VP8_QUANTER2, 27, 0x1f }, -}; - -static const struct hantro_reg vp8_dec_dct_start_bits[8] = { - { VDPU_REG_VP8_CTRL0, 26, 0x3f }, - { VDPU_REG_VP8_DCT_START_BIT, 26, 0x3f }, - { VDPU_REG_VP8_DCT_START_BIT, 20, 0x3f }, - { VDPU_REG_VP8_DCT_START_BIT2, 24, 0x3f }, - { VDPU_REG_VP8_DCT_START_BIT2, 18, 0x3f }, - { VDPU_REG_VP8_DCT_START_BIT2, 12, 0x3f }, - { VDPU_REG_VP8_DCT_START_BIT2, 6, 0x3f }, - { VDPU_REG_VP8_DCT_START_BIT2, 0, 0x3f }, -}; - -static const struct hantro_reg vp8_dec_pred_bc_tap[8][6] = { - { - { 0, 0, 0}, - { VDPU_REG_PRED_FLT, 22, 0x3ff }, - { VDPU_REG_PRED_FLT, 12, 0x3ff }, - { VDPU_REG_PRED_FLT, 2, 0x3ff }, - { VDPU_REG_PRED_FLT1, 22, 0x3ff }, - { 0, 0, 0}, - }, { - { 0, 0, 0}, - { VDPU_REG_PRED_FLT1, 12, 0x3ff }, - { VDPU_REG_PRED_FLT1, 2, 0x3ff }, - { VDPU_REG_PRED_FLT2, 22, 0x3ff }, - { VDPU_REG_PRED_FLT2, 12, 0x3ff }, - { 0, 0, 0}, - }, { - { VDPU_REG_PRED_FLT10, 10, 0x3 }, - { VDPU_REG_PRED_FLT2, 2, 0x3ff }, - { VDPU_REG_PRED_FLT3, 22, 0x3ff }, - { VDPU_REG_PRED_FLT3, 12, 0x3ff }, - { VDPU_REG_PRED_FLT3, 2, 0x3ff }, - { VDPU_REG_PRED_FLT10, 8, 0x3}, - }, { - { 0, 0, 0}, - { VDPU_REG_PRED_FLT4, 22, 0x3ff }, - { VDPU_REG_PRED_FLT4, 12, 0x3ff }, - { VDPU_REG_PRED_FLT4, 2, 0x3ff }, - { VDPU_REG_PRED_FLT5, 22, 0x3ff }, - { 0, 0, 0}, - }, { - { VDPU_REG_PRED_FLT10, 6, 0x3 }, - { VDPU_REG_PRED_FLT5, 12, 0x3ff }, - { VDPU_REG_PRED_FLT5, 2, 0x3ff }, - { VDPU_REG_PRED_FLT6, 22, 0x3ff }, - { VDPU_REG_PRED_FLT6, 12, 0x3ff }, - { VDPU_REG_PRED_FLT10, 4, 0x3 }, - }, { - { 0, 0, 0}, - { VDPU_REG_PRED_FLT6, 2, 0x3ff }, - { VDPU_REG_PRED_FLT7, 22, 0x3ff }, - { VDPU_REG_PRED_FLT7, 12, 0x3ff }, - { VDPU_REG_PRED_FLT7, 2, 0x3ff }, - { 0, 0, 0}, - }, { - { VDPU_REG_PRED_FLT10, 2, 0x3 }, - { VDPU_REG_PRED_FLT8, 22, 0x3ff }, - { VDPU_REG_PRED_FLT8, 12, 0x3ff }, - { VDPU_REG_PRED_FLT8, 2, 0x3ff }, - { VDPU_REG_PRED_FLT9, 22, 0x3ff }, - { VDPU_REG_PRED_FLT10, 0, 0x3 }, - }, { - { 0, 0, 0}, - { VDPU_REG_PRED_FLT9, 12, 0x3ff }, - { VDPU_REG_PRED_FLT9, 2, 0x3ff }, - { VDPU_REG_PRED_FLT10, 22, 0x3ff }, - { VDPU_REG_PRED_FLT10, 12, 0x3ff }, - { 0, 0, 0}, - }, -}; - -static const struct hantro_reg vp8_dec_mb_start_bit = { - .base = VDPU_REG_VP8_CTRL0, - .shift = 18, - .mask = 0x3f -}; - -static const struct hantro_reg vp8_dec_mb_aligned_data_len = { - .base = VDPU_REG_VP8_DATA_VAL, - .shift = 0, - .mask = 0x3fffff -}; - -static const struct hantro_reg vp8_dec_num_dct_partitions = { - .base = VDPU_REG_VP8_DATA_VAL, - .shift = 24, - .mask = 0xf -}; - -static const struct hantro_reg vp8_dec_stream_len = { - .base = VDPU_REG_STREAM_LEN, - .shift = 0, - .mask = 0xffffff -}; - -static const struct hantro_reg vp8_dec_mb_width = { - .base = VDPU_REG_VP8_PIC_MB_SIZE, - .shift = 23, - .mask = 0x1ff -}; - -static const struct hantro_reg vp8_dec_mb_height = { - .base = VDPU_REG_VP8_PIC_MB_SIZE, - .shift = 11, - .mask = 0xff -}; - -static const struct hantro_reg vp8_dec_mb_width_ext = { - .base = VDPU_REG_VP8_PIC_MB_SIZE, - .shift = 3, - .mask = 0x7 -}; - -static const struct hantro_reg vp8_dec_mb_height_ext = { - .base = VDPU_REG_VP8_PIC_MB_SIZE, - .shift = 0, - .mask = 0x7 -}; - -static const struct hantro_reg vp8_dec_bool_range = { - .base = VDPU_REG_VP8_CTRL0, - .shift = 0, - .mask = 0xff -}; - -static const struct hantro_reg vp8_dec_bool_value = { - .base = VDPU_REG_VP8_CTRL0, - .shift = 8, - .mask = 0xff -}; - -static const struct hantro_reg vp8_dec_filter_disable = { - .base = VDPU_REG_DEC_CTRL0, - .shift = 8, - .mask = 1 -}; - -static const struct hantro_reg vp8_dec_skip_mode = { - .base = VDPU_REG_DEC_CTRL0, - .shift = 9, - .mask = 1 -}; - -static const struct hantro_reg vp8_dec_start_dec = { - .base = VDPU_REG_EN_FLAGS, - .shift = 0, - .mask = 1 -}; - -static void cfg_lf(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp8_frame *hdr) -{ - const struct v4l2_vp8_segment *seg = &hdr->segment; - const struct v4l2_vp8_loop_filter *lf = &hdr->lf; - struct hantro_dev *vpu = ctx->dev; - unsigned int i; - u32 reg; - - if (!(seg->flags & V4L2_VP8_SEGMENT_FLAG_ENABLED)) { - hantro_reg_write(vpu, &vp8_dec_lf_level[0], lf->level); - } else if (seg->flags & V4L2_VP8_SEGMENT_FLAG_DELTA_VALUE_MODE) { - for (i = 0; i < 4; i++) { - u32 lf_level = clamp(lf->level + seg->lf_update[i], - 0, 63); - - hantro_reg_write(vpu, &vp8_dec_lf_level[i], lf_level); - } - } else { - for (i = 0; i < 4; i++) - hantro_reg_write(vpu, &vp8_dec_lf_level[i], - seg->lf_update[i]); - } - - reg = VDPU_REG_REF_PIC_FILT_SHARPNESS(lf->sharpness_level); - if (lf->flags & V4L2_VP8_LF_FILTER_TYPE_SIMPLE) - reg |= VDPU_REG_REF_PIC_FILT_TYPE_E; - vdpu_write_relaxed(vpu, reg, VDPU_REG_FILTER_MB_ADJ); - - if (lf->flags & V4L2_VP8_LF_ADJ_ENABLE) { - for (i = 0; i < 4; i++) { - hantro_reg_write(vpu, &vp8_dec_mb_adj[i], - lf->mb_mode_delta[i]); - hantro_reg_write(vpu, &vp8_dec_ref_adj[i], - lf->ref_frm_delta[i]); - } - } -} - -static void cfg_qp(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp8_frame *hdr) -{ - const struct v4l2_vp8_quantization *q = &hdr->quant; - const struct v4l2_vp8_segment *seg = &hdr->segment; - struct hantro_dev *vpu = ctx->dev; - unsigned int i; - - if (!(seg->flags & V4L2_VP8_SEGMENT_FLAG_ENABLED)) { - hantro_reg_write(vpu, &vp8_dec_quant[0], q->y_ac_qi); - } else if (seg->flags & V4L2_VP8_SEGMENT_FLAG_DELTA_VALUE_MODE) { - for (i = 0; i < 4; i++) { - u32 quant = clamp(q->y_ac_qi + seg->quant_update[i], - 0, 127); - - hantro_reg_write(vpu, &vp8_dec_quant[i], quant); - } - } else { - for (i = 0; i < 4; i++) - hantro_reg_write(vpu, &vp8_dec_quant[i], - seg->quant_update[i]); - } - - hantro_reg_write(vpu, &vp8_dec_quant_delta[0], q->y_dc_delta); - hantro_reg_write(vpu, &vp8_dec_quant_delta[1], q->y2_dc_delta); - hantro_reg_write(vpu, &vp8_dec_quant_delta[2], q->y2_ac_delta); - hantro_reg_write(vpu, &vp8_dec_quant_delta[3], q->uv_dc_delta); - hantro_reg_write(vpu, &vp8_dec_quant_delta[4], q->uv_ac_delta); -} - -static void cfg_parts(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp8_frame *hdr) -{ - struct hantro_dev *vpu = ctx->dev; - struct vb2_v4l2_buffer *vb2_src; - u32 first_part_offset = V4L2_VP8_FRAME_IS_KEY_FRAME(hdr) ? 10 : 3; - u32 mb_size, mb_offset_bytes, mb_offset_bits, mb_start_bits; - u32 dct_size_part_size, dct_part_offset; - dma_addr_t src_dma; - u32 dct_part_total_len = 0; - u32 count = 0; - unsigned int i; - - vb2_src = hantro_get_src_buf(ctx); - src_dma = vb2_dma_contig_plane_dma_addr(&vb2_src->vb2_buf, 0); - - /* - * Calculate control partition mb data info - * @first_part_header_bits: bits offset of mb data from first - * part start pos - * @mb_offset_bits: bits offset of mb data from src_dma - * base addr - * @mb_offset_byte: bytes offset of mb data from src_dma - * base addr - * @mb_start_bits: bits offset of mb data from mb data - * 64bits alignment addr - */ - mb_offset_bits = first_part_offset * 8 + - hdr->first_part_header_bits + 8; - mb_offset_bytes = mb_offset_bits / 8; - mb_start_bits = mb_offset_bits - - (mb_offset_bytes & (~DEC_8190_ALIGN_MASK)) * 8; - mb_size = hdr->first_part_size - - (mb_offset_bytes - first_part_offset) + - (mb_offset_bytes & DEC_8190_ALIGN_MASK); - - /* Macroblock data aligned base addr */ - vdpu_write_relaxed(vpu, (mb_offset_bytes & (~DEC_8190_ALIGN_MASK)) + - src_dma, VDPU_REG_VP8_ADDR_CTRL_PART); - hantro_reg_write(vpu, &vp8_dec_mb_start_bit, mb_start_bits); - hantro_reg_write(vpu, &vp8_dec_mb_aligned_data_len, mb_size); - - /* - * Calculate DCT partition info - * @dct_size_part_size: Containing sizes of DCT part, every DCT part - * has 3 bytes to store its size, except the last - * DCT part - * @dct_part_offset: bytes offset of DCT parts from src_dma base addr - * @dct_part_total_len: total size of all DCT parts - */ - dct_size_part_size = (hdr->num_dct_parts - 1) * 3; - dct_part_offset = first_part_offset + hdr->first_part_size; - for (i = 0; i < hdr->num_dct_parts; i++) - dct_part_total_len += hdr->dct_part_sizes[i]; - dct_part_total_len += dct_size_part_size; - dct_part_total_len += (dct_part_offset & DEC_8190_ALIGN_MASK); - - /* Number of DCT partitions */ - hantro_reg_write(vpu, &vp8_dec_num_dct_partitions, - hdr->num_dct_parts - 1); - - /* DCT partition length */ - hantro_reg_write(vpu, &vp8_dec_stream_len, dct_part_total_len); - - /* DCT partitions base address */ - for (i = 0; i < hdr->num_dct_parts; i++) { - u32 byte_offset = dct_part_offset + dct_size_part_size + count; - u32 base_addr = byte_offset + src_dma; - - hantro_reg_write(vpu, &vp8_dec_dct_base[i], - base_addr & (~DEC_8190_ALIGN_MASK)); - - hantro_reg_write(vpu, &vp8_dec_dct_start_bits[i], - (byte_offset & DEC_8190_ALIGN_MASK) * 8); - - count += hdr->dct_part_sizes[i]; - } -} - -/* - * prediction filter taps - * normal 6-tap filters - */ -static void cfg_tap(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp8_frame *hdr) -{ - struct hantro_dev *vpu = ctx->dev; - int i, j; - - if ((hdr->version & 0x03) != 0) - return; /* Tap filter not used. */ - - for (i = 0; i < 8; i++) { - for (j = 0; j < 6; j++) { - if (vp8_dec_pred_bc_tap[i][j].base != 0) - hantro_reg_write(vpu, - &vp8_dec_pred_bc_tap[i][j], - hantro_vp8_dec_mc_filter[i][j]); - } - } -} - -static void cfg_ref(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp8_frame *hdr, - struct vb2_v4l2_buffer *vb2_dst) -{ - struct hantro_dev *vpu = ctx->dev; - dma_addr_t ref; - - ref = hantro_get_ref(ctx, hdr->last_frame_ts); - if (!ref) { - vpu_debug(0, "failed to find last frame ts=%llu\n", - hdr->last_frame_ts); - ref = vb2_dma_contig_plane_dma_addr(&vb2_dst->vb2_buf, 0); - } - vdpu_write_relaxed(vpu, ref, VDPU_REG_VP8_ADDR_REF0); - - ref = hantro_get_ref(ctx, hdr->golden_frame_ts); - if (!ref && hdr->golden_frame_ts) - vpu_debug(0, "failed to find golden frame ts=%llu\n", - hdr->golden_frame_ts); - if (!ref) - ref = vb2_dma_contig_plane_dma_addr(&vb2_dst->vb2_buf, 0); - if (hdr->flags & V4L2_VP8_FRAME_FLAG_SIGN_BIAS_GOLDEN) - ref |= VDPU_REG_VP8_GREF_SIGN_BIAS; - vdpu_write_relaxed(vpu, ref, VDPU_REG_VP8_ADDR_REF2_5(2)); - - ref = hantro_get_ref(ctx, hdr->alt_frame_ts); - if (!ref && hdr->alt_frame_ts) - vpu_debug(0, "failed to find alt frame ts=%llu\n", - hdr->alt_frame_ts); - if (!ref) - ref = vb2_dma_contig_plane_dma_addr(&vb2_dst->vb2_buf, 0); - if (hdr->flags & V4L2_VP8_FRAME_FLAG_SIGN_BIAS_ALT) - ref |= VDPU_REG_VP8_AREF_SIGN_BIAS; - vdpu_write_relaxed(vpu, ref, VDPU_REG_VP8_ADDR_REF2_5(3)); -} - -static void cfg_buffers(struct hantro_ctx *ctx, - const struct v4l2_ctrl_vp8_frame *hdr, - struct vb2_v4l2_buffer *vb2_dst) -{ - const struct v4l2_vp8_segment *seg = &hdr->segment; - struct hantro_dev *vpu = ctx->dev; - dma_addr_t dst_dma; - u32 reg; - - /* Set probability table buffer address */ - vdpu_write_relaxed(vpu, ctx->vp8_dec.prob_tbl.dma, - VDPU_REG_ADDR_QTABLE); - - /* Set segment map address */ - reg = VDPU_REG_FWD_PIC1_SEGMENT_BASE(ctx->vp8_dec.segment_map.dma); - if (seg->flags & V4L2_VP8_SEGMENT_FLAG_ENABLED) { - reg |= VDPU_REG_FWD_PIC1_SEGMENT_E; - if (seg->flags & V4L2_VP8_SEGMENT_FLAG_UPDATE_MAP) - reg |= VDPU_REG_FWD_PIC1_SEGMENT_UPD_E; - } - vdpu_write_relaxed(vpu, reg, VDPU_REG_VP8_SEGMENT_VAL); - - /* set output frame buffer address */ - dst_dma = vb2_dma_contig_plane_dma_addr(&vb2_dst->vb2_buf, 0); - vdpu_write_relaxed(vpu, dst_dma, VDPU_REG_ADDR_DST); -} - -int rockchip_vpu2_vp8_dec_run(struct hantro_ctx *ctx) -{ - const struct v4l2_ctrl_vp8_frame *hdr; - struct hantro_dev *vpu = ctx->dev; - struct vb2_v4l2_buffer *vb2_dst; - size_t height = ctx->dst_fmt.height; - size_t width = ctx->dst_fmt.width; - u32 mb_width, mb_height; - u32 reg; - - hantro_start_prepare_run(ctx); - - hdr = hantro_get_ctrl(ctx, V4L2_CID_STATELESS_VP8_FRAME); - if (WARN_ON(!hdr)) - return -EINVAL; - - /* Reset segment_map buffer in keyframe */ - if (V4L2_VP8_FRAME_IS_KEY_FRAME(hdr) && ctx->vp8_dec.segment_map.cpu) - memset(ctx->vp8_dec.segment_map.cpu, 0, - ctx->vp8_dec.segment_map.size); - - hantro_vp8_prob_update(ctx, hdr); - - /* - * Extensive testing shows that the hardware does not properly - * clear the internal state from previous a decoding run. This - * causes corruption in decoded frames for multi-instance use cases. - * A soft reset before programming the registers has been found - * to resolve those problems. - */ - ctx->codec_ops->reset(ctx); - - reg = VDPU_REG_CONFIG_DEC_TIMEOUT_E - | VDPU_REG_CONFIG_DEC_CLK_GATE_E; - if (!V4L2_VP8_FRAME_IS_KEY_FRAME(hdr)) - reg |= VDPU_REG_DEC_CTRL0_PIC_INTER_E; - vdpu_write_relaxed(vpu, reg, VDPU_REG_EN_FLAGS); - - reg = VDPU_REG_CONFIG_DEC_STRENDIAN_E - | VDPU_REG_CONFIG_DEC_INSWAP32_E - | VDPU_REG_CONFIG_DEC_STRSWAP32_E - | VDPU_REG_CONFIG_DEC_OUTSWAP32_E - | VDPU_REG_CONFIG_DEC_IN_ENDIAN - | VDPU_REG_CONFIG_DEC_OUT_ENDIAN; - vdpu_write_relaxed(vpu, reg, VDPU_REG_DATA_ENDIAN); - - reg = VDPU_REG_CONFIG_DEC_MAX_BURST(16); - vdpu_write_relaxed(vpu, reg, VDPU_REG_AXI_CTRL); - - reg = VDPU_REG_DEC_CTRL0_DEC_MODE(10); - vdpu_write_relaxed(vpu, reg, VDPU_REG_DEC_FORMAT); - - if (!(hdr->flags & V4L2_VP8_FRAME_FLAG_MB_NO_SKIP_COEFF)) - hantro_reg_write(vpu, &vp8_dec_skip_mode, 1); - if (hdr->lf.level == 0) - hantro_reg_write(vpu, &vp8_dec_filter_disable, 1); - - /* Frame dimensions */ - mb_width = MB_WIDTH(width); - mb_height = MB_HEIGHT(height); - - hantro_reg_write(vpu, &vp8_dec_mb_width, mb_width); - hantro_reg_write(vpu, &vp8_dec_mb_height, mb_height); - hantro_reg_write(vpu, &vp8_dec_mb_width_ext, mb_width >> 9); - hantro_reg_write(vpu, &vp8_dec_mb_height_ext, mb_height >> 8); - - /* Boolean decoder */ - hantro_reg_write(vpu, &vp8_dec_bool_range, hdr->coder_state.range); - hantro_reg_write(vpu, &vp8_dec_bool_value, hdr->coder_state.value); - - reg = vdpu_read(vpu, VDPU_REG_VP8_DCT_START_BIT); - if (hdr->version != 3) - reg |= VDPU_REG_DEC_CTRL4_VC1_HEIGHT_EXT; - if (hdr->version & 0x3) - reg |= VDPU_REG_DEC_CTRL4_BILIN_MC_E; - vdpu_write_relaxed(vpu, reg, VDPU_REG_VP8_DCT_START_BIT); - - cfg_lf(ctx, hdr); - cfg_qp(ctx, hdr); - cfg_parts(ctx, hdr); - cfg_tap(ctx, hdr); - - vb2_dst = hantro_get_dst_buf(ctx); - cfg_ref(ctx, hdr, vb2_dst); - cfg_buffers(ctx, hdr, vb2_dst); - - hantro_end_prepare_run(ctx); - - hantro_reg_write(vpu, &vp8_dec_start_dec, 1); - - return 0; -} diff --git a/drivers/staging/media/hantro/rockchip_vpu2_regs.h b/drivers/staging/media/hantro/rockchip_vpu2_regs.h deleted file mode 100644 index 49e40889545b..000000000000 --- a/drivers/staging/media/hantro/rockchip_vpu2_regs.h +++ /dev/null @@ -1,600 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Hantro VPU codec driver - * - * Copyright (C) 2018 Rockchip Electronics Co., Ltd. - * Alpha Lin <alpha.lin@rock-chips.com> - */ - -#ifndef ROCKCHIP_VPU2_REGS_H_ -#define ROCKCHIP_VPU2_REGS_H_ - -/* Encoder registers. */ -#define VEPU_REG_VP8_QUT_1ST(i) (0x000 + ((i) * 0x24)) -#define VEPU_REG_VP8_QUT_DC_Y2(x) (((x) & 0x3fff) << 16) -#define VEPU_REG_VP8_QUT_DC_Y1(x) (((x) & 0x3fff) << 0) -#define VEPU_REG_VP8_QUT_2ND(i) (0x004 + ((i) * 0x24)) -#define VEPU_REG_VP8_QUT_AC_Y1(x) (((x) & 0x3fff) << 16) -#define VEPU_REG_VP8_QUT_DC_CHR(x) (((x) & 0x3fff) << 0) -#define VEPU_REG_VP8_QUT_3RD(i) (0x008 + ((i) * 0x24)) -#define VEPU_REG_VP8_QUT_AC_CHR(x) (((x) & 0x3fff) << 16) -#define VEPU_REG_VP8_QUT_AC_Y2(x) (((x) & 0x3fff) << 0) -#define VEPU_REG_VP8_QUT_4TH(i) (0x00c + ((i) * 0x24)) -#define VEPU_REG_VP8_QUT_ZB_DC_CHR(x) (((x) & 0x1ff) << 18) -#define VEPU_REG_VP8_QUT_ZB_DC_Y2(x) (((x) & 0x1ff) << 9) -#define VEPU_REG_VP8_QUT_ZB_DC_Y1(x) (((x) & 0x1ff) << 0) -#define VEPU_REG_VP8_QUT_5TH(i) (0x010 + ((i) * 0x24)) -#define VEPU_REG_VP8_QUT_ZB_AC_CHR(x) (((x) & 0x1ff) << 18) -#define VEPU_REG_VP8_QUT_ZB_AC_Y2(x) (((x) & 0x1ff) << 9) -#define VEPU_REG_VP8_QUT_ZB_AC_Y1(x) (((x) & 0x1ff) << 0) -#define VEPU_REG_VP8_QUT_6TH(i) (0x014 + ((i) * 0x24)) -#define VEPU_REG_VP8_QUT_RND_DC_CHR(x) (((x) & 0xff) << 16) -#define VEPU_REG_VP8_QUT_RND_DC_Y2(x) (((x) & 0xff) << 8) -#define VEPU_REG_VP8_QUT_RND_DC_Y1(x) (((x) & 0xff) << 0) -#define VEPU_REG_VP8_QUT_7TH(i) (0x018 + ((i) * 0x24)) -#define VEPU_REG_VP8_QUT_RND_AC_CHR(x) (((x) & 0xff) << 16) -#define VEPU_REG_VP8_QUT_RND_AC_Y2(x) (((x) & 0xff) << 8) -#define VEPU_REG_VP8_QUT_RND_AC_Y1(x) (((x) & 0xff) << 0) -#define VEPU_REG_VP8_QUT_8TH(i) (0x01c + ((i) * 0x24)) -#define VEPU_REG_VP8_SEG_FILTER_LEVEL(x) (((x) & 0x3f) << 25) -#define VEPU_REG_VP8_DEQUT_DC_CHR(x) (((x) & 0xff) << 17) -#define VEPU_REG_VP8_DEQUT_DC_Y2(x) (((x) & 0x1ff) << 8) -#define VEPU_REG_VP8_DEQUT_DC_Y1(x) (((x) & 0xff) << 0) -#define VEPU_REG_VP8_QUT_9TH(i) (0x020 + ((i) * 0x24)) -#define VEPU_REG_VP8_DEQUT_AC_CHR(x) (((x) & 0x1ff) << 18) -#define VEPU_REG_VP8_DEQUT_AC_Y2(x) (((x) & 0x1ff) << 9) -#define VEPU_REG_VP8_DEQUT_AC_Y1(x) (((x) & 0x1ff) << 0) -#define VEPU_REG_ADDR_VP8_SEG_MAP 0x06c -#define VEPU_REG_VP8_INTRA_4X4_PENALTY(i) (0x070 + ((i) * 0x4)) -#define VEPU_REG_VP8_INTRA_4X4_PENALTY_0(x) (((x) & 0xfff) << 0) -#define VEPU_REG_VP8_INTRA_4x4_PENALTY_1(x) (((x) & 0xfff) << 16) -#define VEPU_REG_VP8_INTRA_16X16_PENALTY(i) (0x084 + ((i) * 0x4)) -#define VEPU_REG_VP8_INTRA_16X16_PENALTY_0(x) (((x) & 0xfff) << 0) -#define VEPU_REG_VP8_INTRA_16X16_PENALTY_1(x) (((x) & 0xfff) << 16) -#define VEPU_REG_VP8_CONTROL 0x0a0 -#define VEPU_REG_VP8_LF_MODE_DELTA_BPRED(x) (((x) & 0x1f) << 24) -#define VEPU_REG_VP8_LF_REF_DELTA_INTRA_MB(x) (((x) & 0x7f) << 16) -#define VEPU_REG_VP8_INTER_TYPE_BIT_COST(x) (((x) & 0xfff) << 0) -#define VEPU_REG_VP8_REF_FRAME_VAL 0x0a4 -#define VEPU_REG_VP8_COEF_DMV_PENALTY(x) (((x) & 0xfff) << 16) -#define VEPU_REG_VP8_REF_FRAME(x) (((x) & 0xfff) << 0) -#define VEPU_REG_VP8_LOOP_FILTER_REF_DELTA 0x0a8 -#define VEPU_REG_VP8_LF_REF_DELTA_ALT_REF(x) (((x) & 0x7f) << 16) -#define VEPU_REG_VP8_LF_REF_DELTA_LAST_REF(x) (((x) & 0x7f) << 8) -#define VEPU_REG_VP8_LF_REF_DELTA_GOLDEN(x) (((x) & 0x7f) << 0) -#define VEPU_REG_VP8_LOOP_FILTER_MODE_DELTA 0x0ac -#define VEPU_REG_VP8_LF_MODE_DELTA_SPLITMV(x) (((x) & 0x7f) << 16) -#define VEPU_REG_VP8_LF_MODE_DELTA_ZEROMV(x) (((x) & 0x7f) << 8) -#define VEPU_REG_VP8_LF_MODE_DELTA_NEWMV(x) (((x) & 0x7f) << 0) -#define VEPU_REG_JPEG_LUMA_QUAT(i) (0x000 + ((i) * 0x4)) -#define VEPU_REG_JPEG_CHROMA_QUAT(i) (0x040 + ((i) * 0x4)) -#define VEPU_REG_INTRA_SLICE_BITMAP(i) (0x0b0 + ((i) * 0x4)) -#define VEPU_REG_ADDR_VP8_DCT_PART(i) (0x0b0 + ((i) * 0x4)) -#define VEPU_REG_INTRA_AREA_CTRL 0x0b8 -#define VEPU_REG_INTRA_AREA_TOP(x) (((x) & 0xff) << 24) -#define VEPU_REG_INTRA_AREA_BOTTOM(x) (((x) & 0xff) << 16) -#define VEPU_REG_INTRA_AREA_LEFT(x) (((x) & 0xff) << 8) -#define VEPU_REG_INTRA_AREA_RIGHT(x) (((x) & 0xff) << 0) -#define VEPU_REG_CIR_INTRA_CTRL 0x0bc -#define VEPU_REG_CIR_INTRA_FIRST_MB(x) (((x) & 0xffff) << 16) -#define VEPU_REG_CIR_INTRA_INTERVAL(x) (((x) & 0xffff) << 0) -#define VEPU_REG_ADDR_IN_PLANE_0 0x0c0 -#define VEPU_REG_ADDR_IN_PLANE_1 0x0c4 -#define VEPU_REG_ADDR_IN_PLANE_2 0x0c8 -#define VEPU_REG_STR_HDR_REM_MSB 0x0cc -#define VEPU_REG_STR_HDR_REM_LSB 0x0d0 -#define VEPU_REG_STR_BUF_LIMIT 0x0d4 -#define VEPU_REG_AXI_CTRL 0x0d8 -#define VEPU_REG_AXI_CTRL_READ_ID(x) (((x) & 0xff) << 24) -#define VEPU_REG_AXI_CTRL_WRITE_ID(x) (((x) & 0xff) << 16) -#define VEPU_REG_AXI_CTRL_BURST_LEN(x) (((x) & 0x3f) << 8) -#define VEPU_REG_AXI_CTRL_INCREMENT_MODE(x) (((x) & 0x01) << 2) -#define VEPU_REG_AXI_CTRL_BIRST_DISCARD(x) (((x) & 0x01) << 1) -#define VEPU_REG_AXI_CTRL_BIRST_DISABLE BIT(0) -#define VEPU_QP_ADJUST_MAD_DELTA_ROI 0x0dc -#define VEPU_REG_ROI_QP_DELTA_1 (((x) & 0xf) << 12) -#define VEPU_REG_ROI_QP_DELTA_2 (((x) & 0xf) << 8) -#define VEPU_REG_MAD_QP_ADJUSTMENT (((x) & 0xf) << 0) -#define VEPU_REG_ADDR_REF_LUMA 0x0e0 -#define VEPU_REG_ADDR_REF_CHROMA 0x0e4 -#define VEPU_REG_QP_SUM_DIV2 0x0e8 -#define VEPU_REG_QP_SUM(x) (((x) & 0x001fffff) * 2) -#define VEPU_REG_ENC_CTRL0 0x0ec -#define VEPU_REG_DISABLE_QUARTER_PIXEL_MV BIT(28) -#define VEPU_REG_DEBLOCKING_FILTER_MODE(x) (((x) & 0x3) << 24) -#define VEPU_REG_CABAC_INIT_IDC(x) (((x) & 0x3) << 21) -#define VEPU_REG_ENTROPY_CODING_MODE BIT(20) -#define VEPU_REG_H264_TRANS8X8_MODE BIT(17) -#define VEPU_REG_H264_INTER4X4_MODE BIT(16) -#define VEPU_REG_H264_STREAM_MODE BIT(15) -#define VEPU_REG_H264_SLICE_SIZE(x) (((x) & 0x7f) << 8) -#define VEPU_REG_ENC_OVER_FILL_STRM_OFFSET 0x0f0 -#define VEPU_REG_STREAM_START_OFFSET(x) (((x) & 0x3f) << 16) -#define VEPU_REG_SKIP_MACROBLOCK_PENALTY(x) (((x) & 0xff) << 8) -#define VEPU_REG_IN_IMG_CTRL_OVRFLR_D4(x) (((x) & 0x3) << 4) -#define VEPU_REG_IN_IMG_CTRL_OVRFLB(x) (((x) & 0xf) << 0) -#define VEPU_REG_INPUT_LUMA_INFO 0x0f4 -#define VEPU_REG_IN_IMG_CHROMA_OFFSET(x) (((x) & 0x7) << 20) -#define VEPU_REG_IN_IMG_LUMA_OFFSET(x) (((x) & 0x7) << 16) -#define VEPU_REG_IN_IMG_CTRL_ROW_LEN(x) (((x) & 0x3fff) << 0) -#define VEPU_REG_RLC_SUM 0x0f8 -#define VEPU_REG_RLC_SUM_OUT(x) (((x) & 0x007fffff) * 4) -#define VEPU_REG_SPLIT_PENALTY_4X4 0x0f8 -#define VEPU_REG_VP8_SPLIT_PENALTY_4X4 (((x) & 0x1ff) << 19) -#define VEPU_REG_ADDR_REC_LUMA 0x0fc -#define VEPU_REG_ADDR_REC_CHROMA 0x100 -#define VEPU_REG_CHECKPOINT(i) (0x104 + ((i) * 0x4)) -#define VEPU_REG_CHECKPOINT_CHECK0(x) (((x) & 0xffff)) -#define VEPU_REG_CHECKPOINT_CHECK1(x) (((x) & 0xffff) << 16) -#define VEPU_REG_CHECKPOINT_RESULT(x) \ - ((((x) >> (16 - 16 * ((i) & 1))) & 0xffff) * 32) -#define VEPU_REG_VP8_SEG0_QUANT_AC_Y1 0x104 -#define VEPU_REG_VP8_SEG0_RND_AC_Y1(x) (((x) & 0xff) << 23) -#define VEPU_REG_VP8_SEG0_ZBIN_AC_Y1(x) (((x) & 0x1ff) << 14) -#define VEPU_REG_VP8_SEG0_QUT_AC_Y1(x) (((x) & 0x3fff) << 0) -#define VEPU_REG_VP8_SEG0_QUANT_DC_Y2 0x108 -#define VEPU_REG_VP8_SEG0_RND_DC_Y2(x) (((x) & 0xff) << 23) -#define VEPU_REG_VP8_SEG0_ZBIN_DC_Y2(x) (((x) & 0x1ff) << 14) -#define VEPU_REG_VP8_SEG0_QUT_DC_Y2(x) (((x) & 0x3fff) << 0) -#define VEPU_REG_VP8_SEG0_QUANT_AC_Y2 0x10c -#define VEPU_REG_VP8_SEG0_RND_AC_Y2(x) (((x) & 0xff) << 23) -#define VEPU_REG_VP8_SEG0_ZBIN_AC_Y2(x) (((x) & 0x1ff) << 14) -#define VEPU_REG_VP8_SEG0_QUT_AC_Y2(x) (((x) & 0x3fff) << 0) -#define VEPU_REG_VP8_SEG0_QUANT_DC_CHR 0x110 -#define VEPU_REG_VP8_SEG0_RND_DC_CHR(x) (((x) & 0xff) << 23) -#define VEPU_REG_VP8_SEG0_ZBIN_DC_CHR(x) (((x) & 0x1ff) << 14) -#define VEPU_REG_VP8_SEG0_QUT_DC_CHR(x) (((x) & 0x3fff) << 0) -#define VEPU_REG_VP8_SEG0_QUANT_AC_CHR 0x114 -#define VEPU_REG_VP8_SEG0_RND_AC_CHR(x) (((x) & 0xff) << 23) -#define VEPU_REG_VP8_SEG0_ZBIN_AC_CHR(x) (((x) & 0x1ff) << 14) -#define VEPU_REG_VP8_SEG0_QUT_AC_CHR(x) (((x) & 0x3fff) << 0) -#define VEPU_REG_VP8_SEG0_QUANT_DQUT 0x118 -#define VEPU_REG_VP8_MV_REF_IDX1(x) (((x) & 0x03) << 26) -#define VEPU_REG_VP8_SEG0_DQUT_DC_Y2(x) (((x) & 0x1ff) << 17) -#define VEPU_REG_VP8_SEG0_DQUT_AC_Y1(x) (((x) & 0x1ff) << 8) -#define VEPU_REG_VP8_SEG0_DQUT_DC_Y1(x) (((x) & 0xff) << 0) -#define VEPU_REG_CHKPT_WORD_ERR(i) (0x118 + ((i) * 0x4)) -#define VEPU_REG_CHKPT_WORD_ERR_CHK0(x) (((x) & 0xffff)) -#define VEPU_REG_CHKPT_WORD_ERR_CHK1(x) (((x) & 0xffff) << 16) -#define VEPU_REG_VP8_SEG0_QUANT_DQUT_1 0x11c -#define VEPU_REG_VP8_SEGMENT_MAP_UPDATE BIT(30) -#define VEPU_REG_VP8_SEGMENT_EN BIT(29) -#define VEPU_REG_VP8_MV_REF_IDX2_EN BIT(28) -#define VEPU_REG_VP8_MV_REF_IDX2(x) (((x) & 0x03) << 26) -#define VEPU_REG_VP8_SEG0_DQUT_AC_CHR(x) (((x) & 0x1ff) << 17) -#define VEPU_REG_VP8_SEG0_DQUT_DC_CHR(x) (((x) & 0xff) << 9) -#define VEPU_REG_VP8_SEG0_DQUT_AC_Y2(x) (((x) & 0x1ff) << 0) -#define VEPU_REG_VP8_BOOL_ENC_VALUE 0x120 -#define VEPU_REG_CHKPT_DELTA_QP 0x124 -#define VEPU_REG_CHKPT_DELTA_QP_CHK0(x) (((x) & 0x0f) << 0) -#define VEPU_REG_CHKPT_DELTA_QP_CHK1(x) (((x) & 0x0f) << 4) -#define VEPU_REG_CHKPT_DELTA_QP_CHK2(x) (((x) & 0x0f) << 8) -#define VEPU_REG_CHKPT_DELTA_QP_CHK3(x) (((x) & 0x0f) << 12) -#define VEPU_REG_CHKPT_DELTA_QP_CHK4(x) (((x) & 0x0f) << 16) -#define VEPU_REG_CHKPT_DELTA_QP_CHK5(x) (((x) & 0x0f) << 20) -#define VEPU_REG_CHKPT_DELTA_QP_CHK6(x) (((x) & 0x0f) << 24) -#define VEPU_REG_VP8_ENC_CTRL2 0x124 -#define VEPU_REG_VP8_ZERO_MV_PENALTY_FOR_REF2(x) (((x) & 0xff) << 24) -#define VEPU_REG_VP8_FILTER_SHARPNESS(x) (((x) & 0x07) << 21) -#define VEPU_REG_VP8_FILTER_LEVEL(x) (((x) & 0x3f) << 15) -#define VEPU_REG_VP8_DCT_PARTITION_CNT(x) (((x) & 0x03) << 13) -#define VEPU_REG_VP8_BOOL_ENC_VALUE_BITS(x) (((x) & 0x1f) << 8) -#define VEPU_REG_VP8_BOOL_ENC_RANGE(x) (((x) & 0xff) << 0) -#define VEPU_REG_ENC_CTRL1 0x128 -#define VEPU_REG_MAD_THRESHOLD(x) (((x) & 0x3f) << 24) -#define VEPU_REG_COMPLETED_SLICES(x) (((x) & 0xff) << 16) -#define VEPU_REG_IN_IMG_CTRL_FMT(x) (((x) & 0xf) << 4) -#define VEPU_REG_IN_IMG_ROTATE_MODE(x) (((x) & 0x3) << 2) -#define VEPU_REG_SIZE_TABLE_PRESENT BIT(0) -#define VEPU_REG_INTRA_INTER_MODE 0x12c -#define VEPU_REG_INTRA16X16_MODE(x) (((x) & 0xffff) << 16) -#define VEPU_REG_INTER_MODE(x) (((x) & 0xffff) << 0) -#define VEPU_REG_ENC_CTRL2 0x130 -#define VEPU_REG_PPS_INIT_QP(x) (((x) & 0x3f) << 26) -#define VEPU_REG_SLICE_FILTER_ALPHA(x) (((x) & 0xf) << 22) -#define VEPU_REG_SLICE_FILTER_BETA(x) (((x) & 0xf) << 18) -#define VEPU_REG_CHROMA_QP_OFFSET(x) (((x) & 0x1f) << 13) -#define VEPU_REG_FILTER_DISABLE BIT(5) -#define VEPU_REG_IDR_PIC_ID(x) (((x) & 0xf) << 1) -#define VEPU_REG_CONSTRAINED_INTRA_PREDICTION BIT(0) -#define VEPU_REG_ADDR_OUTPUT_STREAM 0x134 -#define VEPU_REG_ADDR_OUTPUT_CTRL 0x138 -#define VEPU_REG_ADDR_NEXT_PIC 0x13c -#define VEPU_REG_ADDR_MV_OUT 0x140 -#define VEPU_REG_ADDR_CABAC_TBL 0x144 -#define VEPU_REG_ROI1 0x148 -#define VEPU_REG_ROI1_TOP_MB(x) (((x) & 0xff) << 24) -#define VEPU_REG_ROI1_BOTTOM_MB(x) (((x) & 0xff) << 16) -#define VEPU_REG_ROI1_LEFT_MB(x) (((x) & 0xff) << 8) -#define VEPU_REG_ROI1_RIGHT_MB(x) (((x) & 0xff) << 0) -#define VEPU_REG_ROI2 0x14c -#define VEPU_REG_ROI2_TOP_MB(x) (((x) & 0xff) << 24) -#define VEPU_REG_ROI2_BOTTOM_MB(x) (((x) & 0xff) << 16) -#define VEPU_REG_ROI2_LEFT_MB(x) (((x) & 0xff) << 8) -#define VEPU_REG_ROI2_RIGHT_MB(x) (((x) & 0xff) << 0) -#define VEPU_REG_STABLE_MATRIX(i) (0x150 + ((i) * 0x4)) -#define VEPU_REG_STABLE_MOTION_SUM 0x174 -#define VEPU_REG_STABILIZATION_OUTPUT 0x178 -#define VEPU_REG_STABLE_MIN_VALUE(x) (((x) & 0xffffff) << 8) -#define VEPU_REG_STABLE_MODE_SEL(x) (((x) & 0x3) << 6) -#define VEPU_REG_STABLE_HOR_GMV(x) (((x) & 0x3f) << 0) -#define VEPU_REG_RGB2YUV_CONVERSION_COEF1 0x17c -#define VEPU_REG_RGB2YUV_CONVERSION_COEFB(x) (((x) & 0xffff) << 16) -#define VEPU_REG_RGB2YUV_CONVERSION_COEFA(x) (((x) & 0xffff) << 0) -#define VEPU_REG_RGB2YUV_CONVERSION_COEF2 0x180 -#define VEPU_REG_RGB2YUV_CONVERSION_COEFE(x) (((x) & 0xffff) << 16) -#define VEPU_REG_RGB2YUV_CONVERSION_COEFC(x) (((x) & 0xffff) << 0) -#define VEPU_REG_RGB2YUV_CONVERSION_COEF3 0x184 -#define VEPU_REG_RGB2YUV_CONVERSION_COEFF(x) (((x) & 0xffff) << 0) -#define VEPU_REG_RGB_MASK_MSB 0x188 -#define VEPU_REG_RGB_MASK_B_MSB(x) (((x) & 0x1f) << 16) -#define VEPU_REG_RGB_MASK_G_MSB(x) (((x) & 0x1f) << 8) -#define VEPU_REG_RGB_MASK_R_MSB(x) (((x) & 0x1f) << 0) -#define VEPU_REG_MV_PENALTY 0x18c -#define VEPU_REG_1MV_PENALTY(x) (((x) & 0x3ff) << 21) -#define VEPU_REG_QMV_PENALTY(x) (((x) & 0x3ff) << 11) -#define VEPU_REG_4MV_PENALTY(x) (((x) & 0x3ff) << 1) -#define VEPU_REG_SPLIT_MV_MODE_EN BIT(0) -#define VEPU_REG_QP_VAL 0x190 -#define VEPU_REG_H264_LUMA_INIT_QP(x) (((x) & 0x3f) << 26) -#define VEPU_REG_H264_QP_MAX(x) (((x) & 0x3f) << 20) -#define VEPU_REG_H264_QP_MIN(x) (((x) & 0x3f) << 14) -#define VEPU_REG_H264_CHKPT_DISTANCE(x) (((x) & 0xfff) << 0) -#define VEPU_REG_VP8_SEG0_QUANT_DC_Y1 0x190 -#define VEPU_REG_VP8_SEG0_RND_DC_Y1(x) (((x) & 0xff) << 23) -#define VEPU_REG_VP8_SEG0_ZBIN_DC_Y1(x) (((x) & 0x1ff) << 14) -#define VEPU_REG_VP8_SEG0_QUT_DC_Y1(x) (((x) & 0x3fff) << 0) -#define VEPU_REG_MVC_RELATE 0x198 -#define VEPU_REG_ZERO_MV_FAVOR_D2(x) (((x) & 0xf) << 20) -#define VEPU_REG_PENALTY_4X4MV(x) (((x) & 0x1ff) << 11) -#define VEPU_REG_MVC_VIEW_ID(x) (((x) & 0x7) << 8) -#define VEPU_REG_MVC_ANCHOR_PIC_FLAG BIT(7) -#define VEPU_REG_MVC_PRIORITY_ID(x) (((x) & 0x7) << 4) -#define VEPU_REG_MVC_TEMPORAL_ID(x) (((x) & 0x7) << 1) -#define VEPU_REG_MVC_INTER_VIEW_FLAG BIT(0) -#define VEPU_REG_ENCODE_START 0x19c -#define VEPU_REG_MB_HEIGHT(x) (((x) & 0x1ff) << 20) -#define VEPU_REG_MB_WIDTH(x) (((x) & 0x1ff) << 8) -#define VEPU_REG_FRAME_TYPE_INTER (0x0 << 6) -#define VEPU_REG_FRAME_TYPE_INTRA (0x1 << 6) -#define VEPU_REG_FRAME_TYPE_MVCINTER (0x2 << 6) -#define VEPU_REG_ENCODE_FORMAT_JPEG (0x2 << 4) -#define VEPU_REG_ENCODE_FORMAT_H264 (0x3 << 4) -#define VEPU_REG_ENCODE_ENABLE BIT(0) -#define VEPU_REG_MB_CTRL 0x1a0 -#define VEPU_REG_MB_CNT_OUT(x) (((x) & 0xffff) << 16) -#define VEPU_REG_MB_CNT_SET(x) (((x) & 0xffff) << 0) -#define VEPU_REG_DATA_ENDIAN 0x1a4 -#define VEPU_REG_INPUT_SWAP8 BIT(31) -#define VEPU_REG_INPUT_SWAP16 BIT(30) -#define VEPU_REG_INPUT_SWAP32 BIT(29) -#define VEPU_REG_OUTPUT_SWAP8 BIT(28) -#define VEPU_REG_OUTPUT_SWAP16 BIT(27) -#define VEPU_REG_OUTPUT_SWAP32 BIT(26) -#define VEPU_REG_TEST_IRQ BIT(24) -#define VEPU_REG_TEST_COUNTER(x) (((x) & 0xf) << 20) -#define VEPU_REG_TEST_REG BIT(19) -#define VEPU_REG_TEST_MEMORY BIT(18) -#define VEPU_REG_TEST_LEN(x) (((x) & 0x3ffff) << 0) -#define VEPU_REG_ENC_CTRL3 0x1a8 -#define VEPU_REG_PPS_ID(x) (((x) & 0xff) << 24) -#define VEPU_REG_INTRA_PRED_MODE(x) (((x) & 0xff) << 16) -#define VEPU_REG_FRAME_NUM(x) (((x) & 0xffff) << 0) -#define VEPU_REG_ENC_CTRL4 0x1ac -#define VEPU_REG_MV_PENALTY_16X8_8X16(x) (((x) & 0x3ff) << 20) -#define VEPU_REG_MV_PENALTY_8X8(x) (((x) & 0x3ff) << 10) -#define VEPU_REG_MV_PENALTY_8X4_4X8(x) (((x) & 0x3ff) << 0) -#define VEPU_REG_ADDR_VP8_PROB_CNT 0x1b0 -#define VEPU_REG_INTERRUPT 0x1b4 -#define VEPU_REG_INTERRUPT_NON BIT(28) -#define VEPU_REG_MV_WRITE_EN BIT(24) -#define VEPU_REG_RECON_WRITE_DIS BIT(20) -#define VEPU_REG_INTERRUPT_SLICE_READY_EN BIT(16) -#define VEPU_REG_CLK_GATING_EN BIT(12) -#define VEPU_REG_INTERRUPT_TIMEOUT_EN BIT(10) -#define VEPU_REG_INTERRUPT_RESET BIT(9) -#define VEPU_REG_INTERRUPT_DIS_BIT BIT(8) -#define VEPU_REG_INTERRUPT_TIMEOUT BIT(6) -#define VEPU_REG_INTERRUPT_BUFFER_FULL BIT(5) -#define VEPU_REG_INTERRUPT_BUS_ERROR BIT(4) -#define VEPU_REG_INTERRUPT_FUSE BIT(3) -#define VEPU_REG_INTERRUPT_SLICE_READY BIT(2) -#define VEPU_REG_INTERRUPT_FRAME_READY BIT(1) -#define VEPU_REG_INTERRUPT_BIT BIT(0) -#define VEPU_REG_DMV_PENALTY_TBL(i) (0x1E0 + ((i) * 0x4)) -#define VEPU_REG_DMV_PENALTY_TABLE_BIT(x, i) ((x) << (i) * 8) -#define VEPU_REG_DMV_Q_PIXEL_PENALTY_TBL(i) (0x260 + ((i) * 0x4)) -#define VEPU_REG_DMV_Q_PIXEL_PENALTY_TABLE_BIT(x, i) ((x) << (i) * 8) - -/* vpu decoder register */ -#define VDPU_REG_DEC_CTRL0 0x0c8 // 50 -#define VDPU_REG_REF_BUF_CTRL2_REFBU2_PICID(x) (((x) & 0x1f) << 25) -#define VDPU_REG_REF_BUF_CTRL2_REFBU2_THR(x) (((x) & 0xfff) << 13) -#define VDPU_REG_CONFIG_TILED_MODE_LSB BIT(12) -#define VDPU_REG_CONFIG_DEC_ADV_PRE_DIS BIT(11) -#define VDPU_REG_CONFIG_DEC_SCMD_DIS BIT(10) -#define VDPU_REG_DEC_CTRL0_SKIP_MODE BIT(9) -#define VDPU_REG_DEC_CTRL0_FILTERING_DIS BIT(8) -#define VDPU_REG_DEC_CTRL0_PIC_FIXED_QUANT BIT(7) -#define VDPU_REG_CONFIG_DEC_LATENCY(x) (((x) & 0x3f) << 1) -#define VDPU_REG_CONFIG_TILED_MODE_MSB(x) BIT(0) -#define VDPU_REG_CONFIG_DEC_OUT_TILED_E BIT(0) -#define VDPU_REG_STREAM_LEN 0x0cc -#define VDPU_REG_DEC_CTRL3_INIT_QP(x) (((x) & 0x3f) << 25) -#define VDPU_REG_DEC_STREAM_LEN_HI BIT(24) -#define VDPU_REG_DEC_CTRL3_STREAM_LEN(x) (((x) & 0xffffff) << 0) -#define VDPU_REG_ERROR_CONCEALMENT 0x0d0 -#define VDPU_REG_REF_BUF_CTRL2_APF_THRESHOLD(x) (((x) & 0x3fff) << 17) -#define VDPU_REG_ERR_CONC_STARTMB_X(x) (((x) & 0x1ff) << 8) -#define VDPU_REG_ERR_CONC_STARTMB_Y(x) (((x) & 0xff) << 0) -#define VDPU_REG_DEC_FORMAT 0x0d4 -#define VDPU_REG_DEC_CTRL0_DEC_MODE(x) (((x) & 0xf) << 0) -#define VDPU_REG_DATA_ENDIAN 0x0d8 -#define VDPU_REG_CONFIG_DEC_STRENDIAN_E BIT(5) -#define VDPU_REG_CONFIG_DEC_STRSWAP32_E BIT(4) -#define VDPU_REG_CONFIG_DEC_OUTSWAP32_E BIT(3) -#define VDPU_REG_CONFIG_DEC_INSWAP32_E BIT(2) -#define VDPU_REG_CONFIG_DEC_OUT_ENDIAN BIT(1) -#define VDPU_REG_CONFIG_DEC_IN_ENDIAN BIT(0) -#define VDPU_REG_INTERRUPT 0x0dc -#define VDPU_REG_INTERRUPT_DEC_TIMEOUT BIT(13) -#define VDPU_REG_INTERRUPT_DEC_ERROR_INT BIT(12) -#define VDPU_REG_INTERRUPT_DEC_PIC_INF BIT(10) -#define VDPU_REG_INTERRUPT_DEC_SLICE_INT BIT(9) -#define VDPU_REG_INTERRUPT_DEC_ASO_INT BIT(8) -#define VDPU_REG_INTERRUPT_DEC_BUFFER_INT BIT(6) -#define VDPU_REG_INTERRUPT_DEC_BUS_INT BIT(5) -#define VDPU_REG_INTERRUPT_DEC_RDY_INT BIT(4) -#define VDPU_REG_INTERRUPT_DEC_IRQ_DIS BIT(1) -#define VDPU_REG_INTERRUPT_DEC_IRQ BIT(0) -#define VDPU_REG_AXI_CTRL 0x0e0 -#define VDPU_REG_AXI_DEC_SEL BIT(23) -#define VDPU_REG_CONFIG_DEC_DATA_DISC_E BIT(22) -#define VDPU_REG_PARAL_BUS_E(x) BIT(21) -#define VDPU_REG_CONFIG_DEC_MAX_BURST(x) (((x) & 0x1f) << 16) -#define VDPU_REG_DEC_CTRL0_DEC_AXI_WR_ID(x) (((x) & 0xff) << 8) -#define VDPU_REG_CONFIG_DEC_AXI_RD_ID(x) (((x) & 0xff) << 0) -#define VDPU_REG_EN_FLAGS 0x0e4 -#define VDPU_REG_AHB_HLOCK_E BIT(31) -#define VDPU_REG_CACHE_E BIT(29) -#define VDPU_REG_PREFETCH_SINGLE_CHANNEL_E BIT(28) -#define VDPU_REG_INTRA_3_CYCLE_ENHANCE BIT(27) -#define VDPU_REG_INTRA_DOUBLE_SPEED BIT(26) -#define VDPU_REG_INTER_DOUBLE_SPEED BIT(25) -#define VDPU_REG_DEC_CTRL3_START_CODE_E BIT(22) -#define VDPU_REG_DEC_CTRL3_CH_8PIX_ILEAV_E BIT(21) -#define VDPU_REG_DEC_CTRL0_RLC_MODE_E BIT(20) -#define VDPU_REG_DEC_CTRL0_DIVX3_E BIT(19) -#define VDPU_REG_DEC_CTRL0_PJPEG_E BIT(18) -#define VDPU_REG_DEC_CTRL0_PIC_INTERLACE_E BIT(17) -#define VDPU_REG_DEC_CTRL0_PIC_FIELDMODE_E BIT(16) -#define VDPU_REG_DEC_CTRL0_PIC_B_E BIT(15) -#define VDPU_REG_DEC_CTRL0_PIC_INTER_E BIT(14) -#define VDPU_REG_DEC_CTRL0_PIC_TOPFIELD_E BIT(13) -#define VDPU_REG_DEC_CTRL0_FWD_INTERLACE_E BIT(12) -#define VDPU_REG_DEC_CTRL0_SORENSON_E BIT(11) -#define VDPU_REG_DEC_CTRL0_WRITE_MVS_E BIT(10) -#define VDPU_REG_DEC_CTRL0_REF_TOPFIELD_E BIT(9) -#define VDPU_REG_DEC_CTRL0_REFTOPFIRST_E BIT(8) -#define VDPU_REG_DEC_CTRL0_SEQ_MBAFF_E BIT(7) -#define VDPU_REG_DEC_CTRL0_PICORD_COUNT_E BIT(6) -#define VDPU_REG_CONFIG_DEC_TIMEOUT_E BIT(5) -#define VDPU_REG_CONFIG_DEC_CLK_GATE_E BIT(4) -#define VDPU_REG_DEC_CTRL0_DEC_OUT_DIS BIT(2) -#define VDPU_REG_REF_BUF_CTRL2_REFBU2_BUF_E BIT(1) -#define VDPU_REG_INTERRUPT_DEC_E BIT(0) -#define VDPU_REG_SOFT_RESET 0x0e8 -#define VDPU_REG_PRED_FLT 0x0ec -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_0_0(x) (((x) & 0x3ff) << 22) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_0_1(x) (((x) & 0x3ff) << 12) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_0_2(x) (((x) & 0x3ff) << 2) -#define VDPU_REG_ADDITIONAL_CHROMA_ADDRESS 0x0f0 -#define VDPU_REG_ADDR_QTABLE 0x0f4 -#define VDPU_REG_DIRECT_MV_ADDR 0x0f8 -#define VDPU_REG_ADDR_DST 0x0fc -#define VDPU_REG_ADDR_STR 0x100 -#define VDPU_REG_REFBUF_RELATED 0x104 -#define VDPU_REG_FWD_PIC(i) (0x128 + ((i) * 0x4)) -#define VDPU_REG_FWD_PIC_PINIT_RLIST_F5(x) (((x) & 0x1f) << 25) -#define VDPU_REG_FWD_PIC_PINIT_RLIST_F4(x) (((x) & 0x1f) << 20) -#define VDPU_REG_FWD_PIC_PINIT_RLIST_F3(x) (((x) & 0x1f) << 15) -#define VDPU_REG_FWD_PIC_PINIT_RLIST_F2(x) (((x) & 0x1f) << 10) -#define VDPU_REG_FWD_PIC_PINIT_RLIST_F1(x) (((x) & 0x1f) << 5) -#define VDPU_REG_FWD_PIC_PINIT_RLIST_F0(x) (((x) & 0x1f) << 0) -#define VDPU_REG_REF_PIC(i) (0x130 + ((i) * 0x4)) -#define VDPU_REG_REF_PIC_REFER1_NBR(x) (((x) & 0xffff) << 16) -#define VDPU_REG_REF_PIC_REFER0_NBR(x) (((x) & 0xffff) << 0) -#define VDPU_REG_H264_ADDR_REF(i) (0x150 + ((i) * 0x4)) -#define VDPU_REG_ADDR_REF_FIELD_E BIT(1) -#define VDPU_REG_ADDR_REF_TOPC_E BIT(0) -#define VDPU_REG_INITIAL_REF_PIC_LIST0 0x190 -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F5(x) (((x) & 0x1f) << 25) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F4(x) (((x) & 0x1f) << 20) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F3(x) (((x) & 0x1f) << 15) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F2(x) (((x) & 0x1f) << 10) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F1(x) (((x) & 0x1f) << 5) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F0(x) (((x) & 0x1f) << 0) -#define VDPU_REG_INITIAL_REF_PIC_LIST1 0x194 -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F11(x) (((x) & 0x1f) << 25) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F10(x) (((x) & 0x1f) << 20) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F9(x) (((x) & 0x1f) << 15) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F8(x) (((x) & 0x1f) << 10) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F7(x) (((x) & 0x1f) << 5) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F6(x) (((x) & 0x1f) << 0) -#define VDPU_REG_INITIAL_REF_PIC_LIST2 0x198 -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F15(x) (((x) & 0x1f) << 15) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F14(x) (((x) & 0x1f) << 10) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F13(x) (((x) & 0x1f) << 5) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_F12(x) (((x) & 0x1f) << 0) -#define VDPU_REG_INITIAL_REF_PIC_LIST3 0x19c -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B5(x) (((x) & 0x1f) << 25) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B4(x) (((x) & 0x1f) << 20) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B3(x) (((x) & 0x1f) << 15) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B2(x) (((x) & 0x1f) << 10) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B1(x) (((x) & 0x1f) << 5) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B0(x) (((x) & 0x1f) << 0) -#define VDPU_REG_INITIAL_REF_PIC_LIST4 0x1a0 -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B11(x) (((x) & 0x1f) << 25) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B10(x) (((x) & 0x1f) << 20) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B9(x) (((x) & 0x1f) << 15) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B8(x) (((x) & 0x1f) << 10) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B7(x) (((x) & 0x1f) << 5) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B6(x) (((x) & 0x1f) << 0) -#define VDPU_REG_INITIAL_REF_PIC_LIST5 0x1a4 -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B15(x) (((x) & 0x1f) << 15) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B14(x) (((x) & 0x1f) << 10) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B13(x) (((x) & 0x1f) << 5) -#define VDPU_REG_BD_REF_PIC_BINIT_RLIST_B12(x) (((x) & 0x1f) << 0) -#define VDPU_REG_INITIAL_REF_PIC_LIST6 0x1a8 -#define VDPU_REG_BD_P_REF_PIC_PINIT_RLIST_F3(x) (((x) & 0x1f) << 15) -#define VDPU_REG_BD_P_REF_PIC_PINIT_RLIST_F2(x) (((x) & 0x1f) << 10) -#define VDPU_REG_BD_P_REF_PIC_PINIT_RLIST_F1(x) (((x) & 0x1f) << 5) -#define VDPU_REG_BD_P_REF_PIC_PINIT_RLIST_F0(x) (((x) & 0x1f) << 0) -#define VDPU_REG_LT_REF 0x1ac -#define VDPU_REG_VALID_REF 0x1b0 -#define VDPU_REG_H264_PIC_MB_SIZE 0x1b8 -#define VDPU_REG_DEC_CTRL2_CH_QP_OFFSET2(x) (((x) & 0x1f) << 22) -#define VDPU_REG_DEC_CTRL2_CH_QP_OFFSET(x) (((x) & 0x1f) << 17) -#define VDPU_REG_DEC_CTRL1_PIC_MB_HEIGHT_P(x) (((x) & 0xff) << 9) -#define VDPU_REG_DEC_CTRL1_PIC_MB_WIDTH(x) (((x) & 0x1ff) << 0) -#define VDPU_REG_H264_CTRL 0x1bc -#define VDPU_REG_DEC_CTRL4_WEIGHT_BIPR_IDC(x) (((x) & 0x3) << 16) -#define VDPU_REG_DEC_CTRL1_REF_FRAMES(x) (((x) & 0x1f) << 0) -#define VDPU_REG_CURRENT_FRAME 0x1c0 -#define VDPU_REG_DEC_CTRL5_FILT_CTRL_PRES BIT(31) -#define VDPU_REG_DEC_CTRL5_RDPIC_CNT_PRES BIT(30) -#define VDPU_REG_DEC_CTRL4_FRAMENUM_LEN(x) (((x) & 0x1f) << 16) -#define VDPU_REG_DEC_CTRL4_FRAMENUM(x) (((x) & 0xffff) << 0) -#define VDPU_REG_REF_FRAME 0x1c4 -#define VDPU_REG_DEC_CTRL5_REFPIC_MK_LEN(x) (((x) & 0x7ff) << 16) -#define VDPU_REG_DEC_CTRL5_IDR_PIC_ID(x) (((x) & 0xffff) << 0) -#define VDPU_REG_DEC_CTRL6 0x1c8 -#define VDPU_REG_DEC_CTRL6_PPS_ID(x) (((x) & 0xff) << 24) -#define VDPU_REG_DEC_CTRL6_REFIDX1_ACTIVE(x) (((x) & 0x1f) << 19) -#define VDPU_REG_DEC_CTRL6_REFIDX0_ACTIVE(x) (((x) & 0x1f) << 14) -#define VDPU_REG_DEC_CTRL6_POC_LENGTH(x) (((x) & 0xff) << 0) -#define VDPU_REG_ENABLE_FLAG 0x1cc -#define VDPU_REG_DEC_CTRL5_IDR_PIC_E BIT(8) -#define VDPU_REG_DEC_CTRL4_DIR_8X8_INFER_E BIT(7) -#define VDPU_REG_DEC_CTRL4_BLACKWHITE_E BIT(6) -#define VDPU_REG_DEC_CTRL4_CABAC_E BIT(5) -#define VDPU_REG_DEC_CTRL4_WEIGHT_PRED_E BIT(4) -#define VDPU_REG_DEC_CTRL5_CONST_INTRA_E BIT(3) -#define VDPU_REG_DEC_CTRL5_8X8TRANS_FLAG_E BIT(2) -#define VDPU_REG_DEC_CTRL2_TYPE1_QUANT_E BIT(1) -#define VDPU_REG_DEC_CTRL2_FIELDPIC_FLAG_E BIT(0) -#define VDPU_REG_VP8_PIC_MB_SIZE 0x1e0 -#define VDPU_REG_DEC_PIC_MB_WIDTH(x) (((x) & 0x1ff) << 23) -#define VDPU_REG_DEC_MB_WIDTH_OFF(x) (((x) & 0xf) << 19) -#define VDPU_REG_DEC_PIC_MB_HEIGHT_P(x) (((x) & 0xff) << 11) -#define VDPU_REG_DEC_MB_HEIGHT_OFF(x) (((x) & 0xf) << 7) -#define VDPU_REG_DEC_CTRL1_PIC_MB_W_EXT(x) (((x) & 0x7) << 3) -#define VDPU_REG_DEC_CTRL1_PIC_MB_H_EXT(x) (((x) & 0x7) << 0) -#define VDPU_REG_VP8_DCT_START_BIT 0x1e4 -#define VDPU_REG_DEC_CTRL4_DCT1_START_BIT(x) (((x) & 0x3f) << 26) -#define VDPU_REG_DEC_CTRL4_DCT2_START_BIT(x) (((x) & 0x3f) << 20) -#define VDPU_REG_DEC_CTRL4_VC1_HEIGHT_EXT BIT(13) -#define VDPU_REG_DEC_CTRL4_BILIN_MC_E BIT(12) -#define VDPU_REG_VP8_CTRL0 0x1e8 -#define VDPU_REG_DEC_CTRL2_STRM_START_BIT(x) (((x) & 0x3f) << 26) -#define VDPU_REG_DEC_CTRL2_STRM1_START_BIT(x) (((x) & 0x3f) << 18) -#define VDPU_REG_DEC_CTRL2_BOOLEAN_VALUE(x) (((x) & 0xff) << 8) -#define VDPU_REG_DEC_CTRL2_BOOLEAN_RANGE(x) (((x) & 0xff) << 0) -#define VDPU_REG_VP8_DATA_VAL 0x1f0 -#define VDPU_REG_DEC_CTRL6_COEFFS_PART_AM(x) (((x) & 0xf) << 24) -#define VDPU_REG_DEC_CTRL6_STREAM1_LEN(x) (((x) & 0xffffff) << 0) -#define VDPU_REG_PRED_FLT7 0x1f4 -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_5_1(x) (((x) & 0x3ff) << 22) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_5_2(x) (((x) & 0x3ff) << 12) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_5_3(x) (((x) & 0x3ff) << 2) -#define VDPU_REG_PRED_FLT8 0x1f8 -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_6_0(x) (((x) & 0x3ff) << 22) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_6_1(x) (((x) & 0x3ff) << 12) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_6_2(x) (((x) & 0x3ff) << 2) -#define VDPU_REG_PRED_FLT9 0x1fc -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_6_3(x) (((x) & 0x3ff) << 22) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_7_0(x) (((x) & 0x3ff) << 12) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_7_1(x) (((x) & 0x3ff) << 2) -#define VDPU_REG_PRED_FLT10 0x200 -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_7_2(x) (((x) & 0x3ff) << 22) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_7_3(x) (((x) & 0x3ff) << 12) -#define VDPU_REG_BD_REF_PIC_PRED_TAP_2_M1(x) (((x) & 0x3) << 10) -#define VDPU_REG_BD_REF_PIC_PRED_TAP_2_4(x) (((x) & 0x3) << 8) -#define VDPU_REG_BD_REF_PIC_PRED_TAP_4_M1(x) (((x) & 0x3) << 6) -#define VDPU_REG_BD_REF_PIC_PRED_TAP_4_4(x) (((x) & 0x3) << 4) -#define VDPU_REG_BD_REF_PIC_PRED_TAP_6_M1(x) (((x) & 0x3) << 2) -#define VDPU_REG_BD_REF_PIC_PRED_TAP_6_4(x) (((x) & 0x3) << 0) -#define VDPU_REG_FILTER_LEVEL 0x204 -#define VDPU_REG_REF_PIC_LF_LEVEL_0(x) (((x) & 0x3f) << 18) -#define VDPU_REG_REF_PIC_LF_LEVEL_1(x) (((x) & 0x3f) << 12) -#define VDPU_REG_REF_PIC_LF_LEVEL_2(x) (((x) & 0x3f) << 6) -#define VDPU_REG_REF_PIC_LF_LEVEL_3(x) (((x) & 0x3f) << 0) -#define VDPU_REG_VP8_QUANTER0 0x208 -#define VDPU_REG_REF_PIC_QUANT_DELTA_0(x) (((x) & 0x1f) << 27) -#define VDPU_REG_REF_PIC_QUANT_DELTA_1(x) (((x) & 0x1f) << 22) -#define VDPU_REG_REF_PIC_QUANT_0(x) (((x) & 0x7ff) << 11) -#define VDPU_REG_REF_PIC_QUANT_1(x) (((x) & 0x7ff) << 0) -#define VDPU_REG_VP8_ADDR_REF0 0x20c -#define VDPU_REG_FILTER_MB_ADJ 0x210 -#define VDPU_REG_REF_PIC_FILT_TYPE_E BIT(31) -#define VDPU_REG_REF_PIC_FILT_SHARPNESS(x) (((x) & 0x7) << 28) -#define VDPU_REG_FILT_MB_ADJ_0(x) (((x) & 0x7f) << 21) -#define VDPU_REG_FILT_MB_ADJ_1(x) (((x) & 0x7f) << 14) -#define VDPU_REG_FILT_MB_ADJ_2(x) (((x) & 0x7f) << 7) -#define VDPU_REG_FILT_MB_ADJ_3(x) (((x) & 0x7f) << 0) -#define VDPU_REG_FILTER_REF_ADJ 0x214 -#define VDPU_REG_REF_PIC_ADJ_0(x) (((x) & 0x7f) << 21) -#define VDPU_REG_REF_PIC_ADJ_1(x) (((x) & 0x7f) << 14) -#define VDPU_REG_REF_PIC_ADJ_2(x) (((x) & 0x7f) << 7) -#define VDPU_REG_REF_PIC_ADJ_3(x) (((x) & 0x7f) << 0) -#define VDPU_REG_VP8_ADDR_REF2_5(i) (0x218 + ((i) * 0x4)) -#define VDPU_REG_VP8_GREF_SIGN_BIAS BIT(0) -#define VDPU_REG_VP8_AREF_SIGN_BIAS BIT(0) -#define VDPU_REG_VP8_DCT_BASE(i) (0x230 + ((i) * 0x4)) -#define VDPU_REG_VP8_ADDR_CTRL_PART 0x244 -#define VDPU_REG_VP8_ADDR_REF1 0x250 -#define VDPU_REG_VP8_SEGMENT_VAL 0x254 -#define VDPU_REG_FWD_PIC1_SEGMENT_BASE(x) ((x) << 0) -#define VDPU_REG_FWD_PIC1_SEGMENT_UPD_E BIT(1) -#define VDPU_REG_FWD_PIC1_SEGMENT_E BIT(0) -#define VDPU_REG_VP8_DCT_START_BIT2 0x258 -#define VDPU_REG_DEC_CTRL7_DCT3_START_BIT(x) (((x) & 0x3f) << 24) -#define VDPU_REG_DEC_CTRL7_DCT4_START_BIT(x) (((x) & 0x3f) << 18) -#define VDPU_REG_DEC_CTRL7_DCT5_START_BIT(x) (((x) & 0x3f) << 12) -#define VDPU_REG_DEC_CTRL7_DCT6_START_BIT(x) (((x) & 0x3f) << 6) -#define VDPU_REG_DEC_CTRL7_DCT7_START_BIT(x) (((x) & 0x3f) << 0) -#define VDPU_REG_VP8_QUANTER1 0x25c -#define VDPU_REG_REF_PIC_QUANT_DELTA_2(x) (((x) & 0x1f) << 27) -#define VDPU_REG_REF_PIC_QUANT_DELTA_3(x) (((x) & 0x1f) << 22) -#define VDPU_REG_REF_PIC_QUANT_2(x) (((x) & 0x7ff) << 11) -#define VDPU_REG_REF_PIC_QUANT_3(x) (((x) & 0x7ff) << 0) -#define VDPU_REG_VP8_QUANTER2 0x260 -#define VDPU_REG_REF_PIC_QUANT_DELTA_4(x) (((x) & 0x1f) << 27) -#define VDPU_REG_REF_PIC_QUANT_4(x) (((x) & 0x7ff) << 11) -#define VDPU_REG_REF_PIC_QUANT_5(x) (((x) & 0x7ff) << 0) -#define VDPU_REG_PRED_FLT1 0x264 -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_0_3(x) (((x) & 0x3ff) << 22) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_1_0(x) (((x) & 0x3ff) << 12) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_1_1(x) (((x) & 0x3ff) << 2) -#define VDPU_REG_PRED_FLT2 0x268 -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_1_2(x) (((x) & 0x3ff) << 22) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_1_3(x) (((x) & 0x3ff) << 12) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_2_0(x) (((x) & 0x3ff) << 2) -#define VDPU_REG_PRED_FLT3 0x26c -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_2_1(x) (((x) & 0x3ff) << 22) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_2_2(x) (((x) & 0x3ff) << 12) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_2_3(x) (((x) & 0x3ff) << 2) -#define VDPU_REG_PRED_FLT4 0x270 -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_3_0(x) (((x) & 0x3ff) << 22) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_3_1(x) (((x) & 0x3ff) << 12) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_3_2(x) (((x) & 0x3ff) << 2) -#define VDPU_REG_PRED_FLT5 0x274 -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_3_3(x) (((x) & 0x3ff) << 22) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_4_0(x) (((x) & 0x3ff) << 12) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_4_1(x) (((x) & 0x3ff) << 2) -#define VDPU_REG_PRED_FLT6 0x278 -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_4_2(x) (((x) & 0x3ff) << 22) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_4_3(x) (((x) & 0x3ff) << 12) -#define VDPU_REG_PRED_FLT_PRED_BC_TAP_5_0(x) (((x) & 0x3ff) << 2) - -#endif /* ROCKCHIP_VPU2_REGS_H_ */ diff --git a/drivers/staging/media/hantro/rockchip_vpu_hw.c b/drivers/staging/media/hantro/rockchip_vpu_hw.c deleted file mode 100644 index 8de6fd2e8eef..000000000000 --- a/drivers/staging/media/hantro/rockchip_vpu_hw.c +++ /dev/null @@ -1,680 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VPU codec driver - * - * Copyright (C) 2018 Rockchip Electronics Co., Ltd. - * Jeffy Chen <jeffy.chen@rock-chips.com> - */ - -#include <linux/clk.h> - -#include "hantro.h" -#include "hantro_jpeg.h" -#include "hantro_g1_regs.h" -#include "hantro_h1_regs.h" -#include "rockchip_vpu2_regs.h" - -#define RK3066_ACLK_MAX_FREQ (300 * 1000 * 1000) -#define RK3288_ACLK_MAX_FREQ (400 * 1000 * 1000) - -/* - * Supported formats. - */ - -static const struct hantro_fmt rockchip_vpu_enc_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_YUV420M, - .codec_mode = HANTRO_MODE_NONE, - .enc_fmt = ROCKCHIP_VPU_ENC_FMT_YUV420P, - }, - { - .fourcc = V4L2_PIX_FMT_NV12M, - .codec_mode = HANTRO_MODE_NONE, - .enc_fmt = ROCKCHIP_VPU_ENC_FMT_YUV420SP, - }, - { - .fourcc = V4L2_PIX_FMT_YUYV, - .codec_mode = HANTRO_MODE_NONE, - .enc_fmt = ROCKCHIP_VPU_ENC_FMT_YUYV422, - }, - { - .fourcc = V4L2_PIX_FMT_UYVY, - .codec_mode = HANTRO_MODE_NONE, - .enc_fmt = ROCKCHIP_VPU_ENC_FMT_UYVY422, - }, - { - .fourcc = V4L2_PIX_FMT_JPEG, - .codec_mode = HANTRO_MODE_JPEG_ENC, - .max_depth = 2, - .header_size = JPEG_HEADER_SIZE, - .frmsize = { - .min_width = 96, - .max_width = 8192, - .step_width = MB_DIM, - .min_height = 32, - .max_height = 8192, - .step_height = MB_DIM, - }, - }, -}; - -static const struct hantro_fmt rockchip_vpu1_postproc_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_YUYV, - .codec_mode = HANTRO_MODE_NONE, - .postprocessed = true, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_FHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_FHD_HEIGHT, - .step_height = MB_DIM, - }, - }, -}; - -static const struct hantro_fmt rk3066_vpu_dec_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_NV12, - .codec_mode = HANTRO_MODE_NONE, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_FHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_FHD_HEIGHT, - .step_height = MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_H264_SLICE, - .codec_mode = HANTRO_MODE_H264_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_FHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_FHD_HEIGHT, - .step_height = MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_MPEG2_SLICE, - .codec_mode = HANTRO_MODE_MPEG2_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_FHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_FHD_HEIGHT, - .step_height = MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_VP8_FRAME, - .codec_mode = HANTRO_MODE_VP8_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_FHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_FHD_HEIGHT, - .step_height = MB_DIM, - }, - }, -}; - -static const struct hantro_fmt rk3288_vpu_dec_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_NV12, - .codec_mode = HANTRO_MODE_NONE, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_4K_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_4K_HEIGHT, - .step_height = MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_H264_SLICE, - .codec_mode = HANTRO_MODE_H264_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_4K_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_4K_HEIGHT, - .step_height = MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_MPEG2_SLICE, - .codec_mode = HANTRO_MODE_MPEG2_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_FHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_FHD_HEIGHT, - .step_height = MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_VP8_FRAME, - .codec_mode = HANTRO_MODE_VP8_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_UHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_UHD_HEIGHT, - .step_height = MB_DIM, - }, - }, -}; - -static const struct hantro_fmt rockchip_vdpu2_dec_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_NV12, - .codec_mode = HANTRO_MODE_NONE, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_FHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_FHD_HEIGHT, - .step_height = MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_H264_SLICE, - .codec_mode = HANTRO_MODE_H264_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_FHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_FHD_HEIGHT, - .step_height = MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_MPEG2_SLICE, - .codec_mode = HANTRO_MODE_MPEG2_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_FHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_FHD_HEIGHT, - .step_height = MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_VP8_FRAME, - .codec_mode = HANTRO_MODE_VP8_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_UHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_UHD_HEIGHT, - .step_height = MB_DIM, - }, - }, -}; - -static const struct hantro_fmt rk3399_vpu_dec_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_NV12, - .codec_mode = HANTRO_MODE_NONE, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_FHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_FHD_HEIGHT, - .step_height = MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_MPEG2_SLICE, - .codec_mode = HANTRO_MODE_MPEG2_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_FHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_FHD_HEIGHT, - .step_height = MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_VP8_FRAME, - .codec_mode = HANTRO_MODE_VP8_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_UHD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_UHD_HEIGHT, - .step_height = MB_DIM, - }, - }, -}; - -static irqreturn_t rockchip_vpu1_vepu_irq(int irq, void *dev_id) -{ - struct hantro_dev *vpu = dev_id; - enum vb2_buffer_state state; - u32 status; - - status = vepu_read(vpu, H1_REG_INTERRUPT); - state = (status & H1_REG_INTERRUPT_FRAME_RDY) ? - VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR; - - vepu_write(vpu, 0, H1_REG_INTERRUPT); - vepu_write(vpu, 0, H1_REG_AXI_CTRL); - - hantro_irq_done(vpu, state); - - return IRQ_HANDLED; -} - -static irqreturn_t rockchip_vpu2_vdpu_irq(int irq, void *dev_id) -{ - struct hantro_dev *vpu = dev_id; - enum vb2_buffer_state state; - u32 status; - - status = vdpu_read(vpu, VDPU_REG_INTERRUPT); - state = (status & VDPU_REG_INTERRUPT_DEC_IRQ) ? - VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR; - - vdpu_write(vpu, 0, VDPU_REG_INTERRUPT); - vdpu_write(vpu, 0, VDPU_REG_AXI_CTRL); - - hantro_irq_done(vpu, state); - - return IRQ_HANDLED; -} - -static irqreturn_t rockchip_vpu2_vepu_irq(int irq, void *dev_id) -{ - struct hantro_dev *vpu = dev_id; - enum vb2_buffer_state state; - u32 status; - - status = vepu_read(vpu, VEPU_REG_INTERRUPT); - state = (status & VEPU_REG_INTERRUPT_FRAME_READY) ? - VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR; - - vepu_write(vpu, 0, VEPU_REG_INTERRUPT); - vepu_write(vpu, 0, VEPU_REG_AXI_CTRL); - - hantro_irq_done(vpu, state); - - return IRQ_HANDLED; -} - -static int rk3036_vpu_hw_init(struct hantro_dev *vpu) -{ - /* Bump ACLK to max. possible freq. to improve performance. */ - clk_set_rate(vpu->clocks[0].clk, RK3066_ACLK_MAX_FREQ); - return 0; -} - -static int rk3066_vpu_hw_init(struct hantro_dev *vpu) -{ - /* Bump ACLKs to max. possible freq. to improve performance. */ - clk_set_rate(vpu->clocks[0].clk, RK3066_ACLK_MAX_FREQ); - clk_set_rate(vpu->clocks[2].clk, RK3066_ACLK_MAX_FREQ); - return 0; -} - -static int rockchip_vpu_hw_init(struct hantro_dev *vpu) -{ - /* Bump ACLK to max. possible freq. to improve performance. */ - clk_set_rate(vpu->clocks[0].clk, RK3288_ACLK_MAX_FREQ); - return 0; -} - -static void rk3066_vpu_dec_reset(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - - vdpu_write(vpu, G1_REG_INTERRUPT_DEC_IRQ_DIS, G1_REG_INTERRUPT); - vdpu_write(vpu, G1_REG_CONFIG_DEC_CLK_GATE_E, G1_REG_CONFIG); -} - -static void rockchip_vpu1_enc_reset(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - - vepu_write(vpu, H1_REG_INTERRUPT_DIS_BIT, H1_REG_INTERRUPT); - vepu_write(vpu, 0, H1_REG_ENC_CTRL); - vepu_write(vpu, 0, H1_REG_AXI_CTRL); -} - -static void rockchip_vpu2_dec_reset(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - - vdpu_write(vpu, VDPU_REG_INTERRUPT_DEC_IRQ_DIS, VDPU_REG_INTERRUPT); - vdpu_write(vpu, 0, VDPU_REG_EN_FLAGS); - vdpu_write(vpu, 1, VDPU_REG_SOFT_RESET); -} - -static void rockchip_vpu2_enc_reset(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - - vepu_write(vpu, VEPU_REG_INTERRUPT_DIS_BIT, VEPU_REG_INTERRUPT); - vepu_write(vpu, 0, VEPU_REG_ENCODE_START); - vepu_write(vpu, 0, VEPU_REG_AXI_CTRL); -} - -/* - * Supported codec ops. - */ -static const struct hantro_codec_ops rk3036_vpu_codec_ops[] = { - [HANTRO_MODE_H264_DEC] = { - .run = hantro_g1_h264_dec_run, - .reset = hantro_g1_reset, - .init = hantro_h264_dec_init, - .exit = hantro_h264_dec_exit, - }, - [HANTRO_MODE_MPEG2_DEC] = { - .run = hantro_g1_mpeg2_dec_run, - .reset = hantro_g1_reset, - .init = hantro_mpeg2_dec_init, - .exit = hantro_mpeg2_dec_exit, - }, - [HANTRO_MODE_VP8_DEC] = { - .run = hantro_g1_vp8_dec_run, - .reset = hantro_g1_reset, - .init = hantro_vp8_dec_init, - .exit = hantro_vp8_dec_exit, - }, -}; - -static const struct hantro_codec_ops rk3066_vpu_codec_ops[] = { - [HANTRO_MODE_JPEG_ENC] = { - .run = hantro_h1_jpeg_enc_run, - .reset = rockchip_vpu1_enc_reset, - .done = hantro_h1_jpeg_enc_done, - }, - [HANTRO_MODE_H264_DEC] = { - .run = hantro_g1_h264_dec_run, - .reset = rk3066_vpu_dec_reset, - .init = hantro_h264_dec_init, - .exit = hantro_h264_dec_exit, - }, - [HANTRO_MODE_MPEG2_DEC] = { - .run = hantro_g1_mpeg2_dec_run, - .reset = rk3066_vpu_dec_reset, - .init = hantro_mpeg2_dec_init, - .exit = hantro_mpeg2_dec_exit, - }, - [HANTRO_MODE_VP8_DEC] = { - .run = hantro_g1_vp8_dec_run, - .reset = rk3066_vpu_dec_reset, - .init = hantro_vp8_dec_init, - .exit = hantro_vp8_dec_exit, - }, -}; - -static const struct hantro_codec_ops rk3288_vpu_codec_ops[] = { - [HANTRO_MODE_JPEG_ENC] = { - .run = hantro_h1_jpeg_enc_run, - .reset = rockchip_vpu1_enc_reset, - .done = hantro_h1_jpeg_enc_done, - }, - [HANTRO_MODE_H264_DEC] = { - .run = hantro_g1_h264_dec_run, - .reset = hantro_g1_reset, - .init = hantro_h264_dec_init, - .exit = hantro_h264_dec_exit, - }, - [HANTRO_MODE_MPEG2_DEC] = { - .run = hantro_g1_mpeg2_dec_run, - .reset = hantro_g1_reset, - .init = hantro_mpeg2_dec_init, - .exit = hantro_mpeg2_dec_exit, - }, - [HANTRO_MODE_VP8_DEC] = { - .run = hantro_g1_vp8_dec_run, - .reset = hantro_g1_reset, - .init = hantro_vp8_dec_init, - .exit = hantro_vp8_dec_exit, - }, -}; - -static const struct hantro_codec_ops rk3399_vpu_codec_ops[] = { - [HANTRO_MODE_JPEG_ENC] = { - .run = rockchip_vpu2_jpeg_enc_run, - .reset = rockchip_vpu2_enc_reset, - .done = rockchip_vpu2_jpeg_enc_done, - }, - [HANTRO_MODE_H264_DEC] = { - .run = rockchip_vpu2_h264_dec_run, - .reset = rockchip_vpu2_dec_reset, - .init = hantro_h264_dec_init, - .exit = hantro_h264_dec_exit, - }, - [HANTRO_MODE_MPEG2_DEC] = { - .run = rockchip_vpu2_mpeg2_dec_run, - .reset = rockchip_vpu2_dec_reset, - .init = hantro_mpeg2_dec_init, - .exit = hantro_mpeg2_dec_exit, - }, - [HANTRO_MODE_VP8_DEC] = { - .run = rockchip_vpu2_vp8_dec_run, - .reset = rockchip_vpu2_dec_reset, - .init = hantro_vp8_dec_init, - .exit = hantro_vp8_dec_exit, - }, -}; - -static const struct hantro_codec_ops rk3568_vepu_codec_ops[] = { - [HANTRO_MODE_JPEG_ENC] = { - .run = rockchip_vpu2_jpeg_enc_run, - .reset = rockchip_vpu2_enc_reset, - .done = rockchip_vpu2_jpeg_enc_done, - }, -}; - -/* - * VPU variant. - */ - -static const struct hantro_irq rockchip_vdpu1_irqs[] = { - { "vdpu", hantro_g1_irq }, -}; - -static const struct hantro_irq rockchip_vpu1_irqs[] = { - { "vepu", rockchip_vpu1_vepu_irq }, - { "vdpu", hantro_g1_irq }, -}; - -static const struct hantro_irq rockchip_vdpu2_irqs[] = { - { "vdpu", rockchip_vpu2_vdpu_irq }, -}; - -static const struct hantro_irq rockchip_vpu2_irqs[] = { - { "vepu", rockchip_vpu2_vepu_irq }, - { "vdpu", rockchip_vpu2_vdpu_irq }, -}; - -static const struct hantro_irq rk3568_vepu_irqs[] = { - { "vepu", rockchip_vpu2_vepu_irq }, -}; - -static const char * const rk3066_vpu_clk_names[] = { - "aclk_vdpu", "hclk_vdpu", - "aclk_vepu", "hclk_vepu" -}; - -static const char * const rockchip_vpu_clk_names[] = { - "aclk", "hclk" -}; - -/* VDPU1/VEPU1 */ - -const struct hantro_variant rk3036_vpu_variant = { - .dec_offset = 0x400, - .dec_fmts = rk3066_vpu_dec_fmts, - .num_dec_fmts = ARRAY_SIZE(rk3066_vpu_dec_fmts), - .postproc_fmts = rockchip_vpu1_postproc_fmts, - .num_postproc_fmts = ARRAY_SIZE(rockchip_vpu1_postproc_fmts), - .postproc_ops = &hantro_g1_postproc_ops, - .codec = HANTRO_MPEG2_DECODER | HANTRO_VP8_DECODER | - HANTRO_H264_DECODER, - .codec_ops = rk3036_vpu_codec_ops, - .irqs = rockchip_vdpu1_irqs, - .num_irqs = ARRAY_SIZE(rockchip_vdpu1_irqs), - .init = rk3036_vpu_hw_init, - .clk_names = rockchip_vpu_clk_names, - .num_clocks = ARRAY_SIZE(rockchip_vpu_clk_names) -}; - -/* - * Despite this variant has separate clocks for decoder and encoder, - * it's still required to enable all four of them for either decoding - * or encoding and we can't split it in separate g1/h1 variants. - */ -const struct hantro_variant rk3066_vpu_variant = { - .enc_offset = 0x0, - .enc_fmts = rockchip_vpu_enc_fmts, - .num_enc_fmts = ARRAY_SIZE(rockchip_vpu_enc_fmts), - .dec_offset = 0x400, - .dec_fmts = rk3066_vpu_dec_fmts, - .num_dec_fmts = ARRAY_SIZE(rk3066_vpu_dec_fmts), - .postproc_fmts = rockchip_vpu1_postproc_fmts, - .num_postproc_fmts = ARRAY_SIZE(rockchip_vpu1_postproc_fmts), - .postproc_ops = &hantro_g1_postproc_ops, - .codec = HANTRO_JPEG_ENCODER | HANTRO_MPEG2_DECODER | - HANTRO_VP8_DECODER | HANTRO_H264_DECODER, - .codec_ops = rk3066_vpu_codec_ops, - .irqs = rockchip_vpu1_irqs, - .num_irqs = ARRAY_SIZE(rockchip_vpu1_irqs), - .init = rk3066_vpu_hw_init, - .clk_names = rk3066_vpu_clk_names, - .num_clocks = ARRAY_SIZE(rk3066_vpu_clk_names) -}; - -const struct hantro_variant rk3288_vpu_variant = { - .enc_offset = 0x0, - .enc_fmts = rockchip_vpu_enc_fmts, - .num_enc_fmts = ARRAY_SIZE(rockchip_vpu_enc_fmts), - .dec_offset = 0x400, - .dec_fmts = rk3288_vpu_dec_fmts, - .num_dec_fmts = ARRAY_SIZE(rk3288_vpu_dec_fmts), - .postproc_fmts = rockchip_vpu1_postproc_fmts, - .num_postproc_fmts = ARRAY_SIZE(rockchip_vpu1_postproc_fmts), - .postproc_ops = &hantro_g1_postproc_ops, - .codec = HANTRO_JPEG_ENCODER | HANTRO_MPEG2_DECODER | - HANTRO_VP8_DECODER | HANTRO_H264_DECODER, - .codec_ops = rk3288_vpu_codec_ops, - .irqs = rockchip_vpu1_irqs, - .num_irqs = ARRAY_SIZE(rockchip_vpu1_irqs), - .init = rockchip_vpu_hw_init, - .clk_names = rockchip_vpu_clk_names, - .num_clocks = ARRAY_SIZE(rockchip_vpu_clk_names) -}; - -/* VDPU2/VEPU2 */ - -const struct hantro_variant rk3328_vpu_variant = { - .dec_offset = 0x400, - .dec_fmts = rockchip_vdpu2_dec_fmts, - .num_dec_fmts = ARRAY_SIZE(rockchip_vdpu2_dec_fmts), - .codec = HANTRO_MPEG2_DECODER | HANTRO_VP8_DECODER | - HANTRO_H264_DECODER, - .codec_ops = rk3399_vpu_codec_ops, - .irqs = rockchip_vdpu2_irqs, - .num_irqs = ARRAY_SIZE(rockchip_vdpu2_irqs), - .init = rockchip_vpu_hw_init, - .clk_names = rockchip_vpu_clk_names, - .num_clocks = ARRAY_SIZE(rockchip_vpu_clk_names), -}; - -/* - * H.264 decoding explicitly disabled in RK3399. - * This ensures userspace applications use the Rockchip VDEC core, - * which has better performance. - */ -const struct hantro_variant rk3399_vpu_variant = { - .enc_offset = 0x0, - .enc_fmts = rockchip_vpu_enc_fmts, - .num_enc_fmts = ARRAY_SIZE(rockchip_vpu_enc_fmts), - .dec_offset = 0x400, - .dec_fmts = rk3399_vpu_dec_fmts, - .num_dec_fmts = ARRAY_SIZE(rk3399_vpu_dec_fmts), - .codec = HANTRO_JPEG_ENCODER | HANTRO_MPEG2_DECODER | - HANTRO_VP8_DECODER, - .codec_ops = rk3399_vpu_codec_ops, - .irqs = rockchip_vpu2_irqs, - .num_irqs = ARRAY_SIZE(rockchip_vpu2_irqs), - .init = rockchip_vpu_hw_init, - .clk_names = rockchip_vpu_clk_names, - .num_clocks = ARRAY_SIZE(rockchip_vpu_clk_names) -}; - -const struct hantro_variant rk3568_vepu_variant = { - .enc_offset = 0x0, - .enc_fmts = rockchip_vpu_enc_fmts, - .num_enc_fmts = ARRAY_SIZE(rockchip_vpu_enc_fmts), - .codec = HANTRO_JPEG_ENCODER, - .codec_ops = rk3568_vepu_codec_ops, - .irqs = rk3568_vepu_irqs, - .num_irqs = ARRAY_SIZE(rk3568_vepu_irqs), - .init = rockchip_vpu_hw_init, - .clk_names = rockchip_vpu_clk_names, - .num_clocks = ARRAY_SIZE(rockchip_vpu_clk_names) -}; - -const struct hantro_variant rk3568_vpu_variant = { - .dec_offset = 0x400, - .dec_fmts = rockchip_vdpu2_dec_fmts, - .num_dec_fmts = ARRAY_SIZE(rockchip_vdpu2_dec_fmts), - .codec = HANTRO_MPEG2_DECODER | - HANTRO_VP8_DECODER | HANTRO_H264_DECODER, - .codec_ops = rk3399_vpu_codec_ops, - .irqs = rockchip_vdpu2_irqs, - .num_irqs = ARRAY_SIZE(rockchip_vdpu2_irqs), - .init = rockchip_vpu_hw_init, - .clk_names = rockchip_vpu_clk_names, - .num_clocks = ARRAY_SIZE(rockchip_vpu_clk_names) -}; - -const struct hantro_variant px30_vpu_variant = { - .enc_offset = 0x0, - .enc_fmts = rockchip_vpu_enc_fmts, - .num_enc_fmts = ARRAY_SIZE(rockchip_vpu_enc_fmts), - .dec_offset = 0x400, - .dec_fmts = rockchip_vdpu2_dec_fmts, - .num_dec_fmts = ARRAY_SIZE(rockchip_vdpu2_dec_fmts), - .codec = HANTRO_JPEG_ENCODER | HANTRO_MPEG2_DECODER | - HANTRO_VP8_DECODER | HANTRO_H264_DECODER, - .codec_ops = rk3399_vpu_codec_ops, - .irqs = rockchip_vpu2_irqs, - .num_irqs = ARRAY_SIZE(rockchip_vpu2_irqs), - .init = rk3036_vpu_hw_init, - .clk_names = rockchip_vpu_clk_names, - .num_clocks = ARRAY_SIZE(rockchip_vpu_clk_names) -}; diff --git a/drivers/staging/media/hantro/sama5d4_vdec_hw.c b/drivers/staging/media/hantro/sama5d4_vdec_hw.c deleted file mode 100644 index b205e2db5b04..000000000000 --- a/drivers/staging/media/hantro/sama5d4_vdec_hw.c +++ /dev/null @@ -1,128 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Hantro VDEC driver - * - * Copyright (C) 2021 Collabora Ltd, Emil Velikov <emil.velikov@collabora.com> - */ - -#include "hantro.h" - -/* - * Supported formats. - */ - -static const struct hantro_fmt sama5d4_vdec_postproc_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_YUYV, - .codec_mode = HANTRO_MODE_NONE, - .postprocessed = true, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_HD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_HD_HEIGHT, - .step_height = MB_DIM, - }, - }, -}; - -static const struct hantro_fmt sama5d4_vdec_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_NV12, - .codec_mode = HANTRO_MODE_NONE, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_HD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_HD_HEIGHT, - .step_height = MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_MPEG2_SLICE, - .codec_mode = HANTRO_MODE_MPEG2_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_HD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_HD_HEIGHT, - .step_height = MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_VP8_FRAME, - .codec_mode = HANTRO_MODE_VP8_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_HD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_HD_HEIGHT, - .step_height = MB_DIM, - }, - }, - { - .fourcc = V4L2_PIX_FMT_H264_SLICE, - .codec_mode = HANTRO_MODE_H264_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_HD_WIDTH, - .step_width = MB_DIM, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_HD_HEIGHT, - .step_height = MB_DIM, - }, - }, -}; - -/* - * Supported codec ops. - */ - -static const struct hantro_codec_ops sama5d4_vdec_codec_ops[] = { - [HANTRO_MODE_MPEG2_DEC] = { - .run = hantro_g1_mpeg2_dec_run, - .reset = hantro_g1_reset, - .init = hantro_mpeg2_dec_init, - .exit = hantro_mpeg2_dec_exit, - }, - [HANTRO_MODE_VP8_DEC] = { - .run = hantro_g1_vp8_dec_run, - .reset = hantro_g1_reset, - .init = hantro_vp8_dec_init, - .exit = hantro_vp8_dec_exit, - }, - [HANTRO_MODE_H264_DEC] = { - .run = hantro_g1_h264_dec_run, - .reset = hantro_g1_reset, - .init = hantro_h264_dec_init, - .exit = hantro_h264_dec_exit, - }, -}; - -static const struct hantro_irq sama5d4_irqs[] = { - { "vdec", hantro_g1_irq }, -}; - -static const char * const sama5d4_clk_names[] = { "vdec_clk" }; - -const struct hantro_variant sama5d4_vdec_variant = { - .dec_fmts = sama5d4_vdec_fmts, - .num_dec_fmts = ARRAY_SIZE(sama5d4_vdec_fmts), - .postproc_fmts = sama5d4_vdec_postproc_fmts, - .num_postproc_fmts = ARRAY_SIZE(sama5d4_vdec_postproc_fmts), - .postproc_ops = &hantro_g1_postproc_ops, - .codec = HANTRO_MPEG2_DECODER | HANTRO_VP8_DECODER | - HANTRO_H264_DECODER, - .codec_ops = sama5d4_vdec_codec_ops, - .irqs = sama5d4_irqs, - .num_irqs = ARRAY_SIZE(sama5d4_irqs), - .clk_names = sama5d4_clk_names, - .num_clocks = ARRAY_SIZE(sama5d4_clk_names), -}; diff --git a/drivers/staging/media/hantro/sunxi_vpu_hw.c b/drivers/staging/media/hantro/sunxi_vpu_hw.c deleted file mode 100644 index 02ce8b064a8f..000000000000 --- a/drivers/staging/media/hantro/sunxi_vpu_hw.c +++ /dev/null @@ -1,129 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Allwinner Hantro G2 VPU codec driver - * - * Copyright (C) 2021 Jernej Skrabec <jernej.skrabec@gmail.com> - */ - -#include <linux/clk.h> - -#include "hantro.h" - -static const struct hantro_fmt sunxi_vpu_postproc_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_NV12, - .codec_mode = HANTRO_MODE_NONE, - .postprocessed = true, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_UHD_WIDTH, - .step_width = 32, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_UHD_HEIGHT, - .step_height = 32, - }, - }, - { - .fourcc = V4L2_PIX_FMT_P010, - .codec_mode = HANTRO_MODE_NONE, - .postprocessed = true, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_UHD_WIDTH, - .step_width = 32, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_UHD_HEIGHT, - .step_height = 32, - }, - }, -}; - -static const struct hantro_fmt sunxi_vpu_dec_fmts[] = { - { - .fourcc = V4L2_PIX_FMT_NV12_4L4, - .codec_mode = HANTRO_MODE_NONE, - .match_depth = true, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_UHD_WIDTH, - .step_width = 32, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_UHD_HEIGHT, - .step_height = 32, - }, - }, - { - .fourcc = V4L2_PIX_FMT_P010_4L4, - .codec_mode = HANTRO_MODE_NONE, - .match_depth = true, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_UHD_WIDTH, - .step_width = 32, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_UHD_HEIGHT, - .step_height = 32, - }, - }, - { - .fourcc = V4L2_PIX_FMT_VP9_FRAME, - .codec_mode = HANTRO_MODE_VP9_DEC, - .max_depth = 2, - .frmsize = { - .min_width = FMT_MIN_WIDTH, - .max_width = FMT_UHD_WIDTH, - .step_width = 32, - .min_height = FMT_MIN_HEIGHT, - .max_height = FMT_UHD_HEIGHT, - .step_height = 32, - }, - }, -}; - -static int sunxi_vpu_hw_init(struct hantro_dev *vpu) -{ - clk_set_rate(vpu->clocks[0].clk, 300000000); - - return 0; -} - -static void sunxi_vpu_reset(struct hantro_ctx *ctx) -{ - struct hantro_dev *vpu = ctx->dev; - - reset_control_reset(vpu->resets); -} - -static const struct hantro_codec_ops sunxi_vpu_codec_ops[] = { - [HANTRO_MODE_VP9_DEC] = { - .run = hantro_g2_vp9_dec_run, - .done = hantro_g2_vp9_dec_done, - .reset = sunxi_vpu_reset, - .init = hantro_vp9_dec_init, - .exit = hantro_vp9_dec_exit, - }, -}; - -static const struct hantro_irq sunxi_irqs[] = { - { NULL, hantro_g2_irq }, -}; - -static const char * const sunxi_clk_names[] = { "mod", "bus" }; - -const struct hantro_variant sunxi_vpu_variant = { - .dec_fmts = sunxi_vpu_dec_fmts, - .num_dec_fmts = ARRAY_SIZE(sunxi_vpu_dec_fmts), - .postproc_fmts = sunxi_vpu_postproc_fmts, - .num_postproc_fmts = ARRAY_SIZE(sunxi_vpu_postproc_fmts), - .postproc_ops = &hantro_g2_postproc_ops, - .codec = HANTRO_VP9_DECODER, - .codec_ops = sunxi_vpu_codec_ops, - .init = sunxi_vpu_hw_init, - .irqs = sunxi_irqs, - .num_irqs = ARRAY_SIZE(sunxi_irqs), - .clk_names = sunxi_clk_names, - .num_clocks = ARRAY_SIZE(sunxi_clk_names), - .double_buffer = 1, - .legacy_regs = 1, - .late_postproc = 1, -}; diff --git a/drivers/staging/media/imx/imx7-media-csi.c b/drivers/staging/media/imx/imx7-media-csi.c index a0553c24cce4..cbc66ef0eda8 100644 --- a/drivers/staging/media/imx/imx7-media-csi.c +++ b/drivers/staging/media/imx/imx7-media-csi.c @@ -160,7 +160,7 @@ #define IMX7_CSI_VIDEO_NAME "imx-capture" /* In bytes, per queue */ -#define IMX7_CSI_VIDEO_MEM_LIMIT SZ_64M +#define IMX7_CSI_VIDEO_MEM_LIMIT SZ_512M #define IMX7_CSI_VIDEO_EOF_TIMEOUT 2000 #define IMX7_CSI_DEF_MBUS_CODE MEDIA_BUS_FMT_UYVY8_2X8 diff --git a/drivers/staging/media/meson/vdec/vdec_hevc.c b/drivers/staging/media/meson/vdec/vdec_hevc.c index 9530e580e57a..afced435c907 100644 --- a/drivers/staging/media/meson/vdec/vdec_hevc.c +++ b/drivers/staging/media/meson/vdec/vdec_hevc.c @@ -167,8 +167,12 @@ static int vdec_hevc_start(struct amvdec_session *sess) clk_set_rate(core->vdec_hevc_clk, 666666666); ret = clk_prepare_enable(core->vdec_hevc_clk); - if (ret) + if (ret) { + if (core->platform->revision == VDEC_REVISION_G12A || + core->platform->revision == VDEC_REVISION_SM1) + clk_disable_unprepare(core->vdec_hevcf_clk); return ret; + } if (core->platform->revision == VDEC_REVISION_SM1) regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index 9512cd3314f2..842509dcfedf 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -843,7 +843,7 @@ iss_video_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) * processing might be possible but requires more testing. * * Stream start must be delayed until buffers are available at both the input - * and output. The pipeline must be started in the videobuf queue callback with + * and output. The pipeline must be started in the vb2 queue callback with * the buffers queue spinlock held. The modules subdev set stream operation must * not sleep. */ diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c index 960a0130cd62..55c54dfdc585 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus.c @@ -448,6 +448,8 @@ static int cedrus_probe(struct platform_device *pdev) if (!dev) return -ENOMEM; + platform_set_drvdata(pdev, dev); + dev->vfd = cedrus_video_device; dev->dev = &pdev->dev; dev->pdev = pdev; @@ -521,8 +523,6 @@ static int cedrus_probe(struct platform_device *pdev) goto err_m2m_mc; } - platform_set_drvdata(pdev, dev); - return 0; err_m2m_mc: diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.h b/drivers/staging/media/sunxi/cedrus/cedrus.h index 084193019350..93a2196006f7 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.h +++ b/drivers/staging/media/sunxi/cedrus/cedrus.h @@ -237,19 +237,23 @@ static inline dma_addr_t cedrus_buf_addr(struct vb2_buffer *buf, } static inline dma_addr_t cedrus_dst_buf_addr(struct cedrus_ctx *ctx, - int index, unsigned int plane) + struct vb2_buffer *buf, + unsigned int plane) { - struct vb2_buffer *buf = NULL; - struct vb2_queue *vq; - - if (index < 0) - return 0; + return buf ? cedrus_buf_addr(buf, &ctx->dst_fmt, plane) : 0; +} - vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - if (vq) - buf = vb2_get_buffer(vq, index); +static inline void cedrus_write_ref_buf_addr(struct cedrus_ctx *ctx, + struct vb2_queue *q, + u64 timestamp, + u32 luma_reg, + u32 chroma_reg) +{ + struct cedrus_dev *dev = ctx->dev; + struct vb2_buffer *buf = vb2_find_buffer(q, timestamp); - return buf ? cedrus_buf_addr(buf, &ctx->dst_fmt, plane) : 0; + cedrus_write(dev, luma_reg, cedrus_dst_buf_addr(ctx, buf, 0)); + cedrus_write(dev, chroma_reg, cedrus_dst_buf_addr(ctx, buf, 1)); } static inline struct cedrus_buffer * diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c index 3b6aa78a2985..e7f7602a5ab4 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c @@ -106,11 +106,11 @@ void cedrus_device_run(void *priv) /* Trigger decoding if setup went well, bail out otherwise. */ if (!error) { - dev->dec_ops[ctx->current_codec]->trigger(ctx); - /* Start the watchdog timer. */ schedule_delayed_work(&dev->watchdog_work, msecs_to_jiffies(2000)); + + dev->dec_ops[ctx->current_codec]->trigger(ctx); } else { v4l2_m2m_buf_done_and_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx, diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c index c345e67ba9bc..a8b236cd3800 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c @@ -111,16 +111,16 @@ static void cedrus_write_frame_list(struct cedrus_ctx *ctx, for (i = 0; i < ARRAY_SIZE(decode->dpb); i++) { const struct v4l2_h264_dpb_entry *dpb = &decode->dpb[i]; struct cedrus_buffer *cedrus_buf; - int buf_idx; + struct vb2_buffer *buf; if (!(dpb->flags & V4L2_H264_DPB_ENTRY_FLAG_VALID)) continue; - buf_idx = vb2_find_timestamp(cap_q, dpb->reference_ts, 0); - if (buf_idx < 0) + buf = vb2_find_buffer(cap_q, dpb->reference_ts); + if (!buf) continue; - cedrus_buf = vb2_to_cedrus_buffer(cap_q->bufs[buf_idx]); + cedrus_buf = vb2_to_cedrus_buffer(buf); position = cedrus_buf->codec.h264.position; used_dpbs |= BIT(position); @@ -186,7 +186,7 @@ static void _cedrus_write_ref_list(struct cedrus_ctx *ctx, const struct v4l2_h264_dpb_entry *dpb; const struct cedrus_buffer *cedrus_buf; unsigned int position; - int buf_idx; + struct vb2_buffer *buf; u8 dpb_idx; dpb_idx = ref_list[i].index; @@ -195,11 +195,11 @@ static void _cedrus_write_ref_list(struct cedrus_ctx *ctx, if (!(dpb->flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) continue; - buf_idx = vb2_find_timestamp(cap_q, dpb->reference_ts, 0); - if (buf_idx < 0) + buf = vb2_find_buffer(cap_q, dpb->reference_ts); + if (!buf) continue; - cedrus_buf = vb2_to_cedrus_buffer(cap_q->bufs[buf_idx]); + cedrus_buf = vb2_to_cedrus_buffer(buf); position = cedrus_buf->codec.h264.position; sram_array[i] |= position << 1; diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c index 687f87598f78..4952fc17f3e6 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c @@ -102,14 +102,14 @@ static void cedrus_h265_frame_info_write_single(struct cedrus_ctx *ctx, unsigned int index, bool field_pic, u32 pic_order_cnt[], - int buffer_index) + struct vb2_buffer *buf) { struct cedrus_dev *dev = ctx->dev; - dma_addr_t dst_luma_addr = cedrus_dst_buf_addr(ctx, buffer_index, 0); - dma_addr_t dst_chroma_addr = cedrus_dst_buf_addr(ctx, buffer_index, 1); + dma_addr_t dst_luma_addr = cedrus_dst_buf_addr(ctx, buf, 0); + dma_addr_t dst_chroma_addr = cedrus_dst_buf_addr(ctx, buf, 1); dma_addr_t mv_col_buf_addr[2] = { - cedrus_h265_frame_info_mv_col_buf_addr(ctx, buffer_index, 0), - cedrus_h265_frame_info_mv_col_buf_addr(ctx, buffer_index, + cedrus_h265_frame_info_mv_col_buf_addr(ctx, buf->index, 0), + cedrus_h265_frame_info_mv_col_buf_addr(ctx, buf->index, field_pic ? 1 : 0) }; u32 offset = VE_DEC_H265_SRAM_OFFSET_FRAME_INFO + @@ -141,18 +141,18 @@ static void cedrus_h265_frame_info_write_dpb(struct cedrus_ctx *ctx, unsigned int i; for (i = 0; i < num_active_dpb_entries; i++) { - int buffer_index = vb2_find_timestamp(vq, dpb[i].timestamp, 0); + struct vb2_buffer *buf = vb2_find_buffer(vq, dpb[i].timestamp); u32 pic_order_cnt[2] = { dpb[i].pic_order_cnt_val, dpb[i].pic_order_cnt_val }; - if (buffer_index < 0) + if (!buf) continue; cedrus_h265_frame_info_write_single(ctx, i, dpb[i].field_pic, pic_order_cnt, - buffer_index); + buf); } } @@ -234,8 +234,9 @@ static void cedrus_h265_skip_bits(struct cedrus_dev *dev, int num) cedrus_write(dev, VE_DEC_H265_TRIGGER, VE_DEC_H265_TRIGGER_FLUSH_BITS | VE_DEC_H265_TRIGGER_TYPE_N_BITS(tmp)); - while (cedrus_read(dev, VE_DEC_H265_STATUS) & VE_DEC_H265_STATUS_VLD_BUSY) - udelay(1); + + if (cedrus_wait_for(dev, VE_DEC_H265_STATUS, VE_DEC_H265_STATUS_VLD_BUSY)) + dev_err_ratelimited(dev->dev, "timed out waiting to skip bits\n"); count += tmp; } @@ -751,7 +752,7 @@ static int cedrus_h265_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) cedrus_h265_frame_info_write_single(ctx, output_pic_list_index, slice_params->pic_struct != 0, pic_order_cnt, - run->dst->vb2_buf.index); + &run->dst->vb2_buf); cedrus_write(dev, VE_DEC_H265_OUTPUT_FRAME_IDX, output_pic_list_index); diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c b/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c index 4cfc4a3c8a7f..c1128d2cd555 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c @@ -54,13 +54,9 @@ static int cedrus_mpeg2_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) const struct v4l2_ctrl_mpeg2_picture *pic; const struct v4l2_ctrl_mpeg2_quantisation *quantisation; dma_addr_t src_buf_addr, dst_luma_addr, dst_chroma_addr; - dma_addr_t fwd_luma_addr, fwd_chroma_addr; - dma_addr_t bwd_luma_addr, bwd_chroma_addr; struct cedrus_dev *dev = ctx->dev; struct vb2_queue *vq; const u8 *matrix; - int forward_idx; - int backward_idx; unsigned int i; u32 reg; @@ -123,27 +119,19 @@ static int cedrus_mpeg2_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) cedrus_write(dev, VE_DEC_MPEG_PICBOUNDSIZE, reg); /* Forward and backward prediction reference buffers. */ - vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - forward_idx = vb2_find_timestamp(vq, pic->forward_ref_ts, 0); - fwd_luma_addr = cedrus_dst_buf_addr(ctx, forward_idx, 0); - fwd_chroma_addr = cedrus_dst_buf_addr(ctx, forward_idx, 1); - - cedrus_write(dev, VE_DEC_MPEG_FWD_REF_LUMA_ADDR, fwd_luma_addr); - cedrus_write(dev, VE_DEC_MPEG_FWD_REF_CHROMA_ADDR, fwd_chroma_addr); - - backward_idx = vb2_find_timestamp(vq, pic->backward_ref_ts, 0); - bwd_luma_addr = cedrus_dst_buf_addr(ctx, backward_idx, 0); - bwd_chroma_addr = cedrus_dst_buf_addr(ctx, backward_idx, 1); - - cedrus_write(dev, VE_DEC_MPEG_BWD_REF_LUMA_ADDR, bwd_luma_addr); - cedrus_write(dev, VE_DEC_MPEG_BWD_REF_CHROMA_ADDR, bwd_chroma_addr); + cedrus_write_ref_buf_addr(ctx, vq, pic->forward_ref_ts, + VE_DEC_MPEG_FWD_REF_LUMA_ADDR, + VE_DEC_MPEG_FWD_REF_CHROMA_ADDR); + cedrus_write_ref_buf_addr(ctx, vq, pic->backward_ref_ts, + VE_DEC_MPEG_BWD_REF_LUMA_ADDR, + VE_DEC_MPEG_BWD_REF_CHROMA_ADDR); /* Destination luma and chroma buffers. */ - dst_luma_addr = cedrus_dst_buf_addr(ctx, run->dst->vb2_buf.index, 0); - dst_chroma_addr = cedrus_dst_buf_addr(ctx, run->dst->vb2_buf.index, 1); + dst_luma_addr = cedrus_dst_buf_addr(ctx, &run->dst->vb2_buf, 0); + dst_chroma_addr = cedrus_dst_buf_addr(ctx, &run->dst->vb2_buf, 1); cedrus_write(dev, VE_DEC_MPEG_REC_LUMA, dst_luma_addr); cedrus_write(dev, VE_DEC_MPEG_REC_CHROMA, dst_chroma_addr); diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_vp8.c b/drivers/staging/media/sunxi/cedrus/cedrus_vp8.c index 3f750d1795b6..f7714baae37d 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_vp8.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_vp8.c @@ -660,7 +660,6 @@ static int cedrus_vp8_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) dma_addr_t luma_addr, chroma_addr; dma_addr_t src_buf_addr; int header_size; - int qindex; u32 reg; cedrus_engine_enable(ctx, CEDRUS_CODEC_VP8); @@ -804,43 +803,17 @@ static int cedrus_vp8_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) reg |= VE_VP8_LF_DELTA0(slice->lf.mb_mode_delta[0]); cedrus_write(dev, VE_VP8_MODE_LF_DELTA, reg); - luma_addr = cedrus_dst_buf_addr(ctx, run->dst->vb2_buf.index, 0); - chroma_addr = cedrus_dst_buf_addr(ctx, run->dst->vb2_buf.index, 1); + luma_addr = cedrus_dst_buf_addr(ctx, &run->dst->vb2_buf, 0); + chroma_addr = cedrus_dst_buf_addr(ctx, &run->dst->vb2_buf, 1); cedrus_write(dev, VE_VP8_REC_LUMA, luma_addr); cedrus_write(dev, VE_VP8_REC_CHROMA, chroma_addr); - qindex = vb2_find_timestamp(cap_q, slice->last_frame_ts, 0); - if (qindex >= 0) { - luma_addr = cedrus_dst_buf_addr(ctx, qindex, 0); - chroma_addr = cedrus_dst_buf_addr(ctx, qindex, 1); - cedrus_write(dev, VE_VP8_FWD_LUMA, luma_addr); - cedrus_write(dev, VE_VP8_FWD_CHROMA, chroma_addr); - } else { - cedrus_write(dev, VE_VP8_FWD_LUMA, 0); - cedrus_write(dev, VE_VP8_FWD_CHROMA, 0); - } - - qindex = vb2_find_timestamp(cap_q, slice->golden_frame_ts, 0); - if (qindex >= 0) { - luma_addr = cedrus_dst_buf_addr(ctx, qindex, 0); - chroma_addr = cedrus_dst_buf_addr(ctx, qindex, 1); - cedrus_write(dev, VE_VP8_BWD_LUMA, luma_addr); - cedrus_write(dev, VE_VP8_BWD_CHROMA, chroma_addr); - } else { - cedrus_write(dev, VE_VP8_BWD_LUMA, 0); - cedrus_write(dev, VE_VP8_BWD_CHROMA, 0); - } - - qindex = vb2_find_timestamp(cap_q, slice->alt_frame_ts, 0); - if (qindex >= 0) { - luma_addr = cedrus_dst_buf_addr(ctx, qindex, 0); - chroma_addr = cedrus_dst_buf_addr(ctx, qindex, 1); - cedrus_write(dev, VE_VP8_ALT_LUMA, luma_addr); - cedrus_write(dev, VE_VP8_ALT_CHROMA, chroma_addr); - } else { - cedrus_write(dev, VE_VP8_ALT_LUMA, 0); - cedrus_write(dev, VE_VP8_ALT_CHROMA, 0); - } + cedrus_write_ref_buf_addr(ctx, cap_q, slice->last_frame_ts, + VE_VP8_FWD_LUMA, VE_VP8_FWD_CHROMA); + cedrus_write_ref_buf_addr(ctx, cap_q, slice->golden_frame_ts, + VE_VP8_BWD_LUMA, VE_VP8_BWD_CHROMA); + cedrus_write_ref_buf_addr(ctx, cap_q, slice->alt_frame_ts, + VE_VP8_ALT_LUMA, VE_VP8_ALT_CHROMA); cedrus_write(dev, VE_H264_CTRL, VE_H264_CTRL_VP8 | VE_H264_CTRL_DECODE_ERR_INT | diff --git a/drivers/staging/media/zoran/Kconfig b/drivers/staging/media/zoran/Kconfig deleted file mode 100644 index 3fb3e27e04a8..000000000000 --- a/drivers/staging/media/zoran/Kconfig +++ /dev/null @@ -1,74 +0,0 @@ -config VIDEO_ZORAN - tristate "Zoran ZR36057/36067 Video For Linux (Deprecated)" - depends on PCI && I2C_ALGOBIT && VIDEO_DEV - depends on !ALPHA - depends on DEBUG_FS - select VIDEOBUF2_DMA_CONTIG - select VIDEO_ADV7170 if VIDEO_ZORAN_LML33R10 - select VIDEO_ADV7175 if VIDEO_ZORAN_DC10 || VIDEO_ZORAN_DC30 - select VIDEO_BT819 if VIDEO_ZORAN_LML33 - select VIDEO_BT856 if VIDEO_ZORAN_LML33 || VIDEO_ZORAN_AVS6EYES - select VIDEO_BT866 if VIDEO_ZORAN_AVS6EYES - select VIDEO_KS0127 if VIDEO_ZORAN_AVS6EYES - select VIDEO_SAA711X if VIDEO_ZORAN_BUZ || VIDEO_ZORAN_LML33R10 - select VIDEO_SAA7110 if VIDEO_ZORAN_DC10 - select VIDEO_SAA7185 if VIDEO_ZORAN_BUZ - select VIDEO_VPX3220 if VIDEO_ZORAN_DC30 - help - Say Y for support for MJPEG capture cards based on the Zoran - 36057/36067 PCI controller chipset. This includes the Iomega - Buz, Pinnacle DC10+ and the Linux Media Labs LML33. There is - a driver homepage at <http://mjpeg.sf.net/driver-zoran/>. For - more information, check <file:Documentation/driver-api/media/drivers/zoran.rst>. - - To compile this driver as a module, choose M here: the - module will be called zr36067. - -config VIDEO_ZORAN_DC30 - bool "Pinnacle/Miro DC30(+) support" - depends on VIDEO_ZORAN - help - Support for the Pinnacle/Miro DC30(+) MJPEG capture/playback - card. This also supports really old DC10 cards based on the - zr36050 MJPEG codec and zr36016 VFE. - -config VIDEO_ZORAN_ZR36060 - bool "Zoran ZR36060" - depends on VIDEO_ZORAN - help - Say Y to support Zoran boards based on 36060 chips. - This includes Iomega Buz, Pinnacle DC10, Linux media Labs 33 - and 33 R10 and AverMedia 6 boards. - -config VIDEO_ZORAN_BUZ - bool "Iomega Buz support" - depends on VIDEO_ZORAN_ZR36060 - help - Support for the Iomega Buz MJPEG capture/playback card. - -config VIDEO_ZORAN_DC10 - bool "Pinnacle/Miro DC10(+) support" - depends on VIDEO_ZORAN_ZR36060 - help - Support for the Pinnacle/Miro DC10(+) MJPEG capture/playback - card. - -config VIDEO_ZORAN_LML33 - bool "Linux Media Labs LML33 support" - depends on VIDEO_ZORAN_ZR36060 - help - Support for the Linux Media Labs LML33 MJPEG capture/playback - card. - -config VIDEO_ZORAN_LML33R10 - bool "Linux Media Labs LML33R10 support" - depends on VIDEO_ZORAN_ZR36060 - help - support for the Linux Media Labs LML33R10 MJPEG capture/playback - card. - -config VIDEO_ZORAN_AVS6EYES - bool "AverMedia 6 Eyes support" - depends on VIDEO_ZORAN_ZR36060 - help - Support for the AverMedia 6 Eyes video surveillance card. diff --git a/drivers/staging/media/zoran/Makefile b/drivers/staging/media/zoran/Makefile deleted file mode 100644 index 9603bac0195c..000000000000 --- a/drivers/staging/media/zoran/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -zr36067-objs := zoran_device.o \ - zoran_driver.o zoran_card.o videocodec.o - -obj-$(CONFIG_VIDEO_ZORAN) += zr36067.o -zr36067-$(CONFIG_VIDEO_ZORAN_DC30) += zr36050.o zr36016.o -zr36067-$(CONFIG_VIDEO_ZORAN_ZR36060) += zr36060.o diff --git a/drivers/staging/media/zoran/TODO b/drivers/staging/media/zoran/TODO deleted file mode 100644 index 6992540d3e53..000000000000 --- a/drivers/staging/media/zoran/TODO +++ /dev/null @@ -1,19 +0,0 @@ - -How to test the zoran driver: -- RAW capture - mplayer tv:///dev/video0 -tv driver=v4l2 - -- MJPEG capture (compression) - mplayer tv:///dev/video0 -tv driver=v4l2:outfmt=mjpeg - TODO: need two test for both Dcim path - -- MJPEG play (decompression) - ffmpeg -i test.avi -vcodec mjpeg -an -f v4l2 /dev/video0 - Note: only recent ffmpeg has the ability of sending non-raw video via v4l2 - - The original way of sending video was via mplayer vo_zr/vo_zr2, but it does not compile - anymore and is a dead end (usage of some old private ffmpeg structures). - -TODO -- fix the v4l compliance "TRY_FMT cannot handle an invalid pixelformat" -- Filter JPEG data to made output work diff --git a/drivers/staging/media/zoran/videocodec.c b/drivers/staging/media/zoran/videocodec.c deleted file mode 100644 index a0c8bde5ec11..000000000000 --- a/drivers/staging/media/zoran/videocodec.c +++ /dev/null @@ -1,279 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * VIDEO MOTION CODECs internal API for video devices - * - * Interface for MJPEG (and maybe later MPEG/WAVELETS) codec's - * bound to a master device. - * - * (c) 2002 Wolfgang Scherr <scherr@net4you.at> - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/types.h> -#include <linux/slab.h> - -#include "videocodec.h" - -struct attached_list { - struct videocodec *codec; - struct attached_list *next; -}; - -struct codec_list { - const struct videocodec *codec; - int attached; - struct attached_list *list; - struct codec_list *next; -}; - -static struct codec_list *codeclist_top; - -/* ================================================= */ -/* function prototypes of the master/slave interface */ -/* ================================================= */ - -struct videocodec *videocodec_attach(struct videocodec_master *master) -{ - struct codec_list *h = codeclist_top; - struct zoran *zr; - struct attached_list *a, *ptr; - struct videocodec *codec; - int res; - - if (!master) { - pr_err("%s: no data\n", __func__); - return NULL; - } - - zr = videocodec_master_to_zoran(master); - - zrdev_dbg(zr, "%s: '%s', flags %lx, magic %lx\n", __func__, - master->name, master->flags, master->magic); - - if (!h) { - zrdev_err(zr, "%s: no device available\n", __func__); - return NULL; - } - - while (h) { - // attach only if the slave has at least the flags - // expected by the master - if ((master->flags & h->codec->flags) == master->flags) { - zrdev_dbg(zr, "%s: try '%s'\n", __func__, h->codec->name); - - codec = kmemdup(h->codec, sizeof(struct videocodec), GFP_KERNEL); - if (!codec) - goto out_kfree; - - res = strlen(codec->name); - snprintf(codec->name + res, sizeof(codec->name) - res, "[%d]", h->attached); - codec->master_data = master; - res = codec->setup(codec); - if (res == 0) { - zrdev_dbg(zr, "%s: '%s'\n", __func__, codec->name); - ptr = kzalloc(sizeof(*ptr), GFP_KERNEL); - if (!ptr) - goto out_kfree; - ptr->codec = codec; - - a = h->list; - if (!a) { - h->list = ptr; - zrdev_dbg(zr, "videocodec: first element\n"); - } else { - while (a->next) - a = a->next; // find end - a->next = ptr; - zrdev_dbg(zr, "videocodec: in after '%s'\n", - h->codec->name); - } - - h->attached += 1; - return codec; - } else { - kfree(codec); - } - } - h = h->next; - } - - zrdev_err(zr, "%s: no codec found!\n", __func__); - return NULL; - - out_kfree: - kfree(codec); - return NULL; -} - -int videocodec_detach(struct videocodec *codec) -{ - struct codec_list *h = codeclist_top; - struct zoran *zr; - struct attached_list *a, *prev; - int res; - - if (!codec) { - pr_err("%s: no data\n", __func__); - return -EINVAL; - } - - zr = videocodec_to_zoran(codec); - - zrdev_dbg(zr, "%s: '%s', type: %x, flags %lx, magic %lx\n", __func__, - codec->name, codec->type, codec->flags, codec->magic); - - if (!h) { - zrdev_err(zr, "%s: no device left...\n", __func__); - return -ENXIO; - } - - while (h) { - a = h->list; - prev = NULL; - while (a) { - if (codec == a->codec) { - res = a->codec->unset(a->codec); - if (res >= 0) { - zrdev_dbg(zr, "%s: '%s'\n", __func__, - a->codec->name); - a->codec->master_data = NULL; - } else { - zrdev_err(zr, "%s: '%s'\n", __func__, a->codec->name); - a->codec->master_data = NULL; - } - if (!prev) { - h->list = a->next; - zrdev_dbg(zr, "videocodec: delete first\n"); - } else { - prev->next = a->next; - zrdev_dbg(zr, "videocodec: delete middle\n"); - } - kfree(a->codec); - kfree(a); - h->attached -= 1; - return 0; - } - prev = a; - a = a->next; - } - h = h->next; - } - - zrdev_err(zr, "%s: given codec not found!\n", __func__); - return -EINVAL; -} - -int videocodec_register(const struct videocodec *codec) -{ - struct codec_list *ptr, *h = codeclist_top; - struct zoran *zr; - - if (!codec) { - pr_err("%s: no data!\n", __func__); - return -EINVAL; - } - - zr = videocodec_to_zoran((struct videocodec *)codec); - - zrdev_dbg(zr, - "videocodec: register '%s', type: %x, flags %lx, magic %lx\n", - codec->name, codec->type, codec->flags, codec->magic); - - ptr = kzalloc(sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return -ENOMEM; - ptr->codec = codec; - - if (!h) { - codeclist_top = ptr; - zrdev_dbg(zr, "videocodec: hooked in as first element\n"); - } else { - while (h->next) - h = h->next; // find the end - h->next = ptr; - zrdev_dbg(zr, "videocodec: hooked in after '%s'\n", - h->codec->name); - } - - return 0; -} - -int videocodec_unregister(const struct videocodec *codec) -{ - struct codec_list *prev = NULL, *h = codeclist_top; - struct zoran *zr; - - if (!codec) { - pr_err("%s: no data!\n", __func__); - return -EINVAL; - } - - zr = videocodec_to_zoran((struct videocodec *)codec); - - zrdev_dbg(zr, - "videocodec: unregister '%s', type: %x, flags %lx, magic %lx\n", - codec->name, codec->type, codec->flags, codec->magic); - - if (!h) { - zrdev_err(zr, "%s: no device left...\n", __func__); - return -ENXIO; - } - - while (h) { - if (codec == h->codec) { - if (h->attached) { - zrdev_err(zr, "videocodec: '%s' is used\n", - h->codec->name); - return -EBUSY; - } - zrdev_dbg(zr, "videocodec: unregister '%s' is ok.\n", - h->codec->name); - if (!prev) { - codeclist_top = h->next; - zrdev_dbg(zr, - "videocodec: delete first element\n"); - } else { - prev->next = h->next; - zrdev_dbg(zr, - "videocodec: delete middle element\n"); - } - kfree(h); - return 0; - } - prev = h; - h = h->next; - } - - zrdev_err(zr, "%s: given codec not found!\n", __func__); - return -EINVAL; -} - -int videocodec_debugfs_show(struct seq_file *m) -{ - struct codec_list *h = codeclist_top; - struct attached_list *a; - - seq_printf(m, "<S>lave or attached <M>aster name type flags magic "); - seq_printf(m, "(connected as)\n"); - - while (h) { - seq_printf(m, "S %32s %04x %08lx %08lx (TEMPLATE)\n", - h->codec->name, h->codec->type, - h->codec->flags, h->codec->magic); - a = h->list; - while (a) { - seq_printf(m, "M %32s %04x %08lx %08lx (%s)\n", - a->codec->master_data->name, - a->codec->master_data->type, - a->codec->master_data->flags, - a->codec->master_data->magic, - a->codec->name); - a = a->next; - } - h = h->next; - } - - return 0; -} diff --git a/drivers/staging/media/zoran/videocodec.h b/drivers/staging/media/zoran/videocodec.h deleted file mode 100644 index 5e6057edd339..000000000000 --- a/drivers/staging/media/zoran/videocodec.h +++ /dev/null @@ -1,325 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * VIDEO MOTION CODECs internal API for video devices - * - * Interface for MJPEG (and maybe later MPEG/WAVELETS) codec's - * bound to a master device. - * - * (c) 2002 Wolfgang Scherr <scherr@net4you.at> - */ - -/* =================== */ -/* general description */ -/* =================== */ - -/* Should ease the (re-)usage of drivers supporting cards with (different) - video codecs. The codecs register to this module their functionality, - and the processors (masters) can attach to them if they fit. - - The codecs are typically have a "strong" binding to their master - so I - don't think it makes sense to have a full blown interfacing as with e.g. - i2c. If you have an other opinion, let's discuss & implement it :-))) - - Usage: - - The slave has just to setup the videocodec structure and use two functions: - videocodec_register(codecdata); - videocodec_unregister(codecdata); - The best is just calling them at module (de-)initialisation. - - The master sets up the structure videocodec_master and calls: - codecdata=videocodec_attach(master_codecdata); - videocodec_detach(codecdata); - - The slave is called during attach/detach via functions setup previously - during register. At that time, the master_data pointer is set up - and the slave can access any io registers of the master device (in the case - the slave is bound to it). Otherwise it doesn't need this functions and - therfor they may not be initialized. - - The other functions are just for convenience, as they are for sure used by - most/all of the codecs. The last ones may be omitted, too. - - See the structure declaration below for more information and which data has - to be set up for the master and the slave. - - ---------------------------------------------------------------------------- - The master should have "knowledge" of the slave and vice versa. So the data - structures sent to/from slave via set_data/get_data set_image/get_image are - device dependent and vary between MJPEG/MPEG/WAVELET/... devices. (!!!!) - ---------------------------------------------------------------------------- -*/ - -/* ========================================== */ -/* description of the videocodec_io structure */ -/* ========================================== */ - -/* - ==== master setup ==== - name -> name of the device structure for reference and debugging - master_data -> data ref. for the master (e.g. the zr36055,57,67) - readreg -> ref. to read-fn from register (setup by master, used by slave) - writereg -> ref. to write-fn to register (setup by master, used by slave) - this two functions do the lowlevel I/O job - - ==== slave functionality setup ==== - slave_data -> data ref. for the slave (e.g. the zr36050,60) - check -> fn-ref. checks availability of an device, returns -EIO on failure or - the type on success - this makes espcecially sense if a driver module supports more than - one codec which may be quite similar to access, nevertheless it - is good for a first functionality check - - -- main functions you always need for compression/decompression -- - - set_mode -> this fn-ref. resets the entire codec, and sets up the mode - with the last defined norm/size (or device default if not - available) - it returns 0 if the mode is possible - set_size -> this fn-ref. sets the norm and image size for - compression/decompression (returns 0 on success) - the norm param is defined in videodev2.h (V4L2_STD_*) - - additional setup may be available, too - but the codec should work with - some default values even without this - - set_data -> sets device-specific data (tables, quality etc.) - get_data -> query device-specific data (tables, quality etc.) - - if the device delivers interrupts, they may be setup/handled here - setup_interrupt -> codec irq setup (not needed for 36050/60) - handle_interrupt -> codec irq handling (not needed for 36050/60) - - if the device delivers pictures, they may be handled here - put_image -> puts image data to the codec (not needed for 36050/60) - get_image -> gets image data from the codec (not needed for 36050/60) - the calls include frame numbers and flags (even/odd/...) - if needed and a flag which allows blocking until its ready -*/ - -/* ============== */ -/* user interface */ -/* ============== */ - -/* - Currently there is only a information display planned, as the layer - is not visible for the user space at all. - - Information is available via procfs. The current entry is "/proc/videocodecs" - but it makes sense to "hide" it in the /proc/video tree of v4l(2) --TODO--. - -A example for such an output is: - -<S>lave or attached <M>aster name type flags magic (connected as) -S zr36050 0002 0000d001 00000000 (TEMPLATE) -M zr36055[0] 0001 0000c001 00000000 (zr36050[0]) -M zr36055[1] 0001 0000c001 00000000 (zr36050[1]) - -*/ - -/* =============================================== */ -/* special defines for the videocodec_io structure */ -/* =============================================== */ - -#ifndef __LINUX_VIDEOCODEC_H -#define __LINUX_VIDEOCODEC_H - -#include <linux/debugfs.h> -#include <linux/videodev2.h> - -#define CODEC_DO_COMPRESSION 0 -#define CODEC_DO_EXPANSION 1 - -/* this are the current codec flags I think they are needed */ -/* -> type value in structure */ -#define CODEC_FLAG_JPEG 0x00000001L // JPEG codec -#define CODEC_FLAG_MPEG 0x00000002L // MPEG1/2/4 codec -#define CODEC_FLAG_DIVX 0x00000004L // DIVX codec -#define CODEC_FLAG_WAVELET 0x00000008L // WAVELET codec - // room for other types - -#define CODEC_FLAG_MAGIC 0x00000800L // magic key must match -#define CODEC_FLAG_HARDWARE 0x00001000L // is a hardware codec -#define CODEC_FLAG_VFE 0x00002000L // has direct video frontend -#define CODEC_FLAG_ENCODER 0x00004000L // compression capability -#define CODEC_FLAG_DECODER 0x00008000L // decompression capability -#define CODEC_FLAG_NEEDIRQ 0x00010000L // needs irq handling -#define CODEC_FLAG_RDWRPIC 0x00020000L // handles picture I/O - -/* a list of modes, some are just examples (is there any HW?) */ -#define CODEC_MODE_BJPG 0x0001 // Baseline JPEG -#define CODEC_MODE_LJPG 0x0002 // Lossless JPEG -#define CODEC_MODE_MPEG1 0x0003 // MPEG 1 -#define CODEC_MODE_MPEG2 0x0004 // MPEG 2 -#define CODEC_MODE_MPEG4 0x0005 // MPEG 4 -#define CODEC_MODE_MSDIVX 0x0006 // MS DivX -#define CODEC_MODE_ODIVX 0x0007 // Open DivX -#define CODEC_MODE_WAVELET 0x0008 // Wavelet - -/* this are the current codec types I want to implement */ -/* -> type value in structure */ -#define CODEC_TYPE_NONE 0 -#define CODEC_TYPE_L64702 1 -#define CODEC_TYPE_ZR36050 2 -#define CODEC_TYPE_ZR36016 3 -#define CODEC_TYPE_ZR36060 4 - -/* the type of data may be enhanced by future implementations (data-fn.'s) */ -/* -> used in command */ -#define CODEC_G_STATUS 0x0000 /* codec status (query only) */ -#define CODEC_S_CODEC_MODE 0x0001 /* codec mode (baseline JPEG, MPEG1,... */ -#define CODEC_G_CODEC_MODE 0x8001 -#define CODEC_S_VFE 0x0002 /* additional video frontend setup */ -#define CODEC_G_VFE 0x8002 -#define CODEC_S_MMAP 0x0003 /* MMAP setup (if available) */ - -#define CODEC_S_JPEG_TDS_BYTE 0x0010 /* target data size in bytes */ -#define CODEC_G_JPEG_TDS_BYTE 0x8010 -#define CODEC_S_JPEG_SCALE 0x0011 /* scaling factor for quant. tables */ -#define CODEC_G_JPEG_SCALE 0x8011 -#define CODEC_S_JPEG_HDT_DATA 0x0018 /* huffman-tables */ -#define CODEC_G_JPEG_HDT_DATA 0x8018 -#define CODEC_S_JPEG_QDT_DATA 0x0019 /* quantizing-tables */ -#define CODEC_G_JPEG_QDT_DATA 0x8019 -#define CODEC_S_JPEG_APP_DATA 0x001A /* APP marker */ -#define CODEC_G_JPEG_APP_DATA 0x801A -#define CODEC_S_JPEG_COM_DATA 0x001B /* COM marker */ -#define CODEC_G_JPEG_COM_DATA 0x801B - -#define CODEC_S_PRIVATE 0x1000 /* "private" commands start here */ -#define CODEC_G_PRIVATE 0x9000 - -#define CODEC_G_FLAG 0x8000 /* this is how 'get' is detected */ - -/* types of transfer, directly user space or a kernel buffer (image-fn.'s) */ -/* -> used in get_image, put_image */ -#define CODEC_TRANSFER_KERNEL 0 /* use "memcopy" */ -#define CODEC_TRANSFER_USER 1 /* use "to/from_user" */ - -/* ========================= */ -/* the structures itself ... */ -/* ========================= */ - -struct vfe_polarity { - unsigned int vsync_pol:1; - unsigned int hsync_pol:1; - unsigned int field_pol:1; - unsigned int blank_pol:1; - unsigned int subimg_pol:1; - unsigned int poe_pol:1; - unsigned int pvalid_pol:1; - unsigned int vclk_pol:1; -}; - -struct vfe_settings { - __u32 x, y; /* Offsets into image */ - __u32 width, height; /* Area to capture */ - __u16 decimation; /* Decimation divider */ - __u16 flags; /* Flags for capture */ - __u16 quality; /* quality of the video */ -}; - -struct tvnorm { - u16 wt, wa, h_start, h_sync_start, ht, ha, v_start; -}; - -struct jpeg_com_marker { - int len; /* number of usable bytes in data */ - char data[60]; -}; - -struct jpeg_app_marker { - int appn; /* number app segment */ - int len; /* number of usable bytes in data */ - char data[60]; -}; - -struct videocodec { - /* -- filled in by slave device during register -- */ - char name[32]; - unsigned long magic; /* may be used for client<->master attaching */ - unsigned long flags; /* functionality flags */ - unsigned int type; /* codec type */ - - /* -- these is filled in later during master device attach -- */ - - struct videocodec_master *master_data; - - /* -- these are filled in by the slave device during register -- */ - - void *data; /* private slave data */ - - /* attach/detach client functions (indirect call) */ - int (*setup)(struct videocodec *codec); - int (*unset)(struct videocodec *codec); - - /* main functions, every client needs them for sure! */ - // set compression or decompression (or freeze, stop, standby, etc) - int (*set_mode)(struct videocodec *codec, int mode); - // setup picture size and norm (for the codec's video frontend) - int (*set_video)(struct videocodec *codec, const struct tvnorm *norm, - struct vfe_settings *cap, struct vfe_polarity *pol); - // other control commands, also mmap setup etc. - int (*control)(struct videocodec *codec, int type, int size, void *data); - - /* additional setup/query/processing (may be NULL pointer) */ - // interrupt setup / handling (for irq's delivered by master) - int (*setup_interrupt)(struct videocodec *codec, long mode); - int (*handle_interrupt)(struct videocodec *codec, int source, long flag); - // picture interface (if any) - long (*put_image)(struct videocodec *codec, int tr_type, int block, - long *fr_num, long *flag, long size, void *buf); - long (*get_image)(struct videocodec *codec, int tr_type, int block, - long *fr_num, long *flag, long size, void *buf); -}; - -struct videocodec_master { - /* -- filled in by master device for registration -- */ - char name[32]; - unsigned long magic; /* may be used for client<->master attaching */ - unsigned long flags; /* functionality flags */ - unsigned int type; /* master type */ - - void *data; /* private master data */ - - __u32 (*readreg)(struct videocodec *codec, __u16 reg); - void (*writereg)(struct videocodec *codec, __u16 reg, __u32 value); -}; - -/* ================================================= */ -/* function prototypes of the master/slave interface */ -/* ================================================= */ - -/* attach and detach commands for the master */ -// * master structure needs to be kmalloc'ed before calling attach -// and free'd after calling detach -// * returns pointer on success, NULL on failure -extern struct videocodec *videocodec_attach(struct videocodec_master *); -// * 0 on success, <0 (errno) on failure -extern int videocodec_detach(struct videocodec *); - -/* register and unregister commands for the slaves */ -// * 0 on success, <0 (errno) on failure -extern int videocodec_register(const struct videocodec *); -// * 0 on success, <0 (errno) on failure -extern int videocodec_unregister(const struct videocodec *); - -/* the other calls are directly done via the videocodec structure! */ - -int videocodec_debugfs_show(struct seq_file *m); - -#include "zoran.h" -static inline struct zoran *videocodec_master_to_zoran(struct videocodec_master *master) -{ - struct zoran *zr = master->data; - - return zr; -} - -static inline struct zoran *videocodec_to_zoran(struct videocodec *codec) -{ - struct videocodec_master *master = codec->master_data; - - return videocodec_master_to_zoran(master); -} - -#endif /*ifndef __LINUX_VIDEOCODEC_H */ diff --git a/drivers/staging/media/zoran/zoran.h b/drivers/staging/media/zoran/zoran.h deleted file mode 100644 index 05227e5298f6..000000000000 --- a/drivers/staging/media/zoran/zoran.h +++ /dev/null @@ -1,320 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * zoran - Iomega Buz driver - * - * Copyright (C) 1999 Rainer Johanni <Rainer@Johanni.de> - * - * based on - * - * zoran.0.0.3 Copyright (C) 1998 Dave Perks <dperks@ibm.net> - * - * and - * - * bttv - Bt848 frame grabber driver - * Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) - * & Marcus Metzler (mocm@thp.uni-koeln.de) - */ - -#ifndef _BUZ_H_ -#define _BUZ_H_ - -#include <linux/debugfs.h> -#include <linux/pci.h> -#include <linux/i2c-algo-bit.h> -#include <media/v4l2-device.h> -#include <media/v4l2-ctrls.h> -#include <media/videobuf2-core.h> -#include <media/videobuf2-v4l2.h> -#include <media/videobuf2-dma-contig.h> - -#define ZR_NORM_PAL 0 -#define ZR_NORM_NTSC 1 -#define ZR_NORM_SECAM 2 - -struct zr_buffer { - /* common v4l buffer stuff -- must be first */ - struct vb2_v4l2_buffer vbuf; - struct list_head queue; -}; - -static inline struct zr_buffer *vb2_to_zr_buffer(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - - return container_of(vbuf, struct zr_buffer, vbuf); -} - -#define ZORAN_NAME "ZORAN" /* name of the device */ - -#define ZR_DEVNAME(zr) ((zr)->name) - -#define BUZ_MAX_WIDTH (zr->timing->wa) -#define BUZ_MAX_HEIGHT (zr->timing->ha) -#define BUZ_MIN_WIDTH 32 /* never display less than 32 pixels */ -#define BUZ_MIN_HEIGHT 24 /* never display less than 24 rows */ - -#define BUZ_NUM_STAT_COM 4 -#define BUZ_MASK_STAT_COM 3 - -#define BUZ_MAX_INPUT 16 - -#include "zr36057.h" - -enum card_type { - UNKNOWN = -1, - - /* Pinnacle/Miro */ - DC10_OLD, /* DC30 like */ - DC10_NEW, /* DC10_PLUS like */ - DC10_PLUS, - DC30, - DC30_PLUS, - - /* Linux Media Labs */ - LML33, - LML33R10, - - /* Iomega */ - BUZ, - - /* AverMedia */ - AVS6EYES, - - /* total number of cards */ - NUM_CARDS -}; - -enum zoran_codec_mode { - BUZ_MODE_IDLE, /* nothing going on */ - BUZ_MODE_MOTION_COMPRESS, /* grabbing frames */ - BUZ_MODE_MOTION_DECOMPRESS, /* playing frames */ - BUZ_MODE_STILL_COMPRESS, /* still frame conversion */ - BUZ_MODE_STILL_DECOMPRESS /* still frame conversion */ -}; - -enum zoran_map_mode { - ZORAN_MAP_MODE_NONE, - ZORAN_MAP_MODE_RAW, - ZORAN_MAP_MODE_JPG_REC, - ZORAN_MAP_MODE_JPG_PLAY, -}; - -enum gpio_type { - ZR_GPIO_JPEG_SLEEP = 0, - ZR_GPIO_JPEG_RESET, - ZR_GPIO_JPEG_FRAME, - ZR_GPIO_VID_DIR, - ZR_GPIO_VID_EN, - ZR_GPIO_VID_RESET, - ZR_GPIO_CLK_SEL1, - ZR_GPIO_CLK_SEL2, - ZR_GPIO_MAX, -}; - -enum gpcs_type { - GPCS_JPEG_RESET = 0, - GPCS_JPEG_START, - GPCS_MAX, -}; - -struct zoran_format { - char *name; - __u32 fourcc; - int colorspace; - int depth; - __u32 flags; - __u32 vfespfr; -}; - -/* flags */ -#define ZORAN_FORMAT_COMPRESSED BIT(0) -#define ZORAN_FORMAT_OVERLAY BIT(1) -#define ZORAN_FORMAT_CAPTURE BIT(2) -#define ZORAN_FORMAT_PLAYBACK BIT(3) - -/* v4l-capture settings */ -struct zoran_v4l_settings { - int width, height, bytesperline; /* capture size */ - const struct zoran_format *format; /* capture format */ -}; - -/* jpg-capture/-playback settings */ -struct zoran_jpg_settings { - int decimation; /* this bit is used to set everything to default */ - int hor_dcm, ver_dcm, tmp_dcm; /* capture decimation settings (tmp_dcm=1 means both fields) */ - int field_per_buff, odd_even; /* field-settings (odd_even=1 (+tmp_dcm=1) means top-field-first) */ - int img_x, img_y, img_width, img_height; /* crop settings (subframe capture) */ - struct v4l2_jpegcompression jpg_comp; /* JPEG-specific capture settings */ -}; - -struct zoran; - -/* zoran_fh contains per-open() settings */ -struct zoran_fh { - struct v4l2_fh fh; - struct zoran *zr; -}; - -struct card_info { - enum card_type type; - char name[32]; - const char *i2c_decoder; /* i2c decoder device */ - const unsigned short *addrs_decoder; - const char *i2c_encoder; /* i2c encoder device */ - const unsigned short *addrs_encoder; - u16 video_vfe, video_codec; /* videocodec types */ - u16 audio_chip; /* audio type */ - - int inputs; /* number of video inputs */ - struct input { - int muxsel; - char name[32]; - } input[BUZ_MAX_INPUT]; - - v4l2_std_id norms; - const struct tvnorm *tvn[3]; /* supported TV norms */ - - u32 jpeg_int; /* JPEG interrupt */ - u32 vsync_int; /* VSYNC interrupt */ - s8 gpio[ZR_GPIO_MAX]; - u8 gpcs[GPCS_MAX]; - - struct vfe_polarity vfe_pol; - u8 gpio_pol[ZR_GPIO_MAX]; - - /* is the /GWS line connected? */ - u8 gws_not_connected; - - /* avs6eyes mux setting */ - u8 input_mux; - - void (*init)(struct zoran *zr); -}; - -struct zoran { - struct v4l2_device v4l2_dev; - struct v4l2_ctrl_handler hdl; - struct video_device *video_dev; - struct vb2_queue vq; - - struct i2c_adapter i2c_adapter; /* */ - struct i2c_algo_bit_data i2c_algo; /* */ - u32 i2cbr; - - struct v4l2_subdev *decoder; /* video decoder sub-device */ - struct v4l2_subdev *encoder; /* video encoder sub-device */ - - struct videocodec *codec; /* video codec */ - struct videocodec *vfe; /* video front end */ - - struct mutex lock; /* file ops serialize lock */ - - u8 initialized; /* flag if zoran has been correctly initialized */ - struct card_info card; - const struct tvnorm *timing; - - unsigned short id; /* number of this device */ - char name[32]; /* name of this device */ - struct pci_dev *pci_dev; /* PCI device */ - unsigned char revision; /* revision of zr36057 */ - unsigned char __iomem *zr36057_mem;/* pointer to mapped IO memory */ - - spinlock_t spinlock; /* Spinlock */ - - /* Video for Linux parameters */ - int input; /* card's norm and input */ - v4l2_std_id norm; - - /* Current buffer params */ - unsigned int buffer_size; - - struct zoran_v4l_settings v4l_settings; /* structure with a lot of things to play with */ - - /* Buz MJPEG parameters */ - enum zoran_codec_mode codec_mode; /* status of codec */ - struct zoran_jpg_settings jpg_settings; /* structure with a lot of things to play with */ - - /* grab queue counts/indices, mask with BUZ_MASK_STAT_COM before using as index */ - /* (dma_head - dma_tail) is number active in DMA, must be <= BUZ_NUM_STAT_COM */ - /* (value & BUZ_MASK_STAT_COM) corresponds to index in stat_com table */ - unsigned long jpg_que_head; /* Index where to put next buffer which is queued */ - unsigned long jpg_dma_head; /* Index of next buffer which goes into stat_com */ - unsigned long jpg_dma_tail; /* Index of last buffer in stat_com */ - unsigned long jpg_que_tail; /* Index of last buffer in queue */ - unsigned long jpg_seq_num; /* count of frames since grab/play started */ - unsigned long jpg_err_seq; /* last seq_num before error */ - unsigned long jpg_err_shift; - unsigned long jpg_queued_num; /* count of frames queued since grab/play started */ - unsigned long vbseq; - - /* zr36057's code buffer table */ - __le32 *stat_com; /* stat_com[i] is indexed by dma_head/tail & BUZ_MASK_STAT_COM */ - - /* Additional stuff for testing */ - unsigned int ghost_int; - int intr_counter_GIRQ1; - int intr_counter_GIRQ0; - int intr_counter_cod_rep_irq; - int intr_counter_jpeg_rep_irq; - int field_counter; - int irq1_in; - int irq1_out; - int jpeg_in; - int jpeg_out; - int JPEG_0; - int JPEG_1; - int end_event_missed; - int jpeg_missed; - int jpeg_error; - int num_errors; - int jpeg_max_missed; - int jpeg_min_missed; - unsigned int prepared; - unsigned int queued; - - u32 last_isr; - unsigned long frame_num; - int running; - int buf_in_reserve; - - dma_addr_t p_sc; - __le32 *stat_comb; - dma_addr_t p_scb; - enum zoran_map_mode map_mode; - struct list_head queued_bufs; - spinlock_t queued_bufs_lock; /* Protects queued_bufs */ - struct zr_buffer *inuse[BUZ_NUM_STAT_COM * 2]; - struct dentry *dbgfs_dir; -}; - -static inline struct zoran *to_zoran(struct v4l2_device *v4l2_dev) -{ - return container_of(v4l2_dev, struct zoran, v4l2_dev); -} - -/* There was something called _ALPHA_BUZ that used the PCI address instead of - * the kernel iomapped address for btread/btwrite. */ -#define btwrite(dat, adr) writel((dat), zr->zr36057_mem + (adr)) -#define btread(adr) readl(zr->zr36057_mem + (adr)) - -#define btand(dat, adr) btwrite((dat) & btread(adr), adr) -#define btor(dat, adr) btwrite((dat) | btread(adr), adr) -#define btaor(dat, mask, adr) btwrite((dat) | ((mask) & btread(adr)), adr) - -#endif - -/* - * Debugging macros - */ -#define zrdev_dbg(zr, format, args...) \ - pci_dbg((zr)->pci_dev, format, ##args) \ - -#define zrdev_err(zr, format, args...) \ - pci_err((zr)->pci_dev, format, ##args) \ - -#define zrdev_info(zr, format, args...) \ - pci_info((zr)->pci_dev, format, ##args) \ - -int zoran_queue_init(struct zoran *zr, struct vb2_queue *vq, int dir); -void zoran_queue_exit(struct zoran *zr); -int zr_set_buf(struct zoran *zr); diff --git a/drivers/staging/media/zoran/zoran_card.c b/drivers/staging/media/zoran/zoran_card.c deleted file mode 100644 index 26f978a1cc72..000000000000 --- a/drivers/staging/media/zoran/zoran_card.c +++ /dev/null @@ -1,1442 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Zoran zr36057/zr36067 PCI controller driver, for the - * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux - * Media Labs LML33/LML33R10. - * - * This part handles card-specific data and detection - * - * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx> - */ - -#include <linux/delay.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/slab.h> - -#include <linux/i2c.h> -#include <linux/i2c-algo-bit.h> -#include <linux/videodev2.h> -#include <linux/spinlock.h> - -#include <linux/pci.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <media/v4l2-common.h> -#include <media/i2c/bt819.h> - -#include "videocodec.h" -#include "zoran.h" -#include "zoran_card.h" -#include "zoran_device.h" -#include "zr36016.h" -#include "zr36050.h" -#include "zr36060.h" - -extern const struct zoran_format zoran_formats[]; - -static int card[BUZ_MAX] = { [0 ... (BUZ_MAX - 1)] = -1 }; -module_param_array(card, int, NULL, 0444); -MODULE_PARM_DESC(card, "Card type"); - -/* Default input and video norm at startup of the driver. */ - -static unsigned int default_input; /* default 0 = Composite, 1 = S-Video */ -module_param(default_input, uint, 0444); -MODULE_PARM_DESC(default_input, - "Default input (0=Composite, 1=S-Video, 2=Internal)"); - -static int default_mux = 1; /* 6 Eyes input selection */ -module_param(default_mux, int, 0644); -MODULE_PARM_DESC(default_mux, - "Default 6 Eyes mux setting (Input selection)"); - -static int default_norm; /* default 0 = PAL, 1 = NTSC 2 = SECAM */ -module_param(default_norm, int, 0444); -MODULE_PARM_DESC(default_norm, "Default norm (0=PAL, 1=NTSC, 2=SECAM)"); - -/* /dev/videoN, -1 for autodetect */ -static int video_nr[BUZ_MAX] = { [0 ... (BUZ_MAX - 1)] = -1 }; -module_param_array(video_nr, int, NULL, 0444); -MODULE_PARM_DESC(video_nr, "Video device number (-1=Auto)"); - -/* 1=Pass through TV signal when device is not used */ -/* 0=Show color bar when device is not used (LML33: only if lml33dpath=1) */ -int pass_through; -module_param(pass_through, int, 0644); -MODULE_PARM_DESC(pass_through, - "Pass TV signal through to TV-out when idling"); - -int zr36067_debug = 1; -module_param_named(debug, zr36067_debug, int, 0644); -MODULE_PARM_DESC(debug, "Debug level (0-5)"); - -#define ZORAN_VERSION "0.10.1" - -MODULE_DESCRIPTION("Zoran-36057/36067 JPEG codec driver"); -MODULE_AUTHOR("Serguei Miridonov"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(ZORAN_VERSION); - -#define ZR_DEVICE(subven, subdev, data) { \ - .vendor = PCI_VENDOR_ID_ZORAN, .device = PCI_DEVICE_ID_ZORAN_36057, \ - .subvendor = (subven), .subdevice = (subdev), .driver_data = (data) } - -static const struct pci_device_id zr36067_pci_tbl[] = { - ZR_DEVICE(PCI_VENDOR_ID_MIRO, PCI_DEVICE_ID_MIRO_DC10PLUS, DC10_PLUS), - ZR_DEVICE(PCI_VENDOR_ID_MIRO, PCI_DEVICE_ID_MIRO_DC30PLUS, DC30_PLUS), - ZR_DEVICE(PCI_VENDOR_ID_ELECTRONICDESIGNGMBH, PCI_DEVICE_ID_LML_33R10, LML33R10), - ZR_DEVICE(PCI_VENDOR_ID_IOMEGA, PCI_DEVICE_ID_IOMEGA_BUZ, BUZ), - ZR_DEVICE(PCI_ANY_ID, PCI_ANY_ID, NUM_CARDS), - {0} -}; -MODULE_DEVICE_TABLE(pci, zr36067_pci_tbl); - -static unsigned int zoran_num; /* number of cards found */ - -/* videocodec bus functions ZR36060 */ -static u32 zr36060_read(struct videocodec *codec, u16 reg) -{ - struct zoran *zr = (struct zoran *)codec->master_data->data; - __u32 data; - - if (post_office_wait(zr) || post_office_write(zr, 0, 1, reg >> 8) || - post_office_write(zr, 0, 2, reg & 0xff)) - return -1; - - data = post_office_read(zr, 0, 3) & 0xff; - return data; -} - -static void zr36060_write(struct videocodec *codec, u16 reg, u32 val) -{ - struct zoran *zr = (struct zoran *)codec->master_data->data; - - if (post_office_wait(zr) || post_office_write(zr, 0, 1, reg >> 8) || - post_office_write(zr, 0, 2, reg & 0xff)) - return; - - post_office_write(zr, 0, 3, val & 0xff); -} - -/* videocodec bus functions ZR36050 */ -static u32 zr36050_read(struct videocodec *codec, u16 reg) -{ - struct zoran *zr = (struct zoran *)codec->master_data->data; - __u32 data; - - if (post_office_wait(zr) || post_office_write(zr, 1, 0, reg >> 2)) // reg. HIGHBYTES - return -1; - - data = post_office_read(zr, 0, reg & 0x03) & 0xff; // reg. LOWBYTES + read - return data; -} - -static void zr36050_write(struct videocodec *codec, u16 reg, u32 val) -{ - struct zoran *zr = (struct zoran *)codec->master_data->data; - - if (post_office_wait(zr) || post_office_write(zr, 1, 0, reg >> 2)) // reg. HIGHBYTES - return; - - post_office_write(zr, 0, reg & 0x03, val & 0xff); // reg. LOWBYTES + wr. data -} - -/* videocodec bus functions ZR36016 */ -static u32 zr36016_read(struct videocodec *codec, u16 reg) -{ - struct zoran *zr = (struct zoran *)codec->master_data->data; - __u32 data; - - if (post_office_wait(zr)) - return -1; - - data = post_office_read(zr, 2, reg & 0x03) & 0xff; // read - return data; -} - -/* hack for in zoran_device.c */ -void zr36016_write(struct videocodec *codec, u16 reg, u32 val) -{ - struct zoran *zr = (struct zoran *)codec->master_data->data; - - if (post_office_wait(zr)) - return; - - post_office_write(zr, 2, reg & 0x03, val & 0x0ff); // wr. data -} - -/* - * Board specific information - */ - -static void dc10_init(struct zoran *zr) -{ - pci_dbg(zr->pci_dev, "%s\n", __func__); - - /* Pixel clock selection */ - GPIO(zr, 4, 0); - GPIO(zr, 5, 1); - /* Enable the video bus sync signals */ - GPIO(zr, 7, 0); -} - -static void dc10plus_init(struct zoran *zr) -{ - pci_dbg(zr->pci_dev, "%s\n", __func__); -} - -static void buz_init(struct zoran *zr) -{ - pci_dbg(zr->pci_dev, "%s\n", __func__); - - /* some stuff from Iomega */ - pci_write_config_dword(zr->pci_dev, 0xfc, 0x90680f15); - pci_write_config_dword(zr->pci_dev, 0x0c, 0x00012020); - pci_write_config_dword(zr->pci_dev, 0xe8, 0xc0200000); -} - -static void lml33_init(struct zoran *zr) -{ - pci_dbg(zr->pci_dev, "%s\n", __func__); - - GPIO(zr, 2, 1); // Set Composite input/output -} - -static void avs6eyes_init(struct zoran *zr) -{ - // AverMedia 6-Eyes original driver by Christer Weinigel - - // Lifted straight from Christer's old driver and - // modified slightly by Martin Samuelsson. - - int mux = default_mux; /* 1 = BT866, 7 = VID1 */ - - GPIO(zr, 4, 1); /* Bt866 SLEEP on */ - udelay(2); - - GPIO(zr, 0, 1); /* ZR36060 /RESET on */ - GPIO(zr, 1, 0); /* ZR36060 /SLEEP on */ - GPIO(zr, 2, mux & 1); /* MUX S0 */ - GPIO(zr, 3, 0); /* /FRAME on */ - GPIO(zr, 4, 0); /* Bt866 SLEEP off */ - GPIO(zr, 5, mux & 2); /* MUX S1 */ - GPIO(zr, 6, 0); /* ? */ - GPIO(zr, 7, mux & 4); /* MUX S2 */ -} - -static const char *codecid_to_modulename(u16 codecid) -{ - const char *name = NULL; - - switch (codecid) { - case CODEC_TYPE_ZR36060: - name = "zr36060"; - break; - case CODEC_TYPE_ZR36050: - name = "zr36050"; - break; - case CODEC_TYPE_ZR36016: - name = "zr36016"; - break; - } - - return name; -} - -static int codec_init(struct zoran *zr, u16 codecid) -{ - switch (codecid) { - case CODEC_TYPE_ZR36060: -#ifdef CONFIG_VIDEO_ZORAN_ZR36060 - return zr36060_init_module(); -#else - pci_err(zr->pci_dev, "ZR36060 support is not enabled\n"); - return -EINVAL; -#endif - break; - case CODEC_TYPE_ZR36050: -#ifdef CONFIG_VIDEO_ZORAN_DC30 - return zr36050_init_module(); -#else - pci_err(zr->pci_dev, "ZR36050 support is not enabled\n"); - return -EINVAL; -#endif - break; - case CODEC_TYPE_ZR36016: -#ifdef CONFIG_VIDEO_ZORAN_DC30 - return zr36016_init_module(); -#else - pci_err(zr->pci_dev, "ZR36016 support is not enabled\n"); - return -EINVAL; -#endif - break; - } - - pci_err(zr->pci_dev, "unknown codec id %x\n", codecid); - return -EINVAL; -} - -static void codec_exit(struct zoran *zr, u16 codecid) -{ - switch (codecid) { - case CODEC_TYPE_ZR36060: -#ifdef CONFIG_VIDEO_ZORAN_ZR36060 - zr36060_cleanup_module(); -#endif - break; - case CODEC_TYPE_ZR36050: -#ifdef CONFIG_VIDEO_ZORAN_DC30 - zr36050_cleanup_module(); -#endif - break; - case CODEC_TYPE_ZR36016: -#ifdef CONFIG_VIDEO_ZORAN_DC30 - zr36016_cleanup_module(); -#endif - break; - } -} - -static int videocodec_init(struct zoran *zr) -{ - const char *codec_name, *vfe_name; - int result; - - codec_name = codecid_to_modulename(zr->card.video_codec); - if (codec_name) { - result = codec_init(zr, zr->card.video_codec); - if (result < 0) { - pci_err(zr->pci_dev, "failed to load video codec %s: %d\n", - codec_name, result); - return result; - } - } - vfe_name = codecid_to_modulename(zr->card.video_vfe); - if (vfe_name) { - result = codec_init(zr, zr->card.video_vfe); - if (result < 0) { - pci_err(zr->pci_dev, "failed to load video vfe %s: %d\n", - vfe_name, result); - if (codec_name) - codec_exit(zr, zr->card.video_codec); - return result; - } - } - return 0; -} - -static void videocodec_exit(struct zoran *zr) -{ - if (zr->card.video_codec != CODEC_TYPE_NONE) - codec_exit(zr, zr->card.video_codec); - if (zr->card.video_vfe != CODEC_TYPE_NONE) - codec_exit(zr, zr->card.video_vfe); -} - -// struct tvnorm { -// u16 wt, wa, h_start, h_sync_start, ht, ha, v_start; -// }; - -static const struct tvnorm f50sqpixel = { 944, 768, 83, 880, 625, 576, 16 }; -static const struct tvnorm f60sqpixel = { 780, 640, 51, 716, 525, 480, 12 }; -static const struct tvnorm f50ccir601 = { 864, 720, 75, 804, 625, 576, 18 }; -static const struct tvnorm f60ccir601 = { 858, 720, 57, 788, 525, 480, 16 }; - -static const struct tvnorm f50ccir601_lml33 = { 864, 720, 75 + 34, 804, 625, 576, 18 }; -static const struct tvnorm f60ccir601_lml33 = { 858, 720, 57 + 34, 788, 525, 480, 16 }; - -/* The DC10 (57/16/50) uses VActive as HSync, so h_start must be 0 */ -static const struct tvnorm f50sqpixel_dc10 = { 944, 768, 0, 880, 625, 576, 0 }; -static const struct tvnorm f60sqpixel_dc10 = { 780, 640, 0, 716, 525, 480, 12 }; - -/* - * FIXME: I cannot swap U and V in saa7114, so i do one pixel left shift in zoran (75 -> 74) - * (Maxim Yevtyushkin <max@linuxmedialabs.com>) - */ -static const struct tvnorm f50ccir601_lm33r10 = { 864, 720, 74 + 54, 804, 625, 576, 18 }; -static const struct tvnorm f60ccir601_lm33r10 = { 858, 720, 56 + 54, 788, 525, 480, 16 }; - -/* - * FIXME: The ks0127 seem incapable of swapping U and V, too, which is why I copy Maxim's left - * shift hack for the 6 Eyes. - * - * Christer's driver used the unshifted norms, though... - * /Sam - */ -static const struct tvnorm f50ccir601_avs6eyes = { 864, 720, 74, 804, 625, 576, 18 }; -static const struct tvnorm f60ccir601_avs6eyes = { 858, 720, 56, 788, 525, 480, 16 }; - -static const unsigned short vpx3220_addrs[] = { 0x43, 0x47, I2C_CLIENT_END }; -static const unsigned short saa7110_addrs[] = { 0x4e, 0x4f, I2C_CLIENT_END }; -static const unsigned short saa7111_addrs[] = { 0x25, 0x24, I2C_CLIENT_END }; -static const unsigned short saa7114_addrs[] = { 0x21, 0x20, I2C_CLIENT_END }; -static const unsigned short adv717x_addrs[] = { 0x6a, 0x6b, 0x2a, 0x2b, I2C_CLIENT_END }; -static const unsigned short ks0127_addrs[] = { 0x6c, 0x6d, I2C_CLIENT_END }; -static const unsigned short saa7185_addrs[] = { 0x44, I2C_CLIENT_END }; -static const unsigned short bt819_addrs[] = { 0x45, I2C_CLIENT_END }; -static const unsigned short bt856_addrs[] = { 0x44, I2C_CLIENT_END }; -static const unsigned short bt866_addrs[] = { 0x44, I2C_CLIENT_END }; - -static struct card_info zoran_cards[NUM_CARDS] = { - { - .type = DC10_OLD, - .name = "DC10(old)", - .i2c_decoder = "vpx3220a", - .addrs_decoder = vpx3220_addrs, - .video_codec = CODEC_TYPE_ZR36050, - .video_vfe = CODEC_TYPE_ZR36016, - - .inputs = 3, - .input = { - { 1, "Composite" }, - { 2, "S-Video" }, - { 0, "Internal/comp" } - }, - .norms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM, - .tvn = { - &f50sqpixel_dc10, - &f60sqpixel_dc10, - &f50sqpixel_dc10 - }, - .jpeg_int = 0, - .vsync_int = ZR36057_ISR_GIRQ1, - .gpio = { 2, 1, -1, 3, 7, 0, 4, 5 }, - .gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 }, - .gpcs = { -1, 0 }, - .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, - .gws_not_connected = 0, - .input_mux = 0, - .init = &dc10_init, - }, { - .type = DC10_NEW, - .name = "DC10(new)", - .i2c_decoder = "saa7110", - .addrs_decoder = saa7110_addrs, - .i2c_encoder = "adv7175", - .addrs_encoder = adv717x_addrs, - .video_codec = CODEC_TYPE_ZR36060, - - .inputs = 3, - .input = { - { 0, "Composite" }, - { 7, "S-Video" }, - { 5, "Internal/comp" } - }, - .norms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM, - .tvn = { - &f50sqpixel, - &f60sqpixel, - &f50sqpixel}, - .jpeg_int = ZR36057_ISR_GIRQ0, - .vsync_int = ZR36057_ISR_GIRQ1, - .gpio = { 3, 0, 6, 1, 2, -1, 4, 5 }, - .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, - .gpcs = { -1, 1}, - .vfe_pol = { 1, 1, 1, 1, 0, 0, 0, 0 }, - .gws_not_connected = 0, - .input_mux = 0, - .init = &dc10plus_init, - }, { - .type = DC10_PLUS, - .name = "DC10_PLUS", - .i2c_decoder = "saa7110", - .addrs_decoder = saa7110_addrs, - .i2c_encoder = "adv7175", - .addrs_encoder = adv717x_addrs, - .video_codec = CODEC_TYPE_ZR36060, - - .inputs = 3, - .input = { - { 0, "Composite" }, - { 7, "S-Video" }, - { 5, "Internal/comp" } - }, - .norms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM, - .tvn = { - &f50sqpixel, - &f60sqpixel, - &f50sqpixel - }, - .jpeg_int = ZR36057_ISR_GIRQ0, - .vsync_int = ZR36057_ISR_GIRQ1, - .gpio = { 3, 0, 6, 1, 2, -1, 4, 5 }, - .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, - .gpcs = { -1, 1 }, - .vfe_pol = { 1, 1, 1, 1, 0, 0, 0, 0 }, - .gws_not_connected = 0, - .input_mux = 0, - .init = &dc10plus_init, - }, { - .type = DC30, - .name = "DC30", - .i2c_decoder = "vpx3220a", - .addrs_decoder = vpx3220_addrs, - .i2c_encoder = "adv7175", - .addrs_encoder = adv717x_addrs, - .video_codec = CODEC_TYPE_ZR36050, - .video_vfe = CODEC_TYPE_ZR36016, - - .inputs = 3, - .input = { - { 1, "Composite" }, - { 2, "S-Video" }, - { 0, "Internal/comp" } - }, - .norms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM, - .tvn = { - &f50sqpixel_dc10, - &f60sqpixel_dc10, - &f50sqpixel_dc10 - }, - .jpeg_int = 0, - .vsync_int = ZR36057_ISR_GIRQ1, - .gpio = { 2, 1, -1, 3, 7, 0, 4, 5 }, - .gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 }, - .gpcs = { -1, 0 }, - .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, - .gws_not_connected = 0, - .input_mux = 0, - .init = &dc10_init, - }, { - .type = DC30_PLUS, - .name = "DC30_PLUS", - .i2c_decoder = "vpx3220a", - .addrs_decoder = vpx3220_addrs, - .i2c_encoder = "adv7175", - .addrs_encoder = adv717x_addrs, - .video_codec = CODEC_TYPE_ZR36050, - .video_vfe = CODEC_TYPE_ZR36016, - - .inputs = 3, - .input = { - { 1, "Composite" }, - { 2, "S-Video" }, - { 0, "Internal/comp" } - }, - .norms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM, - .tvn = { - &f50sqpixel_dc10, - &f60sqpixel_dc10, - &f50sqpixel_dc10 - }, - .jpeg_int = 0, - .vsync_int = ZR36057_ISR_GIRQ1, - .gpio = { 2, 1, -1, 3, 7, 0, 4, 5 }, - .gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 }, - .gpcs = { -1, 0 }, - .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, - .gws_not_connected = 0, - .input_mux = 0, - .init = &dc10_init, - }, { - .type = LML33, - .name = "LML33", - .i2c_decoder = "bt819a", - .addrs_decoder = bt819_addrs, - .i2c_encoder = "bt856", - .addrs_encoder = bt856_addrs, - .video_codec = CODEC_TYPE_ZR36060, - - .inputs = 2, - .input = { - { 0, "Composite" }, - { 7, "S-Video" } - }, - .norms = V4L2_STD_NTSC | V4L2_STD_PAL, - .tvn = { - &f50ccir601_lml33, - &f60ccir601_lml33, - NULL - }, - .jpeg_int = ZR36057_ISR_GIRQ1, - .vsync_int = ZR36057_ISR_GIRQ0, - .gpio = { 1, -1, 3, 5, 7, -1, -1, -1 }, - .gpio_pol = { 0, 0, 0, 0, 1, 0, 0, 0 }, - .gpcs = { 3, 1 }, - .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 }, - .gws_not_connected = 1, - .input_mux = 0, - .init = &lml33_init, - }, { - .type = LML33R10, - .name = "LML33R10", - .i2c_decoder = "saa7114", - .addrs_decoder = saa7114_addrs, - .i2c_encoder = "adv7170", - .addrs_encoder = adv717x_addrs, - .video_codec = CODEC_TYPE_ZR36060, - - .inputs = 2, - .input = { - { 0, "Composite" }, - { 7, "S-Video" } - }, - .norms = V4L2_STD_NTSC | V4L2_STD_PAL, - .tvn = { - &f50ccir601_lm33r10, - &f60ccir601_lm33r10, - NULL - }, - .jpeg_int = ZR36057_ISR_GIRQ1, - .vsync_int = ZR36057_ISR_GIRQ0, - .gpio = { 1, -1, 3, 5, 7, -1, -1, -1 }, - .gpio_pol = { 0, 0, 0, 0, 1, 0, 0, 0 }, - .gpcs = { 3, 1 }, - .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 }, - .gws_not_connected = 1, - .input_mux = 0, - .init = &lml33_init, - }, { - .type = BUZ, - .name = "Buz", - .i2c_decoder = "saa7111", - .addrs_decoder = saa7111_addrs, - .i2c_encoder = "saa7185", - .addrs_encoder = saa7185_addrs, - .video_codec = CODEC_TYPE_ZR36060, - - .inputs = 2, - .input = { - { 3, "Composite" }, - { 7, "S-Video" } - }, - .norms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM, - .tvn = { - &f50ccir601, - &f60ccir601, - &f50ccir601 - }, - .jpeg_int = ZR36057_ISR_GIRQ1, - .vsync_int = ZR36057_ISR_GIRQ0, - .gpio = { 1, -1, 3, -1, -1, -1, -1, -1 }, - .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, - .gpcs = { 3, 1 }, - .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 }, - .gws_not_connected = 1, - .input_mux = 0, - .init = &buz_init, - }, { - .type = AVS6EYES, - .name = "6-Eyes", -/* AverMedia chose not to brand the 6-Eyes. Thus it can't be autodetected, and requires card=x. */ - .i2c_decoder = "ks0127", - .addrs_decoder = ks0127_addrs, - .i2c_encoder = "bt866", - .addrs_encoder = bt866_addrs, - .video_codec = CODEC_TYPE_ZR36060, - - .inputs = 10, - .input = { - { 0, "Composite 1" }, - { 1, "Composite 2" }, - { 2, "Composite 3" }, - { 4, "Composite 4" }, - { 5, "Composite 5" }, - { 6, "Composite 6" }, - { 8, "S-Video 1" }, - { 9, "S-Video 2" }, - {10, "S-Video 3" }, - {15, "YCbCr" } - }, - .norms = V4L2_STD_NTSC | V4L2_STD_PAL, - .tvn = { - &f50ccir601_avs6eyes, - &f60ccir601_avs6eyes, - NULL - }, - .jpeg_int = ZR36057_ISR_GIRQ1, - .vsync_int = ZR36057_ISR_GIRQ0, - .gpio = { 1, 0, 3, -1, -1, -1, -1, -1 },// Validity unknown /Sam - .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, // Validity unknown /Sam - .gpcs = { 3, 1 }, // Validity unknown /Sam - .vfe_pol = { 1, 0, 0, 0, 0, 1, 0, 0 }, // Validity unknown /Sam - .gws_not_connected = 1, - .input_mux = 1, - .init = &avs6eyes_init, - } - -}; - -/* - * I2C functions - */ -/* software I2C functions */ -static int zoran_i2c_getsda(void *data) -{ - struct zoran *zr = (struct zoran *)data; - - return (btread(ZR36057_I2CBR) >> 1) & 1; -} - -static int zoran_i2c_getscl(void *data) -{ - struct zoran *zr = (struct zoran *)data; - - return btread(ZR36057_I2CBR) & 1; -} - -static void zoran_i2c_setsda(void *data, int state) -{ - struct zoran *zr = (struct zoran *)data; - - if (state) - zr->i2cbr |= 2; - else - zr->i2cbr &= ~2; - btwrite(zr->i2cbr, ZR36057_I2CBR); -} - -static void zoran_i2c_setscl(void *data, int state) -{ - struct zoran *zr = (struct zoran *)data; - - if (state) - zr->i2cbr |= 1; - else - zr->i2cbr &= ~1; - btwrite(zr->i2cbr, ZR36057_I2CBR); -} - -static const struct i2c_algo_bit_data zoran_i2c_bit_data_template = { - .setsda = zoran_i2c_setsda, - .setscl = zoran_i2c_setscl, - .getsda = zoran_i2c_getsda, - .getscl = zoran_i2c_getscl, - .udelay = 10, - .timeout = 100, -}; - -static int zoran_register_i2c(struct zoran *zr) -{ - zr->i2c_algo = zoran_i2c_bit_data_template; - zr->i2c_algo.data = zr; - strscpy(zr->i2c_adapter.name, ZR_DEVNAME(zr), - sizeof(zr->i2c_adapter.name)); - i2c_set_adapdata(&zr->i2c_adapter, &zr->v4l2_dev); - zr->i2c_adapter.algo_data = &zr->i2c_algo; - zr->i2c_adapter.dev.parent = &zr->pci_dev->dev; - return i2c_bit_add_bus(&zr->i2c_adapter); -} - -static void zoran_unregister_i2c(struct zoran *zr) -{ - i2c_del_adapter(&zr->i2c_adapter); -} - -/* Check a zoran_params struct for correctness, insert default params */ -int zoran_check_jpg_settings(struct zoran *zr, - struct zoran_jpg_settings *settings, int try) -{ - int err = 0, err0 = 0; - - pci_dbg(zr->pci_dev, "%s - dec: %d, Hdcm: %d, Vdcm: %d, Tdcm: %d\n", - __func__, settings->decimation, settings->hor_dcm, - settings->ver_dcm, settings->tmp_dcm); - pci_dbg(zr->pci_dev, "%s - x: %d, y: %d, w: %d, y: %d\n", __func__, - settings->img_x, settings->img_y, - settings->img_width, settings->img_height); - /* Check decimation, set default values for decimation = 1, 2, 4 */ - switch (settings->decimation) { - case 1: - - settings->hor_dcm = 1; - settings->ver_dcm = 1; - settings->tmp_dcm = 1; - settings->field_per_buff = 2; - settings->img_x = 0; - settings->img_y = 0; - settings->img_width = BUZ_MAX_WIDTH; - settings->img_height = BUZ_MAX_HEIGHT / 2; - break; - case 2: - - settings->hor_dcm = 2; - settings->ver_dcm = 1; - settings->tmp_dcm = 2; - settings->field_per_buff = 1; - settings->img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0; - settings->img_y = 0; - settings->img_width = - (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH; - settings->img_height = BUZ_MAX_HEIGHT / 2; - break; - case 4: - - if (zr->card.type == DC10_NEW) { - pci_dbg(zr->pci_dev, "%s - HDec by 4 is not supported on the DC10\n", __func__); - err0++; - break; - } - - settings->hor_dcm = 4; - settings->ver_dcm = 2; - settings->tmp_dcm = 2; - settings->field_per_buff = 1; - settings->img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0; - settings->img_y = 0; - settings->img_width = - (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH; - settings->img_height = BUZ_MAX_HEIGHT / 2; - break; - case 0: - - /* We have to check the data the user has set */ - - if (settings->hor_dcm != 1 && settings->hor_dcm != 2 && - (zr->card.type == DC10_NEW || settings->hor_dcm != 4)) { - settings->hor_dcm = clamp(settings->hor_dcm, 1, 2); - err0++; - } - if (settings->ver_dcm != 1 && settings->ver_dcm != 2) { - settings->ver_dcm = clamp(settings->ver_dcm, 1, 2); - err0++; - } - if (settings->tmp_dcm != 1 && settings->tmp_dcm != 2) { - settings->tmp_dcm = clamp(settings->tmp_dcm, 1, 2); - err0++; - } - if (settings->field_per_buff != 1 && - settings->field_per_buff != 2) { - settings->field_per_buff = clamp(settings->field_per_buff, 1, 2); - err0++; - } - if (settings->img_x < 0) { - settings->img_x = 0; - err0++; - } - if (settings->img_y < 0) { - settings->img_y = 0; - err0++; - } - if (settings->img_width < 0 || settings->img_width > BUZ_MAX_WIDTH) { - settings->img_width = clamp(settings->img_width, 0, (int)BUZ_MAX_WIDTH); - err0++; - } - if (settings->img_height < 0 || settings->img_height > BUZ_MAX_HEIGHT / 2) { - settings->img_height = clamp(settings->img_height, 0, BUZ_MAX_HEIGHT / 2); - err0++; - } - if (settings->img_x + settings->img_width > BUZ_MAX_WIDTH) { - settings->img_x = BUZ_MAX_WIDTH - settings->img_width; - err0++; - } - if (settings->img_y + settings->img_height > BUZ_MAX_HEIGHT / 2) { - settings->img_y = BUZ_MAX_HEIGHT / 2 - settings->img_height; - err0++; - } - if (settings->img_width % (16 * settings->hor_dcm) != 0) { - settings->img_width -= settings->img_width % (16 * settings->hor_dcm); - if (settings->img_width == 0) - settings->img_width = 16 * settings->hor_dcm; - err0++; - } - if (settings->img_height % (8 * settings->ver_dcm) != 0) { - settings->img_height -= settings->img_height % (8 * settings->ver_dcm); - if (settings->img_height == 0) - settings->img_height = 8 * settings->ver_dcm; - err0++; - } - - if (!try && err0) { - pci_err(zr->pci_dev, "%s - error in params for decimation = 0\n", __func__); - err++; - } - break; - default: - pci_err(zr->pci_dev, "%s - decimation = %d, must be 0, 1, 2 or 4\n", - __func__, settings->decimation); - err++; - break; - } - - if (settings->jpg_comp.quality > 100) - settings->jpg_comp.quality = 100; - if (settings->jpg_comp.quality < 5) - settings->jpg_comp.quality = 5; - if (settings->jpg_comp.APPn < 0) - settings->jpg_comp.APPn = 0; - if (settings->jpg_comp.APPn > 15) - settings->jpg_comp.APPn = 15; - if (settings->jpg_comp.APP_len < 0) - settings->jpg_comp.APP_len = 0; - if (settings->jpg_comp.APP_len > 60) - settings->jpg_comp.APP_len = 60; - if (settings->jpg_comp.COM_len < 0) - settings->jpg_comp.COM_len = 0; - if (settings->jpg_comp.COM_len > 60) - settings->jpg_comp.COM_len = 60; - if (err) - return -EINVAL; - return 0; -} - -static int zoran_init_video_device(struct zoran *zr, struct video_device *video_dev, int dir) -{ - int err; - - /* Now add the template and register the device unit. */ - *video_dev = zoran_template; - video_dev->v4l2_dev = &zr->v4l2_dev; - video_dev->lock = &zr->lock; - video_dev->device_caps = V4L2_CAP_STREAMING | dir; - - strscpy(video_dev->name, ZR_DEVNAME(zr), sizeof(video_dev->name)); - /* - * It's not a mem2mem device, but you can both capture and output from one and the same - * device. This should really be split up into two device nodes, but that's a job for - * another day. - */ - video_dev->vfl_dir = VFL_DIR_M2M; - zoran_queue_init(zr, &zr->vq, V4L2_BUF_TYPE_VIDEO_CAPTURE); - - err = video_register_device(video_dev, VFL_TYPE_VIDEO, video_nr[zr->id]); - if (err < 0) - return err; - video_set_drvdata(video_dev, zr); - return 0; -} - -static void zoran_exit_video_devices(struct zoran *zr) -{ - video_unregister_device(zr->video_dev); - kfree(zr->video_dev); -} - -static int zoran_init_video_devices(struct zoran *zr) -{ - int err; - - zr->video_dev = video_device_alloc(); - if (!zr->video_dev) - return -ENOMEM; - - err = zoran_init_video_device(zr, zr->video_dev, V4L2_CAP_VIDEO_CAPTURE); - if (err) - kfree(zr->video_dev); - return err; -} - -/* - * v4l2_device_unregister() will care about removing zr->encoder/zr->decoder - * via v4l2_i2c_subdev_unregister() - */ -static int zoran_i2c_init(struct zoran *zr) -{ - int err; - - pci_info(zr->pci_dev, "Initializing i2c bus...\n"); - - err = zoran_register_i2c(zr); - if (err) { - pci_err(zr->pci_dev, "%s - cannot initialize i2c bus\n", __func__); - return err; - } - - zr->decoder = v4l2_i2c_new_subdev(&zr->v4l2_dev, &zr->i2c_adapter, - zr->card.i2c_decoder, 0, - zr->card.addrs_decoder); - if (!zr->decoder) { - pci_err(zr->pci_dev, "Fail to get decoder %s\n", zr->card.i2c_decoder); - err = -EINVAL; - goto error_decoder; - } - - if (zr->card.i2c_encoder) { - zr->encoder = v4l2_i2c_new_subdev(&zr->v4l2_dev, &zr->i2c_adapter, - zr->card.i2c_encoder, 0, - zr->card.addrs_encoder); - if (!zr->encoder) { - pci_err(zr->pci_dev, "Fail to get encoder %s\n", zr->card.i2c_encoder); - err = -EINVAL; - goto error_decoder; - } - } - return 0; - -error_decoder: - zoran_unregister_i2c(zr); - return err; -} - -static void zoran_i2c_exit(struct zoran *zr) -{ - zoran_unregister_i2c(zr); -} - -void zoran_open_init_params(struct zoran *zr) -{ - int i; - - zr->v4l_settings.width = 192; - zr->v4l_settings.height = 144; - zr->v4l_settings.format = &zoran_formats[7]; /* YUY2 - YUV-4:2:2 packed */ - zr->v4l_settings.bytesperline = zr->v4l_settings.width * - ((zr->v4l_settings.format->depth + 7) / 8); - - /* Set necessary params and call zoran_check_jpg_settings to set the defaults */ - zr->jpg_settings.decimation = 1; - zr->jpg_settings.jpg_comp.quality = 50; /* default compression factor 8 */ - if (zr->card.type != BUZ) - zr->jpg_settings.odd_even = 1; - else - zr->jpg_settings.odd_even = 0; - zr->jpg_settings.jpg_comp.APPn = 0; - zr->jpg_settings.jpg_comp.APP_len = 0; /* No APPn marker */ - memset(zr->jpg_settings.jpg_comp.APP_data, 0, - sizeof(zr->jpg_settings.jpg_comp.APP_data)); - zr->jpg_settings.jpg_comp.COM_len = 0; /* No COM marker */ - memset(zr->jpg_settings.jpg_comp.COM_data, 0, - sizeof(zr->jpg_settings.jpg_comp.COM_data)); - zr->jpg_settings.jpg_comp.jpeg_markers = - V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT; - i = zoran_check_jpg_settings(zr, &zr->jpg_settings, 0); - if (i) - pci_err(zr->pci_dev, "%s internal error\n", __func__); - - zr->buffer_size = zr->v4l_settings.bytesperline * zr->v4l_settings.height; - - clear_interrupt_counters(zr); -} - -static int zr36057_init(struct zoran *zr) -{ - int j, err; - - pci_info(zr->pci_dev, "initializing card[%d]\n", zr->id); - - /* Avoid nonsense settings from user for default input/norm */ - if (default_norm < 0 || default_norm > 2) - default_norm = 0; - if (default_norm == 0) { - zr->norm = V4L2_STD_PAL; - zr->timing = zr->card.tvn[ZR_NORM_PAL]; - } else if (default_norm == 1) { - zr->norm = V4L2_STD_NTSC; - zr->timing = zr->card.tvn[ZR_NORM_NTSC]; - } else { - zr->norm = V4L2_STD_SECAM; - zr->timing = zr->card.tvn[ZR_NORM_SECAM]; - } - if (!zr->timing) { - pci_warn(zr->pci_dev, "%s - default TV standard not supported by hardware. PAL will be used.\n", __func__); - zr->norm = V4L2_STD_PAL; - zr->timing = zr->card.tvn[ZR_NORM_PAL]; - } - - if (default_input > zr->card.inputs - 1) { - pci_warn(zr->pci_dev, "default_input value %d out of range (0-%d)\n", - default_input, zr->card.inputs - 1); - default_input = 0; - } - zr->input = default_input; - - /* default setup (will be repeated at every open) */ - zoran_open_init_params(zr); - - /* allocate memory *before* doing anything to the hardware in case allocation fails */ - zr->stat_com = dma_alloc_coherent(&zr->pci_dev->dev, - BUZ_NUM_STAT_COM * sizeof(u32), - &zr->p_sc, GFP_KERNEL); - if (!zr->stat_com) { - return -ENOMEM; - } - for (j = 0; j < BUZ_NUM_STAT_COM; j++) - zr->stat_com[j] = cpu_to_le32(1); /* mark as unavailable to zr36057 */ - - zr->stat_comb = dma_alloc_coherent(&zr->pci_dev->dev, - BUZ_NUM_STAT_COM * sizeof(u32) * 2, - &zr->p_scb, GFP_KERNEL); - if (!zr->stat_comb) { - err = -ENOMEM; - goto exit_statcom; - } - - err = zoran_init_video_devices(zr); - if (err) - goto exit_statcomb; - - zoran_init_hardware(zr); - if (!pass_through) { - decoder_call(zr, video, s_stream, 0); - encoder_call(zr, video, s_routing, 2, 0, 0); - } - - zr->initialized = 1; - return 0; - -exit_statcomb: - dma_free_coherent(&zr->pci_dev->dev, BUZ_NUM_STAT_COM * sizeof(u32) * 2, zr->stat_comb, zr->p_scb); -exit_statcom: - dma_free_coherent(&zr->pci_dev->dev, BUZ_NUM_STAT_COM * sizeof(u32), zr->stat_com, zr->p_sc); - return err; -} - -static void zoran_remove(struct pci_dev *pdev) -{ - struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); - struct zoran *zr = to_zoran(v4l2_dev); - - if (!zr->initialized) - goto exit_free; - - debugfs_remove_recursive(zr->dbgfs_dir); - - zoran_queue_exit(zr); - - /* unregister videocodec bus */ - if (zr->codec) - videocodec_detach(zr->codec); - if (zr->vfe) - videocodec_detach(zr->vfe); - videocodec_exit(zr); - - /* unregister i2c bus */ - zoran_i2c_exit(zr); - /* disable PCI bus-mastering */ - zoran_set_pci_master(zr, 0); - /* put chip into reset */ - btwrite(0, ZR36057_SPGPPCR); - pci_free_irq(zr->pci_dev, 0, zr); - /* unmap and free memory */ - dma_free_coherent(&zr->pci_dev->dev, BUZ_NUM_STAT_COM * sizeof(u32), zr->stat_com, zr->p_sc); - dma_free_coherent(&zr->pci_dev->dev, BUZ_NUM_STAT_COM * sizeof(u32) * 2, zr->stat_comb, zr->p_scb); - pci_release_regions(pdev); - pci_disable_device(zr->pci_dev); - zoran_exit_video_devices(zr); -exit_free: - v4l2_ctrl_handler_free(&zr->hdl); - v4l2_device_unregister(&zr->v4l2_dev); -} - -void zoran_vdev_release(struct video_device *vdev) -{ - kfree(vdev); -} - -static struct videocodec_master *zoran_setup_videocodec(struct zoran *zr, - int type) -{ - struct videocodec_master *m = NULL; - - m = devm_kmalloc(&zr->pci_dev->dev, sizeof(*m), GFP_KERNEL); - if (!m) - return m; - - /* - * magic and type are unused for master struct. Makes sense only at codec structs. - * In the past, .type were initialized to the old V4L1 .hardware value, - * as VID_HARDWARE_ZR36067 - */ - m->magic = 0L; - m->type = 0; - - m->flags = CODEC_FLAG_ENCODER | CODEC_FLAG_DECODER; - strscpy(m->name, ZR_DEVNAME(zr), sizeof(m->name)); - m->data = zr; - - switch (type) { - case CODEC_TYPE_ZR36060: - m->readreg = zr36060_read; - m->writereg = zr36060_write; - m->flags |= CODEC_FLAG_JPEG | CODEC_FLAG_VFE; - break; - case CODEC_TYPE_ZR36050: - m->readreg = zr36050_read; - m->writereg = zr36050_write; - m->flags |= CODEC_FLAG_JPEG; - break; - case CODEC_TYPE_ZR36016: - m->readreg = zr36016_read; - m->writereg = zr36016_write; - m->flags |= CODEC_FLAG_VFE; - break; - } - - return m; -} - -static void zoran_subdev_notify(struct v4l2_subdev *sd, unsigned int cmd, void *arg) -{ - struct zoran *zr = to_zoran(sd->v4l2_dev); - - /* - * Bt819 needs to reset its FIFO buffer using #FRST pin and - * LML33 card uses GPIO(7) for that. - */ - if (cmd == BT819_FIFO_RESET_LOW) - GPIO(zr, 7, 0); - else if (cmd == BT819_FIFO_RESET_HIGH) - GPIO(zr, 7, 1); -} - -static int zoran_video_set_ctrl(struct v4l2_ctrl *ctrl) -{ - struct zoran *zr = container_of(ctrl->handler, struct zoran, hdl); - - switch (ctrl->id) { - case V4L2_CID_JPEG_COMPRESSION_QUALITY: - zr->jpg_settings.jpg_comp.quality = ctrl->val; - return zoran_check_jpg_settings(zr, &zr->jpg_settings, 0); - default: - return -EINVAL; - } - - return 0; -} - -static const struct v4l2_ctrl_ops zoran_video_ctrl_ops = { - .s_ctrl = zoran_video_set_ctrl, -}; - -static int zoran_debugfs_show(struct seq_file *seq, void *v) -{ - struct zoran *zr = seq->private; - - seq_printf(seq, "Running mode %x\n", zr->running); - seq_printf(seq, "Codec mode %x\n", zr->codec_mode); - seq_printf(seq, "Norm %llx\n", zr->norm); - seq_printf(seq, "Input %d\n", zr->input); - seq_printf(seq, "Buffersize %d\n", zr->buffer_size); - - seq_printf(seq, "V4L width %dx%d\n", zr->v4l_settings.width, zr->v4l_settings.height); - seq_printf(seq, "V4L bytesperline %d\n", zr->v4l_settings.bytesperline); - - seq_printf(seq, "JPG decimation %u\n", zr->jpg_settings.decimation); - seq_printf(seq, "JPG hor_dcm %u\n", zr->jpg_settings.hor_dcm); - seq_printf(seq, "JPG ver_dcm %u\n", zr->jpg_settings.ver_dcm); - seq_printf(seq, "JPG tmp_dcm %u\n", zr->jpg_settings.tmp_dcm); - seq_printf(seq, "JPG odd_even %u\n", zr->jpg_settings.odd_even); - seq_printf(seq, "JPG crop %dx%d %d %d\n", - zr->jpg_settings.img_x, - zr->jpg_settings.img_y, - zr->jpg_settings.img_width, - zr->jpg_settings.img_height); - - seq_printf(seq, "Prepared %u\n", zr->prepared); - seq_printf(seq, "Queued %u\n", zr->queued); - - videocodec_debugfs_show(seq); - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(zoran_debugfs); - -/* - * Scan for a Buz card (actually for the PCI controller ZR36057), - * request the irq and map the io memory - */ -static int zoran_probe(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - unsigned char latency, need_latency; - struct zoran *zr; - int result; - struct videocodec_master *master_vfe = NULL; - struct videocodec_master *master_codec = NULL; - int card_num; - unsigned int nr; - int err; - - pci_info(pdev, "Zoran MJPEG board driver version %s\n", ZORAN_VERSION); - - /* some mainboards might not do PCI-PCI data transfer well */ - if (pci_pci_problems & (PCIPCI_FAIL | PCIAGP_FAIL | PCIPCI_ALIMAGIK)) - pci_warn(pdev, "%s: chipset does not support reliable PCI-PCI DMA\n", - ZORAN_NAME); - - err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); - if (err) - return err; - err = vb2_dma_contig_set_max_seg_size(&pdev->dev, U32_MAX); - if (err) - return err; - - nr = zoran_num++; - if (nr >= BUZ_MAX) { - pci_err(pdev, "driver limited to %d card(s) maximum\n", BUZ_MAX); - return -ENOENT; - } - - zr = devm_kzalloc(&pdev->dev, sizeof(*zr), GFP_KERNEL); - if (!zr) - return -ENOMEM; - - zr->v4l2_dev.notify = zoran_subdev_notify; - if (v4l2_device_register(&pdev->dev, &zr->v4l2_dev)) - goto zr_free_mem; - zr->pci_dev = pdev; - zr->id = nr; - snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)), "MJPEG[%u]", zr->id); - if (v4l2_ctrl_handler_init(&zr->hdl, 10)) - goto zr_unreg; - zr->v4l2_dev.ctrl_handler = &zr->hdl; - v4l2_ctrl_new_std(&zr->hdl, &zoran_video_ctrl_ops, - V4L2_CID_JPEG_COMPRESSION_QUALITY, 0, - 100, 1, 50); - spin_lock_init(&zr->spinlock); - mutex_init(&zr->lock); - if (pci_enable_device(pdev)) - goto zr_unreg; - zr->revision = zr->pci_dev->revision; - - pci_info(zr->pci_dev, "Zoran ZR360%c7 (rev %d), irq: %d, memory: 0x%08llx\n", - zr->revision < 2 ? '5' : '6', zr->revision, - zr->pci_dev->irq, (uint64_t)pci_resource_start(zr->pci_dev, 0)); - if (zr->revision >= 2) - pci_info(zr->pci_dev, "Subsystem vendor=0x%04x id=0x%04x\n", - zr->pci_dev->subsystem_vendor, zr->pci_dev->subsystem_device); - - /* Use auto-detected card type? */ - if (card[nr] == -1) { - if (zr->revision < 2) { - pci_err(pdev, "No card type specified, please use the card=X module parameter\n"); - pci_err(pdev, "It is not possible to auto-detect ZR36057 based cards\n"); - goto zr_unreg; - } - - card_num = ent->driver_data; - if (card_num >= NUM_CARDS) { - pci_err(pdev, "Unknown card, try specifying card=X module parameter\n"); - goto zr_unreg; - } - pci_info(zr->pci_dev, "%s() - card %s detected\n", __func__, zoran_cards[card_num].name); - } else { - card_num = card[nr]; - if (card_num >= NUM_CARDS || card_num < 0) { - pci_err(pdev, "User specified card type %d out of range (0 .. %d)\n", - card_num, NUM_CARDS - 1); - goto zr_unreg; - } - } - - /* - * even though we make this a non pointer and thus - * theoretically allow for making changes to this struct - * on a per-individual card basis at runtime, this is - * strongly discouraged. This structure is intended to - * keep general card information, no settings or anything - */ - zr->card = zoran_cards[card_num]; - snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)), "%s[%u]", - zr->card.name, zr->id); - - err = pci_request_regions(pdev, ZR_DEVNAME(zr)); - if (err) - goto zr_unreg; - - zr->zr36057_mem = devm_ioremap(&pdev->dev, pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); - if (!zr->zr36057_mem) { - pci_err(pdev, "%s() - ioremap failed\n", __func__); - goto zr_pci_release; - } - - result = pci_request_irq(pdev, 0, zoran_irq, NULL, zr, ZR_DEVNAME(zr)); - if (result < 0) { - if (result == -EINVAL) { - pci_err(pdev, "%s - bad IRQ number or handler\n", __func__); - } else if (result == -EBUSY) { - pci_err(pdev, "%s - IRQ %d busy, change your PnP config in BIOS\n", - __func__, zr->pci_dev->irq); - } else { - pci_err(pdev, "%s - cannot assign IRQ, error code %d\n", __func__, result); - } - goto zr_pci_release; - } - - /* set PCI latency timer */ - pci_read_config_byte(zr->pci_dev, PCI_LATENCY_TIMER, - &latency); - need_latency = zr->revision > 1 ? 32 : 48; - if (latency != need_latency) { - pci_info(zr->pci_dev, "Changing PCI latency from %d to %d\n", latency, need_latency); - pci_write_config_byte(zr->pci_dev, PCI_LATENCY_TIMER, need_latency); - } - - zr36057_restart(zr); - - err = zoran_i2c_init(zr); - if (err) - goto zr_free_irq; - - pci_info(zr->pci_dev, "Initializing videocodec bus...\n"); - err = videocodec_init(zr); - if (err) - goto zr_unreg_i2c; - - /* reset JPEG codec */ - jpeg_codec_sleep(zr, 1); - jpeg_codec_reset(zr); - /* video bus enabled */ - /* display codec revision */ - if (zr->card.video_codec != 0) { - master_codec = zoran_setup_videocodec(zr, zr->card.video_codec); - if (!master_codec) - goto zr_unreg_videocodec; - zr->codec = videocodec_attach(master_codec); - if (!zr->codec) { - pci_err(pdev, "%s - no codec found\n", __func__); - goto zr_unreg_videocodec; - } - if (zr->codec->type != zr->card.video_codec) { - pci_err(pdev, "%s - wrong codec\n", __func__); - goto zr_unreg_videocodec; - } - } - if (zr->card.video_vfe != 0) { - master_vfe = zoran_setup_videocodec(zr, zr->card.video_vfe); - if (!master_vfe) - goto zr_detach_codec; - zr->vfe = videocodec_attach(master_vfe); - if (!zr->vfe) { - pci_err(pdev, "%s - no VFE found\n", __func__); - goto zr_detach_codec; - } - if (zr->vfe->type != zr->card.video_vfe) { - pci_err(pdev, "%s = wrong VFE\n", __func__); - goto zr_detach_vfe; - } - } - - /* take care of Natoma chipset and a revision 1 zr36057 */ - if ((pci_pci_problems & PCIPCI_NATOMA) && zr->revision <= 1) - pci_info(zr->pci_dev, "ZR36057/Natoma bug, max. buffer size is 128K\n"); - - if (zr36057_init(zr) < 0) - goto zr_detach_vfe; - - zr->map_mode = ZORAN_MAP_MODE_RAW; - - zr->dbgfs_dir = debugfs_create_dir(ZR_DEVNAME(zr), NULL); - debugfs_create_file("debug", 0444, zr->dbgfs_dir, zr, - &zoran_debugfs_fops); - return 0; - -zr_detach_vfe: - videocodec_detach(zr->vfe); -zr_detach_codec: - videocodec_detach(zr->codec); -zr_unreg_videocodec: - videocodec_exit(zr); -zr_unreg_i2c: - zoran_i2c_exit(zr); -zr_free_irq: - btwrite(0, ZR36057_SPGPPCR); - pci_free_irq(zr->pci_dev, 0, zr); -zr_pci_release: - pci_release_regions(pdev); -zr_unreg: - v4l2_ctrl_handler_free(&zr->hdl); - v4l2_device_unregister(&zr->v4l2_dev); -zr_free_mem: - - return -ENODEV; -} - -static struct pci_driver zoran_driver = { - .name = "zr36067", - .id_table = zr36067_pci_tbl, - .probe = zoran_probe, - .remove = zoran_remove, -}; - -module_pci_driver(zoran_driver); diff --git a/drivers/staging/media/zoran/zoran_card.h b/drivers/staging/media/zoran/zoran_card.h deleted file mode 100644 index 8e0d634cb30f..000000000000 --- a/drivers/staging/media/zoran/zoran_card.h +++ /dev/null @@ -1,30 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Zoran zr36057/zr36067 PCI controller driver, for the - * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux - * Media Labs LML33/LML33R10. - * - * This part handles card-specific data and detection - * - * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx> - */ - -#ifndef __ZORAN_CARD_H__ -#define __ZORAN_CARD_H__ - -extern int zr36067_debug; - -/* Anybody who uses more than four? */ -#define BUZ_MAX 4 - -extern const struct video_device zoran_template; - -extern int zoran_check_jpg_settings(struct zoran *zr, - struct zoran_jpg_settings *settings, - int try); -extern void zoran_open_init_params(struct zoran *zr); -extern void zoran_vdev_release(struct video_device *vdev); - -void zr36016_write(struct videocodec *codec, u16 reg, u32 val); - -#endif /* __ZORAN_CARD_H__ */ diff --git a/drivers/staging/media/zoran/zoran_device.c b/drivers/staging/media/zoran/zoran_device.c deleted file mode 100644 index 2470889a58fa..000000000000 --- a/drivers/staging/media/zoran/zoran_device.c +++ /dev/null @@ -1,953 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Zoran zr36057/zr36067 PCI controller driver, for the - * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux - * Media Labs LML33/LML33R10. - * - * This part handles device access (PCI/I2C/codec/...) - * - * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx> - */ - -#include <linux/types.h> -#include <linux/kernel.h> -#include <linux/module.h> - -#include <linux/interrupt.h> -#include <linux/i2c.h> -#include <linux/i2c-algo-bit.h> -#include <linux/videodev2.h> -#include <media/v4l2-common.h> -#include <linux/spinlock.h> - -#include <linux/pci.h> -#include <linux/delay.h> -#include <linux/wait.h> -#include <linux/dma-mapping.h> - -#include <linux/io.h> - -#include "videocodec.h" -#include "zoran.h" -#include "zoran_device.h" -#include "zoran_card.h" - -#define IRQ_MASK (ZR36057_ISR_GIRQ0 | \ - ZR36057_ISR_GIRQ1 | \ - ZR36057_ISR_JPEG_REP_IRQ) - -static bool lml33dpath; /* default = 0 - * 1 will use digital path in capture - * mode instead of analog. It can be - * used for picture adjustments using - * tool like xawtv while watching image - * on TV monitor connected to the output. - * However, due to absence of 75 Ohm - * load on Bt819 input, there will be - * some image imperfections - */ - -module_param(lml33dpath, bool, 0644); -MODULE_PARM_DESC(lml33dpath, "Use digital path capture mode (on LML33 cards)"); - -int zr_set_buf(struct zoran *zr); -/* - * initialize video front end - */ -static void zr36057_init_vfe(struct zoran *zr) -{ - u32 reg; - - reg = btread(ZR36057_VFESPFR); - reg |= ZR36057_VFESPFR_LITTLE_ENDIAN; - reg &= ~ZR36057_VFESPFR_VCLK_POL; - reg |= ZR36057_VFESPFR_EXT_FL; - reg |= ZR36057_VFESPFR_TOP_FIELD; - btwrite(reg, ZR36057_VFESPFR); - reg = btread(ZR36057_VDCR); - if (pci_pci_problems & PCIPCI_TRITON) - // || zr->revision < 1) // Revision 1 has also Triton support - reg &= ~ZR36057_VDCR_TRITON; - else - reg |= ZR36057_VDCR_TRITON; - btwrite(reg, ZR36057_VDCR); -} - -/* - * General Purpose I/O and Guest bus access - */ - -/* - * This is a bit tricky. When a board lacks a GPIO function, the corresponding - * GPIO bit number in the card_info structure is set to 0. - */ - -void GPIO(struct zoran *zr, int bit, unsigned int value) -{ - u32 reg; - u32 mask; - - /* Make sure the bit number is legal - * A bit number of -1 (lacking) gives a mask of 0, - * making it harmless - */ - mask = (1 << (24 + bit)) & 0xff000000; - reg = btread(ZR36057_GPPGCR1) & ~mask; - if (value) - reg |= mask; - - btwrite(reg, ZR36057_GPPGCR1); - udelay(1); -} - -/* - * Wait til post office is no longer busy - */ - -int post_office_wait(struct zoran *zr) -{ - u32 por; - -// while (((por = btread(ZR36057_POR)) & (ZR36057_POR_PO_PEN | ZR36057_POR_PO_TIME)) == ZR36057_POR_PO_PEN) { - while ((por = btread(ZR36057_POR)) & ZR36057_POR_PO_PEN) { - /* wait for something to happen */ - /* TODO add timeout */ - } - if ((por & ZR36057_POR_PO_TIME) && !zr->card.gws_not_connected) { - /* In LML33/BUZ \GWS line is not connected, so it has always timeout set */ - pci_info(zr->pci_dev, "pop timeout %08x\n", por); - return -1; - } - - return 0; -} - -int post_office_write(struct zoran *zr, unsigned int guest, - unsigned int reg, unsigned int value) -{ - u32 por; - - por = - ZR36057_POR_PO_DIR | ZR36057_POR_PO_TIME | ((guest & 7) << 20) | - ((reg & 7) << 16) | (value & 0xFF); - btwrite(por, ZR36057_POR); - - return post_office_wait(zr); -} - -int post_office_read(struct zoran *zr, unsigned int guest, unsigned int reg) -{ - u32 por; - - por = ZR36057_POR_PO_TIME | ((guest & 7) << 20) | ((reg & 7) << 16); - btwrite(por, ZR36057_POR); - if (post_office_wait(zr) < 0) - return -1; - - return btread(ZR36057_POR) & 0xFF; -} - -/* - * JPEG Codec access - */ - -void jpeg_codec_sleep(struct zoran *zr, int sleep) -{ - GPIO(zr, zr->card.gpio[ZR_GPIO_JPEG_SLEEP], !sleep); - if (!sleep) { - pci_dbg(zr->pci_dev, "%s() - wake GPIO=0x%08x\n", __func__, btread(ZR36057_GPPGCR1)); - udelay(500); - } else { - pci_dbg(zr->pci_dev, "%s() - sleep GPIO=0x%08x\n", __func__, btread(ZR36057_GPPGCR1)); - udelay(2); - } -} - -int jpeg_codec_reset(struct zoran *zr) -{ - /* Take the codec out of sleep */ - jpeg_codec_sleep(zr, 0); - - if (zr->card.gpcs[GPCS_JPEG_RESET] != 0xff) { - post_office_write(zr, zr->card.gpcs[GPCS_JPEG_RESET], 0, - 0); - udelay(2); - } else { - GPIO(zr, zr->card.gpio[ZR_GPIO_JPEG_RESET], 0); - udelay(2); - GPIO(zr, zr->card.gpio[ZR_GPIO_JPEG_RESET], 1); - udelay(2); - } - - return 0; -} - -/* - * Set the registers for the size we have specified. Don't bother - * trying to understand this without the ZR36057 manual in front of - * you [AC]. - */ -static void zr36057_adjust_vfe(struct zoran *zr, enum zoran_codec_mode mode) -{ - u32 reg; - - switch (mode) { - case BUZ_MODE_MOTION_DECOMPRESS: - btand(~ZR36057_VFESPFR_EXT_FL, ZR36057_VFESPFR); - reg = btread(ZR36057_VFEHCR); - if ((reg & (1 << 10)) && zr->card.type != LML33R10) - reg += ((1 << 10) | 1); - - btwrite(reg, ZR36057_VFEHCR); - break; - case BUZ_MODE_MOTION_COMPRESS: - case BUZ_MODE_IDLE: - default: - if ((zr->norm & V4L2_STD_NTSC) || - (zr->card.type == LML33R10 && - (zr->norm & V4L2_STD_PAL))) - btand(~ZR36057_VFESPFR_EXT_FL, ZR36057_VFESPFR); - else - btor(ZR36057_VFESPFR_EXT_FL, ZR36057_VFESPFR); - reg = btread(ZR36057_VFEHCR); - if (!(reg & (1 << 10)) && zr->card.type != LML33R10) - reg -= ((1 << 10) | 1); - - btwrite(reg, ZR36057_VFEHCR); - break; - } -} - -/* - * set geometry - */ - -static void zr36057_set_vfe(struct zoran *zr, int video_width, int video_height, - const struct zoran_format *format) -{ - const struct tvnorm *tvn; - unsigned int h_start, h_end, v_start, v_end; - unsigned int disp_mode; - unsigned int vid_win_wid, vid_win_ht; - unsigned int hcrop1, hcrop2, vcrop1, vcrop2; - unsigned int wa, we, ha, he; - unsigned int X, Y, hor_dcm, ver_dcm; - u32 reg; - - tvn = zr->timing; - - wa = tvn->wa; - ha = tvn->ha; - - pci_dbg(zr->pci_dev, "set_vfe() - width = %d, height = %d\n", video_width, video_height); - - if (video_width < BUZ_MIN_WIDTH || - video_height < BUZ_MIN_HEIGHT || - video_width > wa || video_height > ha) { - pci_err(zr->pci_dev, "set_vfe: w=%d h=%d not valid\n", video_width, video_height); - return; - } - - /**** zr36057 ****/ - - /* horizontal */ - vid_win_wid = video_width; - X = DIV_ROUND_UP(vid_win_wid * 64, tvn->wa); - we = (vid_win_wid * 64) / X; - hor_dcm = 64 - X; - hcrop1 = 2 * ((tvn->wa - we) / 4); - hcrop2 = tvn->wa - we - hcrop1; - h_start = tvn->h_start ? tvn->h_start : 1; - /* (Ronald) Original comment: - * "| 1 Doesn't have any effect, tested on both a DC10 and a DC10+" - * this is false. It inverses chroma values on the LML33R10 (so Cr - * suddenly is shown as Cb and reverse, really cool effect if you - * want to see blue faces, not useful otherwise). So don't use |1. - * However, the DC10 has '0' as h_start, but does need |1, so we - * use a dirty check... - */ - h_end = h_start + tvn->wa - 1; - h_start += hcrop1; - h_end -= hcrop2; - reg = ((h_start & ZR36057_VFEHCR_HMASK) << ZR36057_VFEHCR_H_START) - | ((h_end & ZR36057_VFEHCR_HMASK) << ZR36057_VFEHCR_H_END); - if (zr->card.vfe_pol.hsync_pol) - reg |= ZR36057_VFEHCR_HS_POL; - btwrite(reg, ZR36057_VFEHCR); - - /* Vertical */ - disp_mode = !(video_height > BUZ_MAX_HEIGHT / 2); - vid_win_ht = disp_mode ? video_height : video_height / 2; - Y = DIV_ROUND_UP(vid_win_ht * 64 * 2, tvn->ha); - he = (vid_win_ht * 64) / Y; - ver_dcm = 64 - Y; - vcrop1 = (tvn->ha / 2 - he) / 2; - vcrop2 = tvn->ha / 2 - he - vcrop1; - v_start = tvn->v_start; - v_end = v_start + tvn->ha / 2; // - 1; FIXME SnapShot times out with -1 in 768*576 on the DC10 - LP - v_start += vcrop1; - v_end -= vcrop2; - reg = ((v_start & ZR36057_VFEVCR_VMASK) << ZR36057_VFEVCR_V_START) - | ((v_end & ZR36057_VFEVCR_VMASK) << ZR36057_VFEVCR_V_END); - if (zr->card.vfe_pol.vsync_pol) - reg |= ZR36057_VFEVCR_VS_POL; - btwrite(reg, ZR36057_VFEVCR); - - /* scaler and pixel format */ - reg = 0; - reg |= (hor_dcm << ZR36057_VFESPFR_HOR_DCM); - reg |= (ver_dcm << ZR36057_VFESPFR_VER_DCM); - reg |= (disp_mode << ZR36057_VFESPFR_DISP_MODE); - /* RJ: I don't know, why the following has to be the opposite - * of the corresponding ZR36060 setting, but only this way - * we get the correct colors when uncompressing to the screen */ - //reg |= ZR36057_VFESPFR_VCLK_POL; /**/ - /* RJ: Don't know if that is needed for NTSC also */ - if (!(zr->norm & V4L2_STD_NTSC)) - reg |= ZR36057_VFESPFR_EXT_FL; // NEEDED!!!!!!! Wolfgang - reg |= ZR36057_VFESPFR_TOP_FIELD; - if (hor_dcm >= 48) - reg |= 3 << ZR36057_VFESPFR_H_FILTER; /* 5 tap filter */ - else if (hor_dcm >= 32) - reg |= 2 << ZR36057_VFESPFR_H_FILTER; /* 4 tap filter */ - else if (hor_dcm >= 16) - reg |= 1 << ZR36057_VFESPFR_H_FILTER; /* 3 tap filter */ - - reg |= format->vfespfr; - btwrite(reg, ZR36057_VFESPFR); - - /* display configuration */ - reg = (16 << ZR36057_VDCR_MIN_PIX) - | (vid_win_ht << ZR36057_VDCR_VID_WIN_HT) - | (vid_win_wid << ZR36057_VDCR_VID_WIN_WID); - if (pci_pci_problems & PCIPCI_TRITON) - // || zr->revision < 1) // Revision 1 has also Triton support - reg &= ~ZR36057_VDCR_TRITON; - else - reg |= ZR36057_VDCR_TRITON; - btwrite(reg, ZR36057_VDCR); - - zr36057_adjust_vfe(zr, zr->codec_mode); -} - -/* Enable/Disable uncompressed memory grabbing of the 36057 */ -void zr36057_set_memgrab(struct zoran *zr, int mode) -{ - if (mode) { - /* We only check SnapShot and not FrameGrab here. SnapShot==1 - * means a capture is already in progress, but FrameGrab==1 - * doesn't necessary mean that. It's more correct to say a 1 - * to 0 transition indicates a capture completed. If a - * capture is pending when capturing is tuned off, FrameGrab - * will be stuck at 1 until capturing is turned back on. - */ - if (btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_SNAP_SHOT) - pci_warn(zr->pci_dev, "zr36057_set_memgrab(1) with SnapShot on!?\n"); - - /* switch on VSync interrupts */ - btwrite(IRQ_MASK, ZR36057_ISR); // Clear Interrupts - btor(zr->card.vsync_int, ZR36057_ICR); // SW - - /* enable SnapShot */ - btor(ZR36057_VSSFGR_SNAP_SHOT, ZR36057_VSSFGR); - - /* Set zr36057 video front end and enable video */ - zr36057_set_vfe(zr, zr->v4l_settings.width, - zr->v4l_settings.height, - zr->v4l_settings.format); - } else { - /* switch off VSync interrupts */ - btand(~zr->card.vsync_int, ZR36057_ICR); // SW - - /* re-enable grabbing to screen if it was running */ - btand(~ZR36057_VDCR_VID_EN, ZR36057_VDCR); - btand(~ZR36057_VSSFGR_SNAP_SHOT, ZR36057_VSSFGR); - } -} - -/***************************************************************************** - * * - * Set up the Buz-specific MJPEG part * - * * - *****************************************************************************/ - -static inline void set_frame(struct zoran *zr, int val) -{ - GPIO(zr, zr->card.gpio[ZR_GPIO_JPEG_FRAME], val); -} - -static void set_videobus_dir(struct zoran *zr, int val) -{ - switch (zr->card.type) { - case LML33: - case LML33R10: - if (!lml33dpath) - GPIO(zr, 5, val); - else - GPIO(zr, 5, 1); - break; - default: - GPIO(zr, zr->card.gpio[ZR_GPIO_VID_DIR], - zr->card.gpio_pol[ZR_GPIO_VID_DIR] ? !val : val); - break; - } -} - -static void init_jpeg_queue(struct zoran *zr) -{ - int i; - - /* re-initialize DMA ring stuff */ - zr->jpg_que_head = 0; - zr->jpg_dma_head = 0; - zr->jpg_dma_tail = 0; - zr->jpg_que_tail = 0; - zr->jpg_seq_num = 0; - zr->jpeg_error = 0; - zr->num_errors = 0; - zr->jpg_err_seq = 0; - zr->jpg_err_shift = 0; - zr->jpg_queued_num = 0; - for (i = 0; i < BUZ_NUM_STAT_COM; i++) - zr->stat_com[i] = cpu_to_le32(1); /* mark as unavailable to zr36057 */ -} - -static void zr36057_set_jpg(struct zoran *zr, enum zoran_codec_mode mode) -{ - const struct tvnorm *tvn; - u32 reg; - - tvn = zr->timing; - - /* assert P_Reset, disable code transfer, deassert Active */ - btwrite(0, ZR36057_JPC); - - /* MJPEG compression mode */ - switch (mode) { - case BUZ_MODE_MOTION_COMPRESS: - default: - reg = ZR36057_JMC_MJPG_CMP_MODE; - break; - - case BUZ_MODE_MOTION_DECOMPRESS: - reg = ZR36057_JMC_MJPG_EXP_MODE; - reg |= ZR36057_JMC_SYNC_MSTR; - /* RJ: The following is experimental - improves the output to screen */ - //if(zr->jpg_settings.VFIFO_FB) reg |= ZR36057_JMC_VFIFO_FB; // No, it doesn't. SM - break; - - case BUZ_MODE_STILL_COMPRESS: - reg = ZR36057_JMC_JPG_CMP_MODE; - break; - - case BUZ_MODE_STILL_DECOMPRESS: - reg = ZR36057_JMC_JPG_EXP_MODE; - break; - } - reg |= ZR36057_JMC_JPG; - if (zr->jpg_settings.field_per_buff == 1) - reg |= ZR36057_JMC_FLD_PER_BUFF; - btwrite(reg, ZR36057_JMC); - - /* vertical */ - btor(ZR36057_VFEVCR_VS_POL, ZR36057_VFEVCR); - reg = (6 << ZR36057_VSP_VSYNC_SIZE) | - (tvn->ht << ZR36057_VSP_FRM_TOT); - btwrite(reg, ZR36057_VSP); - reg = ((zr->jpg_settings.img_y + tvn->v_start) << ZR36057_FVAP_NAY) | - (zr->jpg_settings.img_height << ZR36057_FVAP_PAY); - btwrite(reg, ZR36057_FVAP); - - /* horizontal */ - if (zr->card.vfe_pol.hsync_pol) - btor(ZR36057_VFEHCR_HS_POL, ZR36057_VFEHCR); - else - btand(~ZR36057_VFEHCR_HS_POL, ZR36057_VFEHCR); - reg = ((tvn->h_sync_start) << ZR36057_HSP_HSYNC_START) | - (tvn->wt << ZR36057_HSP_LINE_TOT); - btwrite(reg, ZR36057_HSP); - reg = ((zr->jpg_settings.img_x + - tvn->h_start + 4) << ZR36057_FHAP_NAX) | - (zr->jpg_settings.img_width << ZR36057_FHAP_PAX); - btwrite(reg, ZR36057_FHAP); - - /* field process parameters */ - if (zr->jpg_settings.odd_even) - reg = ZR36057_FPP_ODD_EVEN; - else - reg = 0; - - btwrite(reg, ZR36057_FPP); - - /* Set proper VCLK Polarity, else colors will be wrong during playback */ - //btor(ZR36057_VFESPFR_VCLK_POL, ZR36057_VFESPFR); - - /* code base address */ - btwrite(zr->p_sc, ZR36057_JCBA); - - /* FIFO threshold (FIFO is 160. double words) */ - /* NOTE: decimal values here */ - switch (mode) { - case BUZ_MODE_STILL_COMPRESS: - case BUZ_MODE_MOTION_COMPRESS: - if (zr->card.type != BUZ) - reg = 140; - else - reg = 60; - break; - - case BUZ_MODE_STILL_DECOMPRESS: - case BUZ_MODE_MOTION_DECOMPRESS: - reg = 20; - break; - - default: - reg = 80; - break; - } - btwrite(reg, ZR36057_JCFT); - zr36057_adjust_vfe(zr, mode); -} - -void clear_interrupt_counters(struct zoran *zr) -{ - zr->intr_counter_GIRQ1 = 0; - zr->intr_counter_GIRQ0 = 0; - zr->intr_counter_cod_rep_irq = 0; - zr->intr_counter_jpeg_rep_irq = 0; - zr->field_counter = 0; - zr->irq1_in = 0; - zr->irq1_out = 0; - zr->jpeg_in = 0; - zr->jpeg_out = 0; - zr->JPEG_0 = 0; - zr->JPEG_1 = 0; - zr->end_event_missed = 0; - zr->jpeg_missed = 0; - zr->jpeg_max_missed = 0; - zr->jpeg_min_missed = 0x7fffffff; -} - -static u32 count_reset_interrupt(struct zoran *zr) -{ - u32 isr; - - isr = btread(ZR36057_ISR) & 0x78000000; - if (isr) { - if (isr & ZR36057_ISR_GIRQ1) { - btwrite(ZR36057_ISR_GIRQ1, ZR36057_ISR); - zr->intr_counter_GIRQ1++; - } - if (isr & ZR36057_ISR_GIRQ0) { - btwrite(ZR36057_ISR_GIRQ0, ZR36057_ISR); - zr->intr_counter_GIRQ0++; - } - if (isr & ZR36057_ISR_COD_REP_IRQ) { - btwrite(ZR36057_ISR_COD_REP_IRQ, ZR36057_ISR); - zr->intr_counter_cod_rep_irq++; - } - if (isr & ZR36057_ISR_JPEG_REP_IRQ) { - btwrite(ZR36057_ISR_JPEG_REP_IRQ, ZR36057_ISR); - zr->intr_counter_jpeg_rep_irq++; - } - } - return isr; -} - -void jpeg_start(struct zoran *zr) -{ - int reg; - - zr->frame_num = 0; - - /* deassert P_reset, disable code transfer, deassert Active */ - btwrite(ZR36057_JPC_P_RESET, ZR36057_JPC); - /* stop flushing the internal code buffer */ - btand(~ZR36057_MCTCR_C_FLUSH, ZR36057_MCTCR); - /* enable code transfer */ - btor(ZR36057_JPC_COD_TRNS_EN, ZR36057_JPC); - - /* clear IRQs */ - btwrite(IRQ_MASK, ZR36057_ISR); - /* enable the JPEG IRQs */ - btwrite(zr->card.jpeg_int | ZR36057_ICR_JPEG_REP_IRQ | ZR36057_ICR_INT_PIN_EN, - ZR36057_ICR); - - set_frame(zr, 0); // \FRAME - - /* set the JPEG codec guest ID */ - reg = (zr->card.gpcs[1] << ZR36057_JCGI_JPE_GUEST_ID) | - (0 << ZR36057_JCGI_JPE_GUEST_REG); - btwrite(reg, ZR36057_JCGI); - - if (zr->card.video_vfe == CODEC_TYPE_ZR36016 && - zr->card.video_codec == CODEC_TYPE_ZR36050) { - /* Enable processing on the ZR36016 */ - if (zr->vfe) - zr36016_write(zr->vfe, 0, 1); - - /* load the address of the GO register in the ZR36050 latch */ - post_office_write(zr, 0, 0, 0); - } - - /* assert Active */ - btor(ZR36057_JPC_ACTIVE, ZR36057_JPC); - - /* enable the Go generation */ - btor(ZR36057_JMC_GO_EN, ZR36057_JMC); - udelay(30); - - set_frame(zr, 1); // /FRAME - - pci_dbg(zr->pci_dev, "jpeg_start\n"); -} - -void zr36057_enable_jpg(struct zoran *zr, enum zoran_codec_mode mode) -{ - struct vfe_settings cap; - int field_size = zr->buffer_size / zr->jpg_settings.field_per_buff; - - zr->codec_mode = mode; - - cap.x = zr->jpg_settings.img_x; - cap.y = zr->jpg_settings.img_y; - cap.width = zr->jpg_settings.img_width; - cap.height = zr->jpg_settings.img_height; - cap.decimation = - zr->jpg_settings.hor_dcm | (zr->jpg_settings.ver_dcm << 8); - cap.quality = zr->jpg_settings.jpg_comp.quality; - - switch (mode) { - case BUZ_MODE_MOTION_COMPRESS: { - struct jpeg_app_marker app; - struct jpeg_com_marker com; - - /* In motion compress mode, the decoder output must be enabled, and - * the video bus direction set to input. - */ - set_videobus_dir(zr, 0); - decoder_call(zr, video, s_stream, 1); - encoder_call(zr, video, s_routing, 0, 0, 0); - - /* Take the JPEG codec and the VFE out of sleep */ - jpeg_codec_sleep(zr, 0); - - /* set JPEG app/com marker */ - app.appn = zr->jpg_settings.jpg_comp.APPn; - app.len = zr->jpg_settings.jpg_comp.APP_len; - memcpy(app.data, zr->jpg_settings.jpg_comp.APP_data, 60); - zr->codec->control(zr->codec, CODEC_S_JPEG_APP_DATA, - sizeof(struct jpeg_app_marker), &app); - - com.len = zr->jpg_settings.jpg_comp.COM_len; - memcpy(com.data, zr->jpg_settings.jpg_comp.COM_data, 60); - zr->codec->control(zr->codec, CODEC_S_JPEG_COM_DATA, - sizeof(struct jpeg_com_marker), &com); - - /* Setup the JPEG codec */ - zr->codec->control(zr->codec, CODEC_S_JPEG_TDS_BYTE, - sizeof(int), &field_size); - zr->codec->set_video(zr->codec, zr->timing, &cap, - &zr->card.vfe_pol); - zr->codec->set_mode(zr->codec, CODEC_DO_COMPRESSION); - - /* Setup the VFE */ - if (zr->vfe) { - zr->vfe->control(zr->vfe, CODEC_S_JPEG_TDS_BYTE, - sizeof(int), &field_size); - zr->vfe->set_video(zr->vfe, zr->timing, &cap, - &zr->card.vfe_pol); - zr->vfe->set_mode(zr->vfe, CODEC_DO_COMPRESSION); - } - - init_jpeg_queue(zr); - zr36057_set_jpg(zr, mode); // \P_Reset, ... Video param, FIFO - - clear_interrupt_counters(zr); - pci_dbg(zr->pci_dev, "enable_jpg(MOTION_COMPRESS)\n"); - break; - } - - case BUZ_MODE_MOTION_DECOMPRESS: - /* In motion decompression mode, the decoder output must be disabled, and - * the video bus direction set to output. - */ - decoder_call(zr, video, s_stream, 0); - set_videobus_dir(zr, 1); - encoder_call(zr, video, s_routing, 1, 0, 0); - - /* Take the JPEG codec and the VFE out of sleep */ - jpeg_codec_sleep(zr, 0); - /* Setup the VFE */ - if (zr->vfe) { - zr->vfe->set_video(zr->vfe, zr->timing, &cap, - &zr->card.vfe_pol); - zr->vfe->set_mode(zr->vfe, CODEC_DO_EXPANSION); - } - /* Setup the JPEG codec */ - zr->codec->set_video(zr->codec, zr->timing, &cap, - &zr->card.vfe_pol); - zr->codec->set_mode(zr->codec, CODEC_DO_EXPANSION); - - init_jpeg_queue(zr); - zr36057_set_jpg(zr, mode); // \P_Reset, ... Video param, FIFO - - clear_interrupt_counters(zr); - pci_dbg(zr->pci_dev, "enable_jpg(MOTION_DECOMPRESS)\n"); - break; - - case BUZ_MODE_IDLE: - default: - /* shut down processing */ - btand(~(zr->card.jpeg_int | ZR36057_ICR_JPEG_REP_IRQ), - ZR36057_ICR); - btwrite(zr->card.jpeg_int | ZR36057_ICR_JPEG_REP_IRQ, - ZR36057_ISR); - btand(~ZR36057_JMC_GO_EN, ZR36057_JMC); // \Go_en - - msleep(50); - - set_videobus_dir(zr, 0); - set_frame(zr, 1); // /FRAME - btor(ZR36057_MCTCR_C_FLUSH, ZR36057_MCTCR); // /CFlush - btwrite(0, ZR36057_JPC); // \P_Reset,\CodTrnsEn,\Active - btand(~ZR36057_JMC_VFIFO_FB, ZR36057_JMC); - btand(~ZR36057_JMC_SYNC_MSTR, ZR36057_JMC); - jpeg_codec_reset(zr); - jpeg_codec_sleep(zr, 1); - zr36057_adjust_vfe(zr, mode); - - decoder_call(zr, video, s_stream, 1); - encoder_call(zr, video, s_routing, 0, 0, 0); - - pci_dbg(zr->pci_dev, "enable_jpg(IDLE)\n"); - break; - } -} - -/* when this is called the spinlock must be held */ -void zoran_feed_stat_com(struct zoran *zr) -{ - /* move frames from pending queue to DMA */ - - int i, max_stat_com; - struct zr_buffer *buf; - struct vb2_v4l2_buffer *vbuf; - dma_addr_t phys_addr = 0; - unsigned long flags; - unsigned long payload; - - max_stat_com = - (zr->jpg_settings.tmp_dcm == - 1) ? BUZ_NUM_STAT_COM : (BUZ_NUM_STAT_COM >> 1); - - spin_lock_irqsave(&zr->queued_bufs_lock, flags); - while ((zr->jpg_dma_head - zr->jpg_dma_tail) < max_stat_com) { - buf = list_first_entry_or_null(&zr->queued_bufs, struct zr_buffer, queue); - if (!buf) { - pci_err(zr->pci_dev, "No buffer available to queue\n"); - spin_unlock_irqrestore(&zr->queued_bufs_lock, flags); - return; - } - list_del(&buf->queue); - zr->buf_in_reserve--; - vbuf = &buf->vbuf; - vbuf->vb2_buf.state = VB2_BUF_STATE_ACTIVE; - phys_addr = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); - payload = vb2_get_plane_payload(&vbuf->vb2_buf, 0); - if (payload == 0) - payload = zr->buffer_size; - if (zr->jpg_settings.tmp_dcm == 1) { - /* fill 1 stat_com entry */ - i = (zr->jpg_dma_head - - zr->jpg_err_shift) & BUZ_MASK_STAT_COM; - if (!(zr->stat_com[i] & cpu_to_le32(1))) - break; - zr->stat_comb[i * 2] = cpu_to_le32(phys_addr); - zr->stat_comb[i * 2 + 1] = cpu_to_le32((payload >> 1) | 1); - zr->inuse[i] = buf; - zr->stat_com[i] = cpu_to_le32(zr->p_scb + i * 2 * 4); - } else { - /* fill 2 stat_com entries */ - i = ((zr->jpg_dma_head - - zr->jpg_err_shift) & 1) * 2; - if (!(zr->stat_com[i] & cpu_to_le32(1))) - break; - zr->stat_com[i] = cpu_to_le32(zr->p_scb + i * 2 * 4); - zr->stat_com[i + 1] = cpu_to_le32(zr->p_scb + i * 2 * 4); - - zr->stat_comb[i * 2] = cpu_to_le32(phys_addr); - zr->stat_comb[i * 2 + 1] = cpu_to_le32((payload >> 1) | 1); - - zr->inuse[i] = buf; - zr->inuse[i + 1] = NULL; - } - zr->jpg_dma_head++; - } - spin_unlock_irqrestore(&zr->queued_bufs_lock, flags); - if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) - zr->jpg_queued_num++; -} - -/* when this is called the spinlock must be held */ -static void zoran_reap_stat_com(struct zoran *zr) -{ - /* move frames from DMA queue to done queue */ - - int i; - u32 stat_com; - unsigned int seq; - unsigned int dif; - unsigned long flags; - struct zr_buffer *buf; - unsigned int size = 0; - u32 fcnt; - - /* In motion decompress we don't have a hardware frame counter, - * we just count the interrupts here */ - - if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) - zr->jpg_seq_num++; - - spin_lock_irqsave(&zr->queued_bufs_lock, flags); - while (zr->jpg_dma_tail < zr->jpg_dma_head) { - if (zr->jpg_settings.tmp_dcm == 1) - i = (zr->jpg_dma_tail - zr->jpg_err_shift) & BUZ_MASK_STAT_COM; - else - i = ((zr->jpg_dma_tail - zr->jpg_err_shift) & 1) * 2; - - stat_com = le32_to_cpu(zr->stat_com[i]); - if ((stat_com & 1) == 0) { - spin_unlock_irqrestore(&zr->queued_bufs_lock, flags); - return; - } - - fcnt = (stat_com & GENMASK(31, 24)) >> 24; - size = (stat_com & GENMASK(22, 1)) >> 1; - - buf = zr->inuse[i]; - if (!buf) { - spin_unlock_irqrestore(&zr->queued_bufs_lock, flags); - pci_err(zr->pci_dev, "No buffer at slot %d\n", i); - return; - } - buf->vbuf.vb2_buf.timestamp = ktime_get_ns(); - - if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) { - vb2_set_plane_payload(&buf->vbuf.vb2_buf, 0, size); - - /* update sequence number with the help of the counter in stat_com */ - seq = (fcnt + zr->jpg_err_seq) & 0xff; - dif = (seq - zr->jpg_seq_num) & 0xff; - zr->jpg_seq_num += dif; - } - buf->vbuf.sequence = zr->jpg_settings.tmp_dcm == - 2 ? (zr->jpg_seq_num >> 1) : zr->jpg_seq_num; - zr->inuse[i] = NULL; - if (zr->jpg_settings.tmp_dcm != 1) - buf->vbuf.field = zr->jpg_settings.odd_even ? - V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM; - else - buf->vbuf.field = zr->jpg_settings.odd_even ? - V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT; - vb2_buffer_done(&buf->vbuf.vb2_buf, VB2_BUF_STATE_DONE); - - zr->jpg_dma_tail++; - } - spin_unlock_irqrestore(&zr->queued_bufs_lock, flags); -} - -irqreturn_t zoran_irq(int irq, void *dev_id) -{ - struct zoran *zr = dev_id; - u32 stat, astat; - - stat = count_reset_interrupt(zr); - astat = stat & IRQ_MASK; - if (astat & zr->card.vsync_int) { - if (zr->running == ZORAN_MAP_MODE_RAW) { - if ((btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_SNAP_SHOT) == 0) - pci_warn(zr->pci_dev, "BuzIRQ with SnapShot off ???\n"); - if ((btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_FRAME_GRAB) == 0) - zr_set_buf(zr); - return IRQ_HANDLED; - } - if (astat & ZR36057_ISR_JPEG_REP_IRQ) { - if (zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS && - zr->codec_mode != BUZ_MODE_MOTION_COMPRESS) { - pci_err(zr->pci_dev, "JPG IRQ when not in good mode\n"); - return IRQ_HANDLED; - } - zr->frame_num++; - zoran_reap_stat_com(zr); - zoran_feed_stat_com(zr); - return IRQ_HANDLED; - } - /* unused interrupts */ - } - zr->ghost_int++; - return IRQ_HANDLED; -} - -void zoran_set_pci_master(struct zoran *zr, int set_master) -{ - if (set_master) { - pci_set_master(zr->pci_dev); - } else { - u16 command; - - pci_read_config_word(zr->pci_dev, PCI_COMMAND, &command); - command &= ~PCI_COMMAND_MASTER; - pci_write_config_word(zr->pci_dev, PCI_COMMAND, command); - } -} - -void zoran_init_hardware(struct zoran *zr) -{ - /* Enable bus-mastering */ - zoran_set_pci_master(zr, 1); - - /* Initialize the board */ - if (zr->card.init) - zr->card.init(zr); - - decoder_call(zr, core, init, 0); - decoder_call(zr, video, s_std, zr->norm); - decoder_call(zr, video, s_routing, - zr->card.input[zr->input].muxsel, 0, 0); - - encoder_call(zr, core, init, 0); - encoder_call(zr, video, s_std_output, zr->norm); - encoder_call(zr, video, s_routing, 0, 0, 0); - - /* toggle JPEG codec sleep to sync PLL */ - jpeg_codec_sleep(zr, 1); - jpeg_codec_sleep(zr, 0); - - /* - * set individual interrupt enables (without GIRQ1) - * but don't global enable until zoran_open() - */ - zr36057_init_vfe(zr); - - zr36057_enable_jpg(zr, BUZ_MODE_IDLE); - - btwrite(IRQ_MASK, ZR36057_ISR); // Clears interrupts -} - -void zr36057_restart(struct zoran *zr) -{ - btwrite(0, ZR36057_SPGPPCR); - udelay(1000); - btor(ZR36057_SPGPPCR_SOFT_RESET, ZR36057_SPGPPCR); - udelay(1000); - - /* assert P_Reset */ - btwrite(0, ZR36057_JPC); - /* set up GPIO direction - all output */ - btwrite(ZR36057_SPGPPCR_SOFT_RESET | 0, ZR36057_SPGPPCR); - - /* set up GPIO pins and guest bus timing */ - btwrite((0x81 << 24) | 0x8888, ZR36057_GPPGCR1); -} - diff --git a/drivers/staging/media/zoran/zoran_device.h b/drivers/staging/media/zoran/zoran_device.h deleted file mode 100644 index 322b04c55d41..000000000000 --- a/drivers/staging/media/zoran/zoran_device.h +++ /dev/null @@ -1,60 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Zoran zr36057/zr36067 PCI controller driver, for the - * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux - * Media Labs LML33/LML33R10. - * - * This part handles card-specific data and detection - * - * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx> - */ - -#ifndef __ZORAN_DEVICE_H__ -#define __ZORAN_DEVICE_H__ - -/* general purpose I/O */ -extern void GPIO(struct zoran *zr, int bit, unsigned int value); - -/* codec (or actually: guest bus) access */ -extern int post_office_wait(struct zoran *zr); -extern int post_office_write(struct zoran *zr, unsigned int guest, unsigned int reg, unsigned int value); -extern int post_office_read(struct zoran *zr, unsigned int guest, unsigned int reg); - -extern void jpeg_codec_sleep(struct zoran *zr, int sleep); -extern int jpeg_codec_reset(struct zoran *zr); - -/* zr360x7 access to raw capture */ -extern void zr36057_overlay(struct zoran *zr, int on); -extern void write_overlay_mask(struct zoran_fh *fh, struct v4l2_clip *vp, int count); -extern void zr36057_set_memgrab(struct zoran *zr, int mode); -extern int wait_grab_pending(struct zoran *zr); - -/* interrupts */ -extern void print_interrupts(struct zoran *zr); -extern void clear_interrupt_counters(struct zoran *zr); -extern irqreturn_t zoran_irq(int irq, void *dev_id); - -/* JPEG codec access */ -extern void jpeg_start(struct zoran *zr); -extern void zr36057_enable_jpg(struct zoran *zr, - enum zoran_codec_mode mode); -extern void zoran_feed_stat_com(struct zoran *zr); - -/* general */ -extern void zoran_set_pci_master(struct zoran *zr, int set_master); -extern void zoran_init_hardware(struct zoran *zr); -extern void zr36057_restart(struct zoran *zr); - -extern const struct zoran_format zoran_formats[]; - -extern int v4l_bufsize; -extern int jpg_bufsize; -extern int pass_through; - -/* i2c */ -#define decoder_call(zr, o, f, args...) \ - v4l2_subdev_call(zr->decoder, o, f, ##args) -#define encoder_call(zr, o, f, args...) \ - v4l2_subdev_call(zr->encoder, o, f, ##args) - -#endif /* __ZORAN_DEVICE_H__ */ diff --git a/drivers/staging/media/zoran/zoran_driver.c b/drivers/staging/media/zoran/zoran_driver.c deleted file mode 100644 index 4304b7e21709..000000000000 --- a/drivers/staging/media/zoran/zoran_driver.c +++ /dev/null @@ -1,1035 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Zoran zr36057/zr36067 PCI controller driver, for the - * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux - * Media Labs LML33/LML33R10. - * - * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx> - * - * Changes for BUZ by Wolfgang Scherr <scherr@net4you.net> - * - * Changes for DC10/DC30 by Laurent Pinchart <laurent.pinchart@skynet.be> - * - * Changes for LML33R10 by Maxim Yevtyushkin <max@linuxmedialabs.com> - * - * Changes for videodev2/v4l2 by Ronald Bultje <rbultje@ronald.bitfreak.net> - * - * Based on - * - * Miro DC10 driver - * Copyright (C) 1999 Wolfgang Scherr <scherr@net4you.net> - * - * Iomega Buz driver version 1.0 - * Copyright (C) 1999 Rainer Johanni <Rainer@Johanni.de> - * - * buz.0.0.3 - * Copyright (C) 1998 Dave Perks <dperks@ibm.net> - * - * bttv - Bt848 frame grabber driver - * Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) - * & Marcus Metzler (mocm@thp.uni-koeln.de) - */ - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/slab.h> -#include <linux/pci.h> -#include <linux/wait.h> - -#include <linux/interrupt.h> -#include <linux/i2c.h> -#include <linux/i2c-algo-bit.h> - -#include <linux/spinlock.h> - -#include <linux/videodev2.h> -#include <media/v4l2-common.h> -#include <media/v4l2-ioctl.h> -#include <media/v4l2-event.h> -#include "videocodec.h" - -#include <linux/io.h> -#include <linux/uaccess.h> - -#include <linux/mutex.h> -#include "zoran.h" -#include "zoran_device.h" -#include "zoran_card.h" - -const struct zoran_format zoran_formats[] = { - { - .name = "15-bit RGB LE", - .fourcc = V4L2_PIX_FMT_RGB555, - .colorspace = V4L2_COLORSPACE_SRGB, - .depth = 15, - .flags = ZORAN_FORMAT_CAPTURE, - .vfespfr = ZR36057_VFESPFR_RGB555 | ZR36057_VFESPFR_ERR_DIF | - ZR36057_VFESPFR_LITTLE_ENDIAN, - }, { - .name = "15-bit RGB BE", - .fourcc = V4L2_PIX_FMT_RGB555X, - .colorspace = V4L2_COLORSPACE_SRGB, - .depth = 15, - .flags = ZORAN_FORMAT_CAPTURE, - .vfespfr = ZR36057_VFESPFR_RGB555 | ZR36057_VFESPFR_ERR_DIF, - }, { - .name = "16-bit RGB LE", - .fourcc = V4L2_PIX_FMT_RGB565, - .colorspace = V4L2_COLORSPACE_SRGB, - .depth = 16, - .flags = ZORAN_FORMAT_CAPTURE, - .vfespfr = ZR36057_VFESPFR_RGB565 | ZR36057_VFESPFR_ERR_DIF | - ZR36057_VFESPFR_LITTLE_ENDIAN, - }, { - .name = "16-bit RGB BE", - .fourcc = V4L2_PIX_FMT_RGB565X, - .colorspace = V4L2_COLORSPACE_SRGB, - .depth = 16, - .flags = ZORAN_FORMAT_CAPTURE, - .vfespfr = ZR36057_VFESPFR_RGB565 | ZR36057_VFESPFR_ERR_DIF, - }, { - .name = "24-bit RGB", - .fourcc = V4L2_PIX_FMT_BGR24, - .colorspace = V4L2_COLORSPACE_SRGB, - .depth = 24, - .flags = ZORAN_FORMAT_CAPTURE, - .vfespfr = ZR36057_VFESPFR_RGB888 | ZR36057_VFESPFR_PACK24, - }, { - .name = "32-bit RGB LE", - .fourcc = V4L2_PIX_FMT_BGR32, - .colorspace = V4L2_COLORSPACE_SRGB, - .depth = 32, - .flags = ZORAN_FORMAT_CAPTURE, - .vfespfr = ZR36057_VFESPFR_RGB888 | ZR36057_VFESPFR_LITTLE_ENDIAN, - }, { - .name = "32-bit RGB BE", - .fourcc = V4L2_PIX_FMT_RGB32, - .colorspace = V4L2_COLORSPACE_SRGB, - .depth = 32, - .flags = ZORAN_FORMAT_CAPTURE, - .vfespfr = ZR36057_VFESPFR_RGB888, - }, { - .name = "4:2:2, packed, YUYV", - .fourcc = V4L2_PIX_FMT_YUYV, - .colorspace = V4L2_COLORSPACE_SMPTE170M, - .depth = 16, - .flags = ZORAN_FORMAT_CAPTURE, - .vfespfr = ZR36057_VFESPFR_YUV422, - }, { - .name = "4:2:2, packed, UYVY", - .fourcc = V4L2_PIX_FMT_UYVY, - .colorspace = V4L2_COLORSPACE_SMPTE170M, - .depth = 16, - .flags = ZORAN_FORMAT_CAPTURE, - .vfespfr = ZR36057_VFESPFR_YUV422 | ZR36057_VFESPFR_LITTLE_ENDIAN, - }, { - .name = "Hardware-encoded Motion-JPEG", - .fourcc = V4L2_PIX_FMT_MJPEG, - .colorspace = V4L2_COLORSPACE_SMPTE170M, - .depth = 0, - .flags = ZORAN_FORMAT_CAPTURE | - ZORAN_FORMAT_PLAYBACK | - ZORAN_FORMAT_COMPRESSED, - } -}; - -#define NUM_FORMATS ARRAY_SIZE(zoran_formats) - - /* - * small helper function for calculating buffersizes for v4l2 - * we calculate the nearest higher power-of-two, which - * will be the recommended buffersize - */ -static __u32 zoran_v4l2_calc_bufsize(struct zoran_jpg_settings *settings) -{ - __u8 div = settings->ver_dcm * settings->hor_dcm * settings->tmp_dcm; - __u32 num = (1024 * 512) / (div); - __u32 result = 2; - - num--; - while (num) { - num >>= 1; - result <<= 1; - } - - if (result < 8192) - return 8192; - - return result; -} - -/* - * V4L Buffer grabbing - */ -static int zoran_v4l_set_format(struct zoran *zr, int width, int height, - const struct zoran_format *format) -{ - int bpp; - - /* Check size and format of the grab wanted */ - - if (height < BUZ_MIN_HEIGHT || width < BUZ_MIN_WIDTH || - height > BUZ_MAX_HEIGHT || width > BUZ_MAX_WIDTH) { - pci_dbg(zr->pci_dev, "%s - wrong frame size (%dx%d)\n", __func__, width, height); - return -EINVAL; - } - - bpp = (format->depth + 7) / 8; - - zr->buffer_size = height * width * bpp; - - /* Check against available buffer size */ - if (height * width * bpp > zr->buffer_size) { - pci_dbg(zr->pci_dev, "%s - video buffer size (%d kB) is too small\n", - __func__, zr->buffer_size >> 10); - return -EINVAL; - } - - /* The video front end needs 4-byte alinged line sizes */ - - if ((bpp == 2 && (width & 1)) || (bpp == 3 && (width & 3))) { - pci_dbg(zr->pci_dev, "%s - wrong frame alignment\n", __func__); - return -EINVAL; - } - - zr->v4l_settings.width = width; - zr->v4l_settings.height = height; - zr->v4l_settings.format = format; - zr->v4l_settings.bytesperline = bpp * zr->v4l_settings.width; - - return 0; -} - -static int zoran_set_norm(struct zoran *zr, v4l2_std_id norm) -{ - - if (!(norm & zr->card.norms)) { - pci_dbg(zr->pci_dev, "%s - unsupported norm %llx\n", __func__, norm); - return -EINVAL; - } - - if (norm & V4L2_STD_SECAM) - zr->timing = zr->card.tvn[ZR_NORM_SECAM]; - else if (norm & V4L2_STD_NTSC) - zr->timing = zr->card.tvn[ZR_NORM_NTSC]; - else - zr->timing = zr->card.tvn[ZR_NORM_PAL]; - - decoder_call(zr, video, s_std, norm); - encoder_call(zr, video, s_std_output, norm); - - /* Make sure the changes come into effect */ - zr->norm = norm; - - return 0; -} - -static int zoran_set_input(struct zoran *zr, int input) -{ - if (input == zr->input) - return 0; - - if (input < 0 || input >= zr->card.inputs) { - pci_dbg(zr->pci_dev, "%s - unsupported input %d\n", __func__, input); - return -EINVAL; - } - - zr->input = input; - - decoder_call(zr, video, s_routing, zr->card.input[input].muxsel, 0, 0); - - return 0; -} - -/* - * ioctl routine - */ - -static int zoran_querycap(struct file *file, void *__fh, struct v4l2_capability *cap) -{ - struct zoran *zr = video_drvdata(file); - - strscpy(cap->card, ZR_DEVNAME(zr), sizeof(cap->card)); - strscpy(cap->driver, "zoran", sizeof(cap->driver)); - snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s", pci_name(zr->pci_dev)); - return 0; -} - -static int zoran_enum_fmt(struct zoran *zr, struct v4l2_fmtdesc *fmt, int flag) -{ - unsigned int num, i; - - if (fmt->index >= ARRAY_SIZE(zoran_formats)) - return -EINVAL; - if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - for (num = i = 0; i < NUM_FORMATS; i++) { - if (zoran_formats[i].flags & flag && num++ == fmt->index) { - strscpy(fmt->description, zoran_formats[i].name, - sizeof(fmt->description)); - /* fmt struct pre-zeroed, so adding '\0' not needed */ - fmt->pixelformat = zoran_formats[i].fourcc; - if (zoran_formats[i].flags & ZORAN_FORMAT_COMPRESSED) - fmt->flags |= V4L2_FMT_FLAG_COMPRESSED; - return 0; - } - } - return -EINVAL; -} - -static int zoran_enum_fmt_vid_cap(struct file *file, void *__fh, - struct v4l2_fmtdesc *f) -{ - struct zoran *zr = video_drvdata(file); - - return zoran_enum_fmt(zr, f, ZORAN_FORMAT_CAPTURE); -} - -#if 0 -/* TODO: output does not work yet */ -static int zoran_enum_fmt_vid_out(struct file *file, void *__fh, - struct v4l2_fmtdesc *f) -{ - struct zoran *zr = video_drvdata(file); - - return zoran_enum_fmt(zr, f, ZORAN_FORMAT_PLAYBACK); -} -#endif - -static int zoran_g_fmt_vid_out(struct file *file, void *__fh, - struct v4l2_format *fmt) -{ - struct zoran *zr = video_drvdata(file); - - fmt->fmt.pix.width = zr->jpg_settings.img_width / zr->jpg_settings.hor_dcm; - fmt->fmt.pix.height = zr->jpg_settings.img_height * 2 / - (zr->jpg_settings.ver_dcm * zr->jpg_settings.tmp_dcm); - fmt->fmt.pix.sizeimage = zr->buffer_size; - fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; - if (zr->jpg_settings.tmp_dcm == 1) - fmt->fmt.pix.field = (zr->jpg_settings.odd_even ? - V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT); - else - fmt->fmt.pix.field = (zr->jpg_settings.odd_even ? - V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM); - fmt->fmt.pix.bytesperline = 0; - fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - - return 0; -} - -static int zoran_g_fmt_vid_cap(struct file *file, void *__fh, - struct v4l2_format *fmt) -{ - struct zoran *zr = video_drvdata(file); - - if (zr->map_mode != ZORAN_MAP_MODE_RAW) - return zoran_g_fmt_vid_out(file, __fh, fmt); - fmt->fmt.pix.width = zr->v4l_settings.width; - fmt->fmt.pix.height = zr->v4l_settings.height; - fmt->fmt.pix.sizeimage = zr->buffer_size; - fmt->fmt.pix.pixelformat = zr->v4l_settings.format->fourcc; - fmt->fmt.pix.colorspace = zr->v4l_settings.format->colorspace; - fmt->fmt.pix.bytesperline = zr->v4l_settings.bytesperline; - if (BUZ_MAX_HEIGHT < (zr->v4l_settings.height * 2)) - fmt->fmt.pix.field = V4L2_FIELD_INTERLACED; - else - fmt->fmt.pix.field = V4L2_FIELD_TOP; - return 0; -} - -static int zoran_try_fmt_vid_out(struct file *file, void *__fh, - struct v4l2_format *fmt) -{ - struct zoran *zr = video_drvdata(file); - struct zoran_jpg_settings settings; - int res = 0; - - if (fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) - return -EINVAL; - - settings = zr->jpg_settings; - - /* we actually need to set 'real' parameters now */ - if ((fmt->fmt.pix.height * 2) > BUZ_MAX_HEIGHT) - settings.tmp_dcm = 1; - else - settings.tmp_dcm = 2; - settings.decimation = 0; - if (fmt->fmt.pix.height <= zr->jpg_settings.img_height / 2) - settings.ver_dcm = 2; - else - settings.ver_dcm = 1; - if (fmt->fmt.pix.width <= zr->jpg_settings.img_width / 4) - settings.hor_dcm = 4; - else if (fmt->fmt.pix.width <= zr->jpg_settings.img_width / 2) - settings.hor_dcm = 2; - else - settings.hor_dcm = 1; - if (settings.tmp_dcm == 1) - settings.field_per_buff = 2; - else - settings.field_per_buff = 1; - - if (settings.hor_dcm > 1) { - settings.img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0; - settings.img_width = (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH; - } else { - settings.img_x = 0; - settings.img_width = BUZ_MAX_WIDTH; - } - - /* check */ - res = zoran_check_jpg_settings(zr, &settings, 1); - if (res) - return res; - - /* tell the user what we actually did */ - fmt->fmt.pix.width = settings.img_width / settings.hor_dcm; - fmt->fmt.pix.height = settings.img_height * 2 / - (settings.tmp_dcm * settings.ver_dcm); - if (settings.tmp_dcm == 1) - fmt->fmt.pix.field = (zr->jpg_settings.odd_even ? - V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT); - else - fmt->fmt.pix.field = (zr->jpg_settings.odd_even ? - V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM); - - fmt->fmt.pix.sizeimage = zoran_v4l2_calc_bufsize(&settings); - fmt->fmt.pix.bytesperline = 0; - fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - return res; -} - -static int zoran_try_fmt_vid_cap(struct file *file, void *__fh, - struct v4l2_format *fmt) -{ - struct zoran *zr = video_drvdata(file); - int bpp; - int i; - - if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) - return zoran_try_fmt_vid_out(file, __fh, fmt); - - for (i = 0; i < NUM_FORMATS; i++) - if (zoran_formats[i].fourcc == fmt->fmt.pix.pixelformat) - break; - - if (i == NUM_FORMATS) { - /* TODO do not return here to fix the TRY_FMT cannot handle an invalid pixelformat*/ - return -EINVAL; - } - - fmt->fmt.pix.pixelformat = zoran_formats[i].fourcc; - fmt->fmt.pix.colorspace = zoran_formats[i].colorspace; - if (BUZ_MAX_HEIGHT < (fmt->fmt.pix.height * 2)) - fmt->fmt.pix.field = V4L2_FIELD_INTERLACED; - else - fmt->fmt.pix.field = V4L2_FIELD_TOP; - - bpp = DIV_ROUND_UP(zoran_formats[i].depth, 8); - v4l_bound_align_image(&fmt->fmt.pix.width, BUZ_MIN_WIDTH, BUZ_MAX_WIDTH, bpp == 2 ? 1 : 2, - &fmt->fmt.pix.height, BUZ_MIN_HEIGHT, BUZ_MAX_HEIGHT, 0, 0); - fmt->fmt.pix.bytesperline = fmt->fmt.pix.width * bpp; - fmt->fmt.pix.sizeimage = fmt->fmt.pix.bytesperline * fmt->fmt.pix.height; - return 0; -} - -static int zoran_s_fmt_vid_out(struct file *file, void *__fh, - struct v4l2_format *fmt) -{ - struct zoran *zr = video_drvdata(file); - __le32 printformat = __cpu_to_le32(fmt->fmt.pix.pixelformat); - struct zoran_jpg_settings settings; - int res = 0; - - pci_dbg(zr->pci_dev, "size=%dx%d, fmt=0x%x (%4.4s)\n", - fmt->fmt.pix.width, fmt->fmt.pix.height, - fmt->fmt.pix.pixelformat, - (char *)&printformat); - if (fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) - return -EINVAL; - - if (!fmt->fmt.pix.height || !fmt->fmt.pix.width) - return -EINVAL; - - settings = zr->jpg_settings; - - /* we actually need to set 'real' parameters now */ - if (fmt->fmt.pix.height * 2 > BUZ_MAX_HEIGHT) - settings.tmp_dcm = 1; - else - settings.tmp_dcm = 2; - settings.decimation = 0; - if (fmt->fmt.pix.height <= zr->jpg_settings.img_height / 2) - settings.ver_dcm = 2; - else - settings.ver_dcm = 1; - if (fmt->fmt.pix.width <= zr->jpg_settings.img_width / 4) - settings.hor_dcm = 4; - else if (fmt->fmt.pix.width <= zr->jpg_settings.img_width / 2) - settings.hor_dcm = 2; - else - settings.hor_dcm = 1; - if (settings.tmp_dcm == 1) - settings.field_per_buff = 2; - else - settings.field_per_buff = 1; - - if (settings.hor_dcm > 1) { - settings.img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0; - settings.img_width = (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH; - } else { - settings.img_x = 0; - settings.img_width = BUZ_MAX_WIDTH; - } - - /* check */ - res = zoran_check_jpg_settings(zr, &settings, 0); - if (res) - return res; - - /* it's ok, so set them */ - zr->jpg_settings = settings; - - if (fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) - zr->map_mode = ZORAN_MAP_MODE_JPG_REC; - else - zr->map_mode = ZORAN_MAP_MODE_JPG_PLAY; - - zr->buffer_size = zoran_v4l2_calc_bufsize(&zr->jpg_settings); - - /* tell the user what we actually did */ - fmt->fmt.pix.width = settings.img_width / settings.hor_dcm; - fmt->fmt.pix.height = settings.img_height * 2 / - (settings.tmp_dcm * settings.ver_dcm); - if (settings.tmp_dcm == 1) - fmt->fmt.pix.field = (zr->jpg_settings.odd_even ? - V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT); - else - fmt->fmt.pix.field = (zr->jpg_settings.odd_even ? - V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM); - fmt->fmt.pix.bytesperline = 0; - fmt->fmt.pix.sizeimage = zr->buffer_size; - fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - return res; -} - -static int zoran_s_fmt_vid_cap(struct file *file, void *__fh, - struct v4l2_format *fmt) -{ - struct zoran *zr = video_drvdata(file); - struct zoran_fh *fh = __fh; - int i; - int res = 0; - - if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) - return zoran_s_fmt_vid_out(file, fh, fmt); - - for (i = 0; i < NUM_FORMATS; i++) - if (fmt->fmt.pix.pixelformat == zoran_formats[i].fourcc) - break; - if (i == NUM_FORMATS) { - pci_dbg(zr->pci_dev, "VIDIOC_S_FMT - unknown/unsupported format 0x%x\n", - fmt->fmt.pix.pixelformat); - /* TODO do not return here to fix the TRY_FMT cannot handle an invalid pixelformat*/ - return -EINVAL; - } - - fmt->fmt.pix.pixelformat = zoran_formats[i].fourcc; - if (fmt->fmt.pix.height > BUZ_MAX_HEIGHT) - fmt->fmt.pix.height = BUZ_MAX_HEIGHT; - if (fmt->fmt.pix.width > BUZ_MAX_WIDTH) - fmt->fmt.pix.width = BUZ_MAX_WIDTH; - if (fmt->fmt.pix.height < BUZ_MIN_HEIGHT) - fmt->fmt.pix.height = BUZ_MIN_HEIGHT; - if (fmt->fmt.pix.width < BUZ_MIN_WIDTH) - fmt->fmt.pix.width = BUZ_MIN_WIDTH; - - zr->map_mode = ZORAN_MAP_MODE_RAW; - - res = zoran_v4l_set_format(zr, fmt->fmt.pix.width, fmt->fmt.pix.height, - &zoran_formats[i]); - if (res) - return res; - - /* tell the user the results/missing stuff */ - fmt->fmt.pix.bytesperline = zr->v4l_settings.bytesperline; - fmt->fmt.pix.sizeimage = zr->buffer_size; - fmt->fmt.pix.colorspace = zr->v4l_settings.format->colorspace; - if (BUZ_MAX_HEIGHT < (zr->v4l_settings.height * 2)) - fmt->fmt.pix.field = V4L2_FIELD_INTERLACED; - else - fmt->fmt.pix.field = V4L2_FIELD_TOP; - return res; -} - -static int zoran_g_std(struct file *file, void *__fh, v4l2_std_id *std) -{ - struct zoran *zr = video_drvdata(file); - - *std = zr->norm; - return 0; -} - -static int zoran_s_std(struct file *file, void *__fh, v4l2_std_id std) -{ - struct zoran *zr = video_drvdata(file); - int res = 0; - - if (zr->norm == std) - return 0; - - if (zr->running != ZORAN_MAP_MODE_NONE) - return -EBUSY; - - res = zoran_set_norm(zr, std); - return res; -} - -static int zoran_enum_input(struct file *file, void *__fh, - struct v4l2_input *inp) -{ - struct zoran *zr = video_drvdata(file); - - if (inp->index >= zr->card.inputs) - return -EINVAL; - - strscpy(inp->name, zr->card.input[inp->index].name, sizeof(inp->name)); - inp->type = V4L2_INPUT_TYPE_CAMERA; - inp->std = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM; - - /* Get status of video decoder */ - decoder_call(zr, video, g_input_status, &inp->status); - return 0; -} - -static int zoran_g_input(struct file *file, void *__fh, unsigned int *input) -{ - struct zoran *zr = video_drvdata(file); - - *input = zr->input; - - return 0; -} - -static int zoran_s_input(struct file *file, void *__fh, unsigned int input) -{ - struct zoran *zr = video_drvdata(file); - int res; - - if (zr->running != ZORAN_MAP_MODE_NONE) - return -EBUSY; - - res = zoran_set_input(zr, input); - return res; -} - -#if 0 -/* TODO: output does not work yet */ -static int zoran_enum_output(struct file *file, void *__fh, - struct v4l2_output *outp) -{ - if (outp->index != 0) - return -EINVAL; - - outp->index = 0; - outp->type = V4L2_OUTPUT_TYPE_ANALOGVGAOVERLAY; - outp->std = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM; - outp->capabilities = V4L2_OUT_CAP_STD; - strscpy(outp->name, "Autodetect", sizeof(outp->name)); - - return 0; -} -static int zoran_g_output(struct file *file, void *__fh, unsigned int *output) -{ - *output = 0; - - return 0; -} - -static int zoran_s_output(struct file *file, void *__fh, unsigned int output) -{ - if (output != 0) - return -EINVAL; - - return 0; -} -#endif - -/* cropping (sub-frame capture) */ -static int zoran_g_selection(struct file *file, void *__fh, struct v4l2_selection *sel) -{ - struct zoran *zr = video_drvdata(file); - - if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && - sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - pci_dbg(zr->pci_dev, "%s invalid selection type combination\n", __func__); - return -EINVAL; - } - - switch (sel->target) { - case V4L2_SEL_TGT_CROP: - sel->r.top = zr->jpg_settings.img_y; - sel->r.left = zr->jpg_settings.img_x; - sel->r.width = zr->jpg_settings.img_width; - sel->r.height = zr->jpg_settings.img_height; - break; - case V4L2_SEL_TGT_CROP_DEFAULT: - sel->r.top = 0; - sel->r.left = 0; - sel->r.width = BUZ_MIN_WIDTH; - sel->r.height = BUZ_MIN_HEIGHT; - break; - case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.top = 0; - sel->r.left = 0; - sel->r.width = BUZ_MAX_WIDTH; - sel->r.height = BUZ_MAX_HEIGHT; - break; - default: - return -EINVAL; - } - return 0; -} - -static int zoran_s_selection(struct file *file, void *__fh, struct v4l2_selection *sel) -{ - struct zoran *zr = video_drvdata(file); - struct zoran_jpg_settings settings; - int res; - - if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && - sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (!sel->r.width || !sel->r.height) - return -EINVAL; - - if (sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - if (zr->map_mode == ZORAN_MAP_MODE_RAW) { - pci_dbg(zr->pci_dev, "VIDIOC_S_SELECTION - subcapture only supported for compressed capture\n"); - return -EINVAL; - } - - settings = zr->jpg_settings; - - /* move into a form that we understand */ - settings.img_x = sel->r.left; - settings.img_y = sel->r.top; - settings.img_width = sel->r.width; - settings.img_height = sel->r.height; - - /* check validity */ - res = zoran_check_jpg_settings(zr, &settings, 0); - if (res) - return res; - - /* accept */ - zr->jpg_settings = settings; - return res; -} - -/* - * Output is disabled temporarily - * Zoran is picky about jpeg data it accepts. At least it seems to unsupport COM and APPn. - * So until a way to filter data will be done, disable output. - */ -static const struct v4l2_ioctl_ops zoran_ioctl_ops = { - .vidioc_querycap = zoran_querycap, - .vidioc_s_selection = zoran_s_selection, - .vidioc_g_selection = zoran_g_selection, - .vidioc_enum_input = zoran_enum_input, - .vidioc_g_input = zoran_g_input, - .vidioc_s_input = zoran_s_input, -/* .vidioc_enum_output = zoran_enum_output, - .vidioc_g_output = zoran_g_output, - .vidioc_s_output = zoran_s_output,*/ - .vidioc_g_std = zoran_g_std, - .vidioc_s_std = zoran_s_std, - .vidioc_create_bufs = vb2_ioctl_create_bufs, - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_expbuf = vb2_ioctl_expbuf, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - .vidioc_enum_fmt_vid_cap = zoran_enum_fmt_vid_cap, -/* .vidioc_enum_fmt_vid_out = zoran_enum_fmt_vid_out,*/ - .vidioc_g_fmt_vid_cap = zoran_g_fmt_vid_cap, -/* .vidioc_g_fmt_vid_out = zoran_g_fmt_vid_out,*/ - .vidioc_s_fmt_vid_cap = zoran_s_fmt_vid_cap, -/* .vidioc_s_fmt_vid_out = zoran_s_fmt_vid_out,*/ - .vidioc_try_fmt_vid_cap = zoran_try_fmt_vid_cap, -/* .vidioc_try_fmt_vid_out = zoran_try_fmt_vid_out,*/ - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static const struct v4l2_file_operations zoran_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = video_ioctl2, - .open = v4l2_fh_open, - .release = vb2_fop_release, - .mmap = vb2_fop_mmap, - .poll = vb2_fop_poll, -}; - -const struct video_device zoran_template = { - .name = ZORAN_NAME, - .fops = &zoran_fops, - .ioctl_ops = &zoran_ioctl_ops, - .release = &zoran_vdev_release, - .tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM, -}; - -static int zr_vb2_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], struct device *alloc_devs[]) -{ - struct zoran *zr = vb2_get_drv_priv(vq); - unsigned int size = zr->buffer_size; - - pci_dbg(zr->pci_dev, "%s nbuf=%u nplanes=%u", __func__, *nbuffers, *nplanes); - - zr->buf_in_reserve = 0; - - if (*nbuffers < vq->min_buffers_needed) - *nbuffers = vq->min_buffers_needed; - - if (*nplanes) { - if (sizes[0] < size) - return -EINVAL; - else - return 0; - } - - *nplanes = 1; - sizes[0] = size; - - return 0; -} - -static void zr_vb2_queue(struct vb2_buffer *vb) -{ - struct zoran *zr = vb2_get_drv_priv(vb->vb2_queue); - struct zr_buffer *buf = vb2_to_zr_buffer(vb); - unsigned long flags; - - spin_lock_irqsave(&zr->queued_bufs_lock, flags); - list_add_tail(&buf->queue, &zr->queued_bufs); - zr->buf_in_reserve++; - spin_unlock_irqrestore(&zr->queued_bufs_lock, flags); - if (zr->running == ZORAN_MAP_MODE_JPG_REC) - zoran_feed_stat_com(zr); - zr->queued++; -} - -static int zr_vb2_prepare(struct vb2_buffer *vb) -{ - struct zoran *zr = vb2_get_drv_priv(vb->vb2_queue); - - if (vb2_plane_size(vb, 0) < zr->buffer_size) - return -EINVAL; - zr->prepared++; - - return 0; -} - -int zr_set_buf(struct zoran *zr) -{ - struct zr_buffer *buf; - struct vb2_v4l2_buffer *vbuf; - dma_addr_t phys_addr; - unsigned long flags; - u32 reg; - - if (zr->running == ZORAN_MAP_MODE_NONE) - return 0; - - if (zr->inuse[0]) { - buf = zr->inuse[0]; - buf->vbuf.vb2_buf.timestamp = ktime_get_ns(); - buf->vbuf.sequence = zr->vbseq++; - vbuf = &buf->vbuf; - - buf->vbuf.field = V4L2_FIELD_INTERLACED; - if (BUZ_MAX_HEIGHT < (zr->v4l_settings.height * 2)) - buf->vbuf.field = V4L2_FIELD_INTERLACED; - else - buf->vbuf.field = V4L2_FIELD_TOP; - vb2_set_plane_payload(&buf->vbuf.vb2_buf, 0, zr->buffer_size); - vb2_buffer_done(&buf->vbuf.vb2_buf, VB2_BUF_STATE_DONE); - zr->inuse[0] = NULL; - } - - spin_lock_irqsave(&zr->queued_bufs_lock, flags); - if (list_empty(&zr->queued_bufs)) { - btand(~ZR36057_ICR_INT_PIN_EN, ZR36057_ICR); - vb2_queue_error(zr->video_dev->queue); - spin_unlock_irqrestore(&zr->queued_bufs_lock, flags); - return -EINVAL; - } - buf = list_first_entry_or_null(&zr->queued_bufs, struct zr_buffer, queue); - if (!buf) { - btand(~ZR36057_ICR_INT_PIN_EN, ZR36057_ICR); - vb2_queue_error(zr->video_dev->queue); - spin_unlock_irqrestore(&zr->queued_bufs_lock, flags); - return -EINVAL; - } - list_del(&buf->queue); - zr->buf_in_reserve--; - spin_unlock_irqrestore(&zr->queued_bufs_lock, flags); - - vbuf = &buf->vbuf; - vbuf->vb2_buf.state = VB2_BUF_STATE_ACTIVE; - phys_addr = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); - - if (!phys_addr) - return -EINVAL; - - zr->inuse[0] = buf; - - reg = phys_addr; - btwrite(reg, ZR36057_VDTR); - if (zr->v4l_settings.height > BUZ_MAX_HEIGHT / 2) - reg += zr->v4l_settings.bytesperline; - btwrite(reg, ZR36057_VDBR); - - reg = 0; - if (zr->v4l_settings.height > BUZ_MAX_HEIGHT / 2) - reg += zr->v4l_settings.bytesperline; - reg = (reg << ZR36057_VSSFGR_DISP_STRIDE); - reg |= ZR36057_VSSFGR_VID_OVF; - reg |= ZR36057_VSSFGR_SNAP_SHOT; - reg |= ZR36057_VSSFGR_FRAME_GRAB; - btwrite(reg, ZR36057_VSSFGR); - - btor(ZR36057_VDCR_VID_EN, ZR36057_VDCR); - return 0; -} - -static int zr_vb2_start_streaming(struct vb2_queue *vq, unsigned int count) -{ - struct zoran *zr = vq->drv_priv; - int j; - - for (j = 0; j < BUZ_NUM_STAT_COM; j++) { - zr->stat_com[j] = cpu_to_le32(1); - zr->inuse[j] = NULL; - } - zr->vbseq = 0; - - if (zr->map_mode != ZORAN_MAP_MODE_RAW) { - pci_dbg(zr->pci_dev, "START JPG\n"); - zr36057_restart(zr); - zoran_init_hardware(zr); - if (zr->map_mode == ZORAN_MAP_MODE_JPG_REC) - zr36057_enable_jpg(zr, BUZ_MODE_MOTION_DECOMPRESS); - else - zr36057_enable_jpg(zr, BUZ_MODE_MOTION_COMPRESS); - zoran_feed_stat_com(zr); - jpeg_start(zr); - zr->running = zr->map_mode; - btor(ZR36057_ICR_INT_PIN_EN, ZR36057_ICR); - return 0; - } - - pci_dbg(zr->pci_dev, "START RAW\n"); - zr36057_restart(zr); - zoran_init_hardware(zr); - - zr36057_enable_jpg(zr, BUZ_MODE_IDLE); - zr36057_set_memgrab(zr, 1); - zr->running = zr->map_mode; - btor(ZR36057_ICR_INT_PIN_EN, ZR36057_ICR); - return 0; -} - -static void zr_vb2_stop_streaming(struct vb2_queue *vq) -{ - struct zoran *zr = vq->drv_priv; - struct zr_buffer *buf; - unsigned long flags; - int j; - - btand(~ZR36057_ICR_INT_PIN_EN, ZR36057_ICR); - if (zr->map_mode != ZORAN_MAP_MODE_RAW) - zr36057_enable_jpg(zr, BUZ_MODE_IDLE); - zr36057_set_memgrab(zr, 0); - zr->running = ZORAN_MAP_MODE_NONE; - - zoran_set_pci_master(zr, 0); - - if (!pass_through) { /* Switch to color bar */ - decoder_call(zr, video, s_stream, 0); - encoder_call(zr, video, s_routing, 2, 0, 0); - } - - for (j = 0; j < BUZ_NUM_STAT_COM; j++) { - zr->stat_com[j] = cpu_to_le32(1); - if (!zr->inuse[j]) - continue; - buf = zr->inuse[j]; - pci_dbg(zr->pci_dev, "%s clean buf %d\n", __func__, j); - vb2_buffer_done(&buf->vbuf.vb2_buf, VB2_BUF_STATE_ERROR); - zr->inuse[j] = NULL; - } - - spin_lock_irqsave(&zr->queued_bufs_lock, flags); - while (!list_empty(&zr->queued_bufs)) { - buf = list_entry(zr->queued_bufs.next, struct zr_buffer, queue); - list_del(&buf->queue); - vb2_buffer_done(&buf->vbuf.vb2_buf, VB2_BUF_STATE_ERROR); - zr->buf_in_reserve--; - } - spin_unlock_irqrestore(&zr->queued_bufs_lock, flags); - if (zr->buf_in_reserve) - pci_dbg(zr->pci_dev, "Buffer remaining %d\n", zr->buf_in_reserve); - zr->map_mode = ZORAN_MAP_MODE_RAW; -} - -static const struct vb2_ops zr_video_qops = { - .queue_setup = zr_vb2_queue_setup, - .buf_queue = zr_vb2_queue, - .buf_prepare = zr_vb2_prepare, - .start_streaming = zr_vb2_start_streaming, - .stop_streaming = zr_vb2_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -int zoran_queue_init(struct zoran *zr, struct vb2_queue *vq, int dir) -{ - int err; - - spin_lock_init(&zr->queued_bufs_lock); - INIT_LIST_HEAD(&zr->queued_bufs); - - vq->dev = &zr->pci_dev->dev; - vq->type = dir; - - vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_READ | VB2_WRITE; - vq->drv_priv = zr; - vq->buf_struct_size = sizeof(struct zr_buffer); - vq->ops = &zr_video_qops; - vq->mem_ops = &vb2_dma_contig_memops; - vq->gfp_flags = GFP_DMA32; - vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - vq->min_buffers_needed = 9; - vq->lock = &zr->lock; - err = vb2_queue_init(vq); - if (err) - return err; - zr->video_dev->queue = vq; - return 0; -} - -void zoran_queue_exit(struct zoran *zr) -{ - vb2_queue_release(zr->video_dev->queue); -} diff --git a/drivers/staging/media/zoran/zr36016.c b/drivers/staging/media/zoran/zr36016.c deleted file mode 100644 index 0e0532537a3e..000000000000 --- a/drivers/staging/media/zoran/zr36016.c +++ /dev/null @@ -1,430 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Zoran ZR36016 basic configuration functions - * - * Copyright (C) 2001 Wolfgang Scherr <scherr@net4you.at> - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/slab.h> - -/* headerfile of this module */ -#include "zr36016.h" - -/* codec io API */ -#include "videocodec.h" - -/* it doesn't make sense to have more than 20 or so, - just to prevent some unwanted loops */ -#define MAX_CODECS 20 - -/* amount of chips attached via this driver */ -static int zr36016_codecs; - -/* ========================================================================= - Local hardware I/O functions: - - read/write via codec layer (registers are located in the master device) - ========================================================================= */ - -/* read and write functions */ -static u8 zr36016_read(struct zr36016 *ptr, u16 reg) -{ - u8 value = 0; - struct zoran *zr = videocodec_to_zoran(ptr->codec); - - /* just in case something is wrong... */ - if (ptr->codec->master_data->readreg) - value = (ptr->codec->master_data->readreg(ptr->codec, reg)) & 0xFF; - else - zrdev_err(zr, "%s: invalid I/O setup, nothing read!\n", ptr->name); - - zrdev_dbg(zr, "%s: reading from 0x%04x: %02x\n", ptr->name, reg, value); - - return value; -} - -static void zr36016_write(struct zr36016 *ptr, u16 reg, u8 value) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - - zrdev_dbg(zr, "%s: writing 0x%02x to 0x%04x\n", ptr->name, value, reg); - - // just in case something is wrong... - if (ptr->codec->master_data->writereg) - ptr->codec->master_data->writereg(ptr->codec, reg, value); - else - zrdev_err(zr, "%s: invalid I/O setup, nothing written!\n", ptr->name); -} - -/* indirect read and write functions */ -/* the 016 supports auto-addr-increment, but - * writing it all time cost not much and is safer... */ -static u8 zr36016_readi(struct zr36016 *ptr, u16 reg) -{ - u8 value = 0; - struct zoran *zr = videocodec_to_zoran(ptr->codec); - - /* just in case something is wrong... */ - if ((ptr->codec->master_data->writereg) && (ptr->codec->master_data->readreg)) { - ptr->codec->master_data->writereg(ptr->codec, ZR016_IADDR, reg & 0x0F); // ADDR - value = (ptr->codec->master_data->readreg(ptr->codec, ZR016_IDATA)) & 0xFF; // DATA - } else { - zrdev_err(zr, "%s: invalid I/O setup, nothing read (i)!\n", ptr->name); - } - - zrdev_dbg(zr, "%s: reading indirect from 0x%04x: %02x\n", - ptr->name, reg, value); - return value; -} - -static void zr36016_writei(struct zr36016 *ptr, u16 reg, u8 value) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - - zrdev_dbg(zr, "%s: writing indirect 0x%02x to 0x%04x\n", ptr->name, - value, reg); - - /* just in case something is wrong... */ - if (ptr->codec->master_data->writereg) { - ptr->codec->master_data->writereg(ptr->codec, ZR016_IADDR, reg & 0x0F); // ADDR - ptr->codec->master_data->writereg(ptr->codec, ZR016_IDATA, value & 0x0FF); // DATA - } else { - zrdev_err(zr, "%s: invalid I/O setup, nothing written (i)!\n", ptr->name); - } -} - -/* ========================================================================= - Local helper function: - - version read - ========================================================================= */ - -/* version kept in datastructure */ -static u8 zr36016_read_version(struct zr36016 *ptr) -{ - ptr->version = zr36016_read(ptr, 0) >> 4; - return ptr->version; -} - -/* ========================================================================= - Local helper function: - - basic test of "connectivity", writes/reads to/from PAX-Lo register - ========================================================================= */ - -static int zr36016_basic_test(struct zr36016 *ptr) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - - if (*KERN_INFO <= CONSOLE_LOGLEVEL_DEFAULT) { - int i; - - zr36016_writei(ptr, ZR016I_PAX_LO, 0x55); - zrdev_dbg(zr, "%s: registers: ", ptr->name); - for (i = 0; i <= 0x0b; i++) - zrdev_dbg(zr, "%02x ", zr36016_readi(ptr, i)); - zrdev_dbg(zr, "\n"); - } - // for testing just write 0, then the default value to a register and read - // it back in both cases - zr36016_writei(ptr, ZR016I_PAX_LO, 0x00); - if (zr36016_readi(ptr, ZR016I_PAX_LO) != 0x0) { - zrdev_err(zr, "%s: attach failed, can't connect to vfe processor!\n", ptr->name); - return -ENXIO; - } - zr36016_writei(ptr, ZR016I_PAX_LO, 0x0d0); - if (zr36016_readi(ptr, ZR016I_PAX_LO) != 0x0d0) { - zrdev_err(zr, "%s: attach failed, can't connect to vfe processor!\n", ptr->name); - return -ENXIO; - } - // we allow version numbers from 0-3, should be enough, though - zr36016_read_version(ptr); - if (ptr->version & 0x0c) { - zrdev_err(zr, "%s: attach failed, suspicious version %d found...\n", ptr->name, - ptr->version); - return -ENXIO; - } - - return 0; /* looks good! */ -} - -/* ========================================================================= - Local helper function: - - simple loop for pushing the init datasets - NO USE -- - ========================================================================= */ - -#if 0 -static int zr36016_pushit(struct zr36016 *ptr, - u16 startreg, - u16 len, - const char *data) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - int i = 0; - - zrdev_dbg(zr, "%s: write data block to 0x%04x (len=%d)\n", - ptr->name, startreg, len); - while (i < len) { - zr36016_writei(ptr, startreg++, data[i++]); - } - - return i; -} -#endif - -/* ========================================================================= - Basic datasets & init: - - //TODO// - ========================================================================= */ - -static void zr36016_init(struct zr36016 *ptr) -{ - // stop any processing - zr36016_write(ptr, ZR016_GOSTOP, 0); - - // mode setup (yuv422 in and out, compression/expansuon due to mode) - zr36016_write(ptr, ZR016_MODE, - ZR016_YUV422 | ZR016_YUV422_YUV422 | - (ptr->mode == CODEC_DO_COMPRESSION ? - ZR016_COMPRESSION : ZR016_EXPANSION)); - - // misc setup - zr36016_writei(ptr, ZR016I_SETUP1, - (ptr->xdec ? (ZR016_HRFL | ZR016_HORZ) : 0) | - (ptr->ydec ? ZR016_VERT : 0) | ZR016_CNTI); - zr36016_writei(ptr, ZR016I_SETUP2, ZR016_CCIR); - - // Window setup - // (no extra offset for now, norm defines offset, default width height) - zr36016_writei(ptr, ZR016I_PAX_HI, ptr->width >> 8); - zr36016_writei(ptr, ZR016I_PAX_LO, ptr->width & 0xFF); - zr36016_writei(ptr, ZR016I_PAY_HI, ptr->height >> 8); - zr36016_writei(ptr, ZR016I_PAY_LO, ptr->height & 0xFF); - zr36016_writei(ptr, ZR016I_NAX_HI, ptr->xoff >> 8); - zr36016_writei(ptr, ZR016I_NAX_LO, ptr->xoff & 0xFF); - zr36016_writei(ptr, ZR016I_NAY_HI, ptr->yoff >> 8); - zr36016_writei(ptr, ZR016I_NAY_LO, ptr->yoff & 0xFF); - - /* shall we continue now, please? */ - zr36016_write(ptr, ZR016_GOSTOP, 1); -} - -/* ========================================================================= - CODEC API FUNCTIONS - - this functions are accessed by the master via the API structure - ========================================================================= */ - -/* set compression/expansion mode and launches codec - - this should be the last call from the master before starting processing */ -static int zr36016_set_mode(struct videocodec *codec, int mode) -{ - struct zr36016 *ptr = (struct zr36016 *)codec->data; - struct zoran *zr = videocodec_to_zoran(codec); - - zrdev_dbg(zr, "%s: set_mode %d call\n", ptr->name, mode); - - if ((mode != CODEC_DO_EXPANSION) && (mode != CODEC_DO_COMPRESSION)) - return -EINVAL; - - ptr->mode = mode; - zr36016_init(ptr); - - return 0; -} - -/* set picture size */ -static int zr36016_set_video(struct videocodec *codec, const struct tvnorm *norm, - struct vfe_settings *cap, struct vfe_polarity *pol) -{ - struct zr36016 *ptr = (struct zr36016 *)codec->data; - struct zoran *zr = videocodec_to_zoran(codec); - - zrdev_dbg(zr, "%s: set_video %d.%d, %d/%d-%dx%d (0x%x) call\n", - ptr->name, norm->h_start, norm->v_start, - cap->x, cap->y, cap->width, cap->height, - cap->decimation); - - /* if () return -EINVAL; - * trust the master driver that it knows what it does - so - * we allow invalid startx/y for now ... */ - ptr->width = cap->width; - ptr->height = cap->height; - /* (Ronald) This is ugly. zoran_device.c, line 387 - * already mentions what happens if h_start is even - * (blue faces, etc., cr/cb inversed). There's probably - * some good reason why h_start is 0 instead of 1, so I'm - * leaving it to this for now, but really... This can be - * done a lot simpler */ - ptr->xoff = (norm->h_start ? norm->h_start : 1) + cap->x; - /* Something to note here (I don't understand it), setting - * v_start too high will cause the codec to 'not work'. I - * really don't get it. values of 16 (v_start) already break - * it here. Just '0' seems to work. More testing needed! */ - ptr->yoff = norm->v_start + cap->y; - /* (Ronald) dzjeeh, can't this thing do hor_decimation = 4? */ - ptr->xdec = ((cap->decimation & 0xff) == 1) ? 0 : 1; - ptr->ydec = (((cap->decimation >> 8) & 0xff) == 1) ? 0 : 1; - - return 0; -} - -/* additional control functions */ -static int zr36016_control(struct videocodec *codec, int type, int size, void *data) -{ - struct zr36016 *ptr = (struct zr36016 *)codec->data; - struct zoran *zr = videocodec_to_zoran(codec); - int *ival = (int *)data; - - zrdev_dbg(zr, "%s: control %d call with %d byte\n", - ptr->name, type, size); - - switch (type) { - case CODEC_G_STATUS: /* get last status - we don't know it ... */ - if (size != sizeof(int)) - return -EFAULT; - *ival = 0; - break; - - case CODEC_G_CODEC_MODE: - if (size != sizeof(int)) - return -EFAULT; - *ival = 0; - break; - - case CODEC_S_CODEC_MODE: - if (size != sizeof(int)) - return -EFAULT; - if (*ival != 0) - return -EINVAL; - /* not needed, do nothing */ - return 0; - - case CODEC_G_VFE: - case CODEC_S_VFE: - return 0; - - case CODEC_S_MMAP: - /* not available, give an error */ - return -ENXIO; - - default: - return -EINVAL; - } - - return size; -} - -/* ========================================================================= - Exit and unregister function: - - Deinitializes Zoran's JPEG processor - ========================================================================= */ - -static int zr36016_unset(struct videocodec *codec) -{ - struct zr36016 *ptr = codec->data; - struct zoran *zr = videocodec_to_zoran(codec); - - if (ptr) { - /* do wee need some codec deinit here, too ???? */ - - zrdev_dbg(zr, "%s: finished codec #%d\n", ptr->name, ptr->num); - kfree(ptr); - codec->data = NULL; - - zr36016_codecs--; - return 0; - } - - return -EFAULT; -} - -/* ========================================================================= - Setup and registry function: - - Initializes Zoran's JPEG processor - - Also sets pixel size, average code size, mode (compr./decompr.) - (the given size is determined by the processor with the video interface) - ========================================================================= */ - -static int zr36016_setup(struct videocodec *codec) -{ - struct zr36016 *ptr; - struct zoran *zr = videocodec_to_zoran(codec); - int res; - - zrdev_dbg(zr, "zr36016: initializing VFE subsystem #%d.\n", zr36016_codecs); - - if (zr36016_codecs == MAX_CODECS) { - zrdev_err(zr, "zr36016: Can't attach more codecs!\n"); - return -ENOSPC; - } - //mem structure init - ptr = kzalloc(sizeof(*ptr), GFP_KERNEL); - codec->data = ptr; - if (!ptr) - return -ENOMEM; - - snprintf(ptr->name, sizeof(ptr->name), "zr36016[%d]", zr36016_codecs); - ptr->num = zr36016_codecs++; - ptr->codec = codec; - - //testing - res = zr36016_basic_test(ptr); - if (res < 0) { - zr36016_unset(codec); - return res; - } - //final setup - ptr->mode = CODEC_DO_COMPRESSION; - ptr->width = 768; - ptr->height = 288; - ptr->xdec = 1; - ptr->ydec = 0; - zr36016_init(ptr); - - zrdev_dbg(zr, "%s: codec v%d attached and running\n", - ptr->name, ptr->version); - - return 0; -} - -static const struct videocodec zr36016_codec = { - .name = "zr36016", - .magic = 0L, /* magic not used */ - .flags = - CODEC_FLAG_HARDWARE | CODEC_FLAG_VFE | CODEC_FLAG_ENCODER | - CODEC_FLAG_DECODER, - .type = CODEC_TYPE_ZR36016, - .setup = zr36016_setup, /* functionality */ - .unset = zr36016_unset, - .set_mode = zr36016_set_mode, - .set_video = zr36016_set_video, - .control = zr36016_control, - /* others are not used */ -}; - -/* ========================================================================= - HOOK IN DRIVER AS KERNEL MODULE - ========================================================================= */ - -int zr36016_init_module(void) -{ - zr36016_codecs = 0; - return videocodec_register(&zr36016_codec); -} - -void zr36016_cleanup_module(void) -{ - if (zr36016_codecs) { - pr_debug("zr36016: something's wrong - %d codecs left somehow.\n", - zr36016_codecs); - } - videocodec_unregister(&zr36016_codec); -} diff --git a/drivers/staging/media/zoran/zr36016.h b/drivers/staging/media/zoran/zr36016.h deleted file mode 100644 index 04afba35669d..000000000000 --- a/drivers/staging/media/zoran/zr36016.h +++ /dev/null @@ -1,94 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Zoran ZR36016 basic configuration functions - header file - * - * Copyright (C) 2001 Wolfgang Scherr <scherr@net4you.at> - */ - -#ifndef ZR36016_H -#define ZR36016_H - -/* data stored for each zoran jpeg codec chip */ -struct zr36016 { - char name[32]; - int num; - /* io datastructure */ - struct videocodec *codec; - // coder status - __u8 version; - // actual coder setup - int mode; - - __u16 xoff; - __u16 yoff; - __u16 width; - __u16 height; - __u16 xdec; - __u16 ydec; -}; - -/* direct register addresses */ -#define ZR016_GOSTOP 0x00 -#define ZR016_MODE 0x01 -#define ZR016_IADDR 0x02 -#define ZR016_IDATA 0x03 - -/* indirect register addresses */ -#define ZR016I_SETUP1 0x00 -#define ZR016I_SETUP2 0x01 -#define ZR016I_NAX_LO 0x02 -#define ZR016I_NAX_HI 0x03 -#define ZR016I_PAX_LO 0x04 -#define ZR016I_PAX_HI 0x05 -#define ZR016I_NAY_LO 0x06 -#define ZR016I_NAY_HI 0x07 -#define ZR016I_PAY_LO 0x08 -#define ZR016I_PAY_HI 0x09 -#define ZR016I_NOL_LO 0x0a -#define ZR016I_NOL_HI 0x0b - -/* possible values for mode register */ -#define ZR016_RGB444_YUV444 0x00 -#define ZR016_RGB444_YUV422 0x01 -#define ZR016_RGB444_YUV411 0x02 -#define ZR016_RGB444_Y400 0x03 -#define ZR016_RGB444_RGB444 0x04 -#define ZR016_YUV444_YUV444 0x08 -#define ZR016_YUV444_YUV422 0x09 -#define ZR016_YUV444_YUV411 0x0a -#define ZR016_YUV444_Y400 0x0b -#define ZR016_YUV444_RGB444 0x0c -#define ZR016_YUV422_YUV422 0x11 -#define ZR016_YUV422_YUV411 0x12 -#define ZR016_YUV422_Y400 0x13 -#define ZR016_YUV411_YUV411 0x16 -#define ZR016_YUV411_Y400 0x17 -#define ZR016_4444_4444 0x19 -#define ZR016_100_100 0x1b - -#define ZR016_RGB444 0x00 -#define ZR016_YUV444 0x20 -#define ZR016_YUV422 0x40 - -#define ZR016_COMPRESSION 0x80 -#define ZR016_EXPANSION 0x80 - -/* possible values for setup 1 register */ -#define ZR016_CKRT 0x80 -#define ZR016_VERT 0x40 -#define ZR016_HORZ 0x20 -#define ZR016_HRFL 0x10 -#define ZR016_DSFL 0x08 -#define ZR016_SBFL 0x04 -#define ZR016_RSTR 0x02 -#define ZR016_CNTI 0x01 - -/* possible values for setup 2 register */ -#define ZR016_SYEN 0x40 -#define ZR016_CCIR 0x04 -#define ZR016_SIGN 0x02 -#define ZR016_YMCS 0x01 - -int zr36016_init_module(void); -void zr36016_cleanup_module(void); -#endif /*fndef ZR36016_H */ diff --git a/drivers/staging/media/zoran/zr36050.c b/drivers/staging/media/zoran/zr36050.c deleted file mode 100644 index 6a7ef28d996c..000000000000 --- a/drivers/staging/media/zoran/zr36050.c +++ /dev/null @@ -1,829 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Zoran ZR36050 basic configuration functions - * - * Copyright (C) 2001 Wolfgang Scherr <scherr@net4you.at> - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/delay.h> - -#include <linux/types.h> -#include <linux/wait.h> - -/* I/O commands, error codes */ -#include <linux/io.h> - -/* headerfile of this module */ -#include "zr36050.h" - -/* codec io API */ -#include "videocodec.h" - -/* it doesn't make sense to have more than 20 or so, - just to prevent some unwanted loops */ -#define MAX_CODECS 20 - -/* amount of chips attached via this driver */ -static int zr36050_codecs; - -/* ========================================================================= - Local hardware I/O functions: - - read/write via codec layer (registers are located in the master device) - ========================================================================= */ - -/* read and write functions */ -static u8 zr36050_read(struct zr36050 *ptr, u16 reg) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - u8 value = 0; - - /* just in case something is wrong... */ - if (ptr->codec->master_data->readreg) - value = (ptr->codec->master_data->readreg(ptr->codec, reg)) & 0xFF; - else - zrdev_err(zr, "%s: invalid I/O setup, nothing read!\n", ptr->name); - - zrdev_dbg(zr, "%s: reading from 0x%04x: %02x\n", ptr->name, reg, value); - - return value; -} - -static void zr36050_write(struct zr36050 *ptr, u16 reg, u8 value) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - - zrdev_dbg(zr, "%s: writing 0x%02x to 0x%04x\n", ptr->name, value, reg); - - /* just in case something is wrong... */ - if (ptr->codec->master_data->writereg) - ptr->codec->master_data->writereg(ptr->codec, reg, value); - else - zrdev_err(zr, "%s: invalid I/O setup, nothing written!\n", - ptr->name); -} - -/* ========================================================================= - Local helper function: - - status read - ========================================================================= */ - -/* status is kept in datastructure */ -static u8 zr36050_read_status1(struct zr36050 *ptr) -{ - ptr->status1 = zr36050_read(ptr, ZR050_STATUS_1); - - zr36050_read(ptr, 0); - return ptr->status1; -} - -/* ========================================================================= - Local helper function: - - scale factor read - ========================================================================= */ - -/* scale factor is kept in datastructure */ -static u16 zr36050_read_scalefactor(struct zr36050 *ptr) -{ - ptr->scalefact = (zr36050_read(ptr, ZR050_SF_HI) << 8) | - (zr36050_read(ptr, ZR050_SF_LO) & 0xFF); - - /* leave 0 selected for an eventually GO from master */ - zr36050_read(ptr, 0); - return ptr->scalefact; -} - -/* ========================================================================= - Local helper function: - - wait if codec is ready to proceed (end of processing) or time is over - ========================================================================= */ - -static void zr36050_wait_end(struct zr36050 *ptr) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - int i = 0; - - while (!(zr36050_read_status1(ptr) & 0x4)) { - udelay(1); - if (i++ > 200000) { // 200ms, there is for sure something wrong!!! - zrdev_err(zr, - "%s: timeout at wait_end (last status: 0x%02x)\n", - ptr->name, ptr->status1); - break; - } - } -} - -/* ========================================================================= - Local helper function: - - basic test of "connectivity", writes/reads to/from memory the SOF marker - ========================================================================= */ - -static int zr36050_basic_test(struct zr36050 *ptr) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - - zr36050_write(ptr, ZR050_SOF_IDX, 0x00); - zr36050_write(ptr, ZR050_SOF_IDX + 1, 0x00); - if ((zr36050_read(ptr, ZR050_SOF_IDX) | - zr36050_read(ptr, ZR050_SOF_IDX + 1)) != 0x0000) { - zrdev_err(zr, - "%s: attach failed, can't connect to jpeg processor!\n", - ptr->name); - return -ENXIO; - } - zr36050_write(ptr, ZR050_SOF_IDX, 0xff); - zr36050_write(ptr, ZR050_SOF_IDX + 1, 0xc0); - if (((zr36050_read(ptr, ZR050_SOF_IDX) << 8) | - zr36050_read(ptr, ZR050_SOF_IDX + 1)) != 0xffc0) { - zrdev_err(zr, - "%s: attach failed, can't connect to jpeg processor!\n", - ptr->name); - return -ENXIO; - } - - zr36050_wait_end(ptr); - if ((ptr->status1 & 0x4) == 0) { - zrdev_err(zr, - "%s: attach failed, jpeg processor failed (end flag)!\n", - ptr->name); - return -EBUSY; - } - - return 0; /* looks good! */ -} - -/* ========================================================================= - Local helper function: - - simple loop for pushing the init datasets - ========================================================================= */ - -static int zr36050_pushit(struct zr36050 *ptr, u16 startreg, u16 len, const char *data) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - int i = 0; - - zrdev_dbg(zr, "%s: write data block to 0x%04x (len=%d)\n", ptr->name, - startreg, len); - while (i < len) - zr36050_write(ptr, startreg++, data[i++]); - - return i; -} - -/* ========================================================================= - Basic datasets: - - jpeg baseline setup data (you find it on lots places in internet, or just - extract it from any regular .jpg image...) - - Could be variable, but until it's not needed it they are just fixed to save - memory. Otherwise expand zr36050 structure with arrays, push the values to - it and initialize from there, as e.g. the linux zr36057/60 driver does it. - ========================================================================= */ - -static const char zr36050_dqt[0x86] = { - 0xff, 0xdb, //Marker: DQT - 0x00, 0x84, //Length: 2*65+2 - 0x00, //Pq,Tq first table - 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, - 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, - 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, - 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33, - 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44, - 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, - 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, - 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63, - 0x01, //Pq,Tq second table - 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, - 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63 -}; - -static const char zr36050_dht[0x1a4] = { - 0xff, 0xc4, //Marker: DHT - 0x01, 0xa2, //Length: 2*AC, 2*DC - 0x00, //DC first table - 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, - 0x01, //DC second table - 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, - 0x10, //AC first table - 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, - 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, - 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, - 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, - 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, - 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, - 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, - 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, - 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, - 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, - 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, - 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, - 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, - 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, - 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, - 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, - 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, - 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, - 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, - 0xF8, 0xF9, 0xFA, - 0x11, //AC second table - 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, - 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, - 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, - 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, - 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, - 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, - 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, - 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, - 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, - 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, - 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, - 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, - 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, - 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, - 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, - 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, - 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, - 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, - 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, - 0xF9, 0xFA -}; - -/* jpeg baseline setup, this is just fixed in this driver (YUV pictures) */ -#define NO_OF_COMPONENTS 0x3 //Y,U,V -#define BASELINE_PRECISION 0x8 //MCU size (?) -static const char zr36050_tq[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's QT -static const char zr36050_td[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's DC -static const char zr36050_ta[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's AC - -/* horizontal 422 decimation setup (maybe we support 411 or so later, too) */ -static const char zr36050_decimation_h[8] = { 2, 1, 1, 0, 0, 0, 0, 0 }; -static const char zr36050_decimation_v[8] = { 1, 1, 1, 0, 0, 0, 0, 0 }; - -/* ========================================================================= - Local helper functions: - - calculation and setup of parameter-dependent JPEG baseline segments - (needed for compression only) - ========================================================================= */ - -/* ------------------------------------------------------------------------- */ - -/* SOF (start of frame) segment depends on width, height and sampling ratio - of each color component */ - -static int zr36050_set_sof(struct zr36050 *ptr) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - char sof_data[34]; // max. size of register set - int i; - - zrdev_dbg(zr, "%s: write SOF (%dx%d, %d components)\n", ptr->name, - ptr->width, ptr->height, NO_OF_COMPONENTS); - sof_data[0] = 0xff; - sof_data[1] = 0xc0; - sof_data[2] = 0x00; - sof_data[3] = (3 * NO_OF_COMPONENTS) + 8; - sof_data[4] = BASELINE_PRECISION; // only '8' possible with zr36050 - sof_data[5] = (ptr->height) >> 8; - sof_data[6] = (ptr->height) & 0xff; - sof_data[7] = (ptr->width) >> 8; - sof_data[8] = (ptr->width) & 0xff; - sof_data[9] = NO_OF_COMPONENTS; - for (i = 0; i < NO_OF_COMPONENTS; i++) { - sof_data[10 + (i * 3)] = i; // index identifier - sof_data[11 + (i * 3)] = (ptr->h_samp_ratio[i] << 4) | (ptr->v_samp_ratio[i]); // sampling ratios - sof_data[12 + (i * 3)] = zr36050_tq[i]; // Q table selection - } - return zr36050_pushit(ptr, ZR050_SOF_IDX, - (3 * NO_OF_COMPONENTS) + 10, sof_data); -} - -/* ------------------------------------------------------------------------- */ - -/* SOS (start of scan) segment depends on the used scan components - of each color component */ - -static int zr36050_set_sos(struct zr36050 *ptr) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - char sos_data[16]; // max. size of register set - int i; - - zrdev_dbg(zr, "%s: write SOS\n", ptr->name); - sos_data[0] = 0xff; - sos_data[1] = 0xda; - sos_data[2] = 0x00; - sos_data[3] = 2 + 1 + (2 * NO_OF_COMPONENTS) + 3; - sos_data[4] = NO_OF_COMPONENTS; - for (i = 0; i < NO_OF_COMPONENTS; i++) { - sos_data[5 + (i * 2)] = i; // index - sos_data[6 + (i * 2)] = (zr36050_td[i] << 4) | zr36050_ta[i]; // AC/DC tbl.sel. - } - sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 2] = 00; // scan start - sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 3] = 0x3F; - sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 4] = 00; - return zr36050_pushit(ptr, ZR050_SOS1_IDX, - 4 + 1 + (2 * NO_OF_COMPONENTS) + 3, - sos_data); -} - -/* ------------------------------------------------------------------------- */ - -/* DRI (define restart interval) */ - -static int zr36050_set_dri(struct zr36050 *ptr) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - char dri_data[6]; // max. size of register set - - zrdev_dbg(zr, "%s: write DRI\n", ptr->name); - dri_data[0] = 0xff; - dri_data[1] = 0xdd; - dri_data[2] = 0x00; - dri_data[3] = 0x04; - dri_data[4] = ptr->dri >> 8; - dri_data[5] = ptr->dri & 0xff; - return zr36050_pushit(ptr, ZR050_DRI_IDX, 6, dri_data); -} - -/* ========================================================================= - Setup function: - - Setup compression/decompression of Zoran's JPEG processor - ( see also zoran 36050 manual ) - - ... sorry for the spaghetti code ... - ========================================================================= */ -static void zr36050_init(struct zr36050 *ptr) -{ - int sum = 0; - long bitcnt, tmp; - struct zoran *zr = videocodec_to_zoran(ptr->codec); - - if (ptr->mode == CODEC_DO_COMPRESSION) { - zrdev_dbg(zr, "%s: COMPRESSION SETUP\n", ptr->name); - - /* 050 communicates with 057 in master mode */ - zr36050_write(ptr, ZR050_HARDWARE, ZR050_HW_MSTR); - - /* encoding table preload for compression */ - zr36050_write(ptr, ZR050_MODE, - ZR050_MO_COMP | ZR050_MO_TLM); - zr36050_write(ptr, ZR050_OPTIONS, 0); - - /* disable all IRQs */ - zr36050_write(ptr, ZR050_INT_REQ_0, 0); - zr36050_write(ptr, ZR050_INT_REQ_1, 3); // low 2 bits always 1 - - /* volume control settings */ - /*zr36050_write(ptr, ZR050_MBCV, ptr->max_block_vol);*/ - zr36050_write(ptr, ZR050_SF_HI, ptr->scalefact >> 8); - zr36050_write(ptr, ZR050_SF_LO, ptr->scalefact & 0xff); - - zr36050_write(ptr, ZR050_AF_HI, 0xff); - zr36050_write(ptr, ZR050_AF_M, 0xff); - zr36050_write(ptr, ZR050_AF_LO, 0xff); - - /* setup the variable jpeg tables */ - sum += zr36050_set_sof(ptr); - sum += zr36050_set_sos(ptr); - sum += zr36050_set_dri(ptr); - - /* setup the fixed jpeg tables - maybe variable, though - - * (see table init section above) */ - zrdev_dbg(zr, "%s: write DQT, DHT, APP\n", ptr->name); - sum += zr36050_pushit(ptr, ZR050_DQT_IDX, - sizeof(zr36050_dqt), zr36050_dqt); - sum += zr36050_pushit(ptr, ZR050_DHT_IDX, - sizeof(zr36050_dht), zr36050_dht); - zr36050_write(ptr, ZR050_APP_IDX, 0xff); - zr36050_write(ptr, ZR050_APP_IDX + 1, 0xe0 + ptr->app.appn); - zr36050_write(ptr, ZR050_APP_IDX + 2, 0x00); - zr36050_write(ptr, ZR050_APP_IDX + 3, ptr->app.len + 2); - sum += zr36050_pushit(ptr, ZR050_APP_IDX + 4, 60, - ptr->app.data) + 4; - zr36050_write(ptr, ZR050_COM_IDX, 0xff); - zr36050_write(ptr, ZR050_COM_IDX + 1, 0xfe); - zr36050_write(ptr, ZR050_COM_IDX + 2, 0x00); - zr36050_write(ptr, ZR050_COM_IDX + 3, ptr->com.len + 2); - sum += zr36050_pushit(ptr, ZR050_COM_IDX + 4, 60, - ptr->com.data) + 4; - - /* do the internal huffman table preload */ - zr36050_write(ptr, ZR050_MARKERS_EN, ZR050_ME_DHTI); - - zr36050_write(ptr, ZR050_GO, 1); // launch codec - zr36050_wait_end(ptr); - zrdev_dbg(zr, "%s: Status after table preload: 0x%02x\n", - ptr->name, ptr->status1); - - if ((ptr->status1 & 0x4) == 0) { - zrdev_err(zr, "%s: init aborted!\n", ptr->name); - return; // something is wrong, its timed out!!!! - } - - /* setup misc. data for compression (target code sizes) */ - - /* size of compressed code to reach without header data */ - sum = ptr->real_code_vol - sum; - bitcnt = sum << 3; /* need the size in bits */ - - tmp = bitcnt >> 16; - zrdev_dbg(zr, - "%s: code: csize=%d, tot=%d, bit=%ld, highbits=%ld\n", - ptr->name, sum, ptr->real_code_vol, bitcnt, tmp); - zr36050_write(ptr, ZR050_TCV_NET_HI, tmp >> 8); - zr36050_write(ptr, ZR050_TCV_NET_MH, tmp & 0xff); - tmp = bitcnt & 0xffff; - zr36050_write(ptr, ZR050_TCV_NET_ML, tmp >> 8); - zr36050_write(ptr, ZR050_TCV_NET_LO, tmp & 0xff); - - bitcnt -= bitcnt >> 7; // bits without stuffing - bitcnt -= ((bitcnt * 5) >> 6); // bits without eob - - tmp = bitcnt >> 16; - zrdev_dbg(zr, "%s: code: nettobit=%ld, highnettobits=%ld\n", - ptr->name, bitcnt, tmp); - zr36050_write(ptr, ZR050_TCV_DATA_HI, tmp >> 8); - zr36050_write(ptr, ZR050_TCV_DATA_MH, tmp & 0xff); - tmp = bitcnt & 0xffff; - zr36050_write(ptr, ZR050_TCV_DATA_ML, tmp >> 8); - zr36050_write(ptr, ZR050_TCV_DATA_LO, tmp & 0xff); - - /* compression setup with or without bitrate control */ - zr36050_write(ptr, ZR050_MODE, - ZR050_MO_COMP | ZR050_MO_PASS2 | - (ptr->bitrate_ctrl ? ZR050_MO_BRC : 0)); - - /* this headers seem to deliver "valid AVI" jpeg frames */ - zr36050_write(ptr, ZR050_MARKERS_EN, - ZR050_ME_DQT | ZR050_ME_DHT | - ((ptr->app.len > 0) ? ZR050_ME_APP : 0) | - ((ptr->com.len > 0) ? ZR050_ME_COM : 0)); - } else { - zrdev_dbg(zr, "%s: EXPANSION SETUP\n", ptr->name); - - /* 050 communicates with 055 in master mode */ - zr36050_write(ptr, ZR050_HARDWARE, - ZR050_HW_MSTR | ZR050_HW_CFIS_2_CLK); - - /* encoding table preload */ - zr36050_write(ptr, ZR050_MODE, ZR050_MO_TLM); - - /* disable all IRQs */ - zr36050_write(ptr, ZR050_INT_REQ_0, 0); - zr36050_write(ptr, ZR050_INT_REQ_1, 3); // low 2 bits always 1 - - zrdev_dbg(zr, "%s: write DHT\n", ptr->name); - zr36050_pushit(ptr, ZR050_DHT_IDX, sizeof(zr36050_dht), - zr36050_dht); - - /* do the internal huffman table preload */ - zr36050_write(ptr, ZR050_MARKERS_EN, ZR050_ME_DHTI); - - zr36050_write(ptr, ZR050_GO, 1); // launch codec - zr36050_wait_end(ptr); - zrdev_dbg(zr, "%s: Status after table preload: 0x%02x\n", - ptr->name, ptr->status1); - - if ((ptr->status1 & 0x4) == 0) { - zrdev_err(zr, "%s: init aborted!\n", ptr->name); - return; // something is wrong, its timed out!!!! - } - - /* setup misc. data for expansion */ - zr36050_write(ptr, ZR050_MODE, 0); - zr36050_write(ptr, ZR050_MARKERS_EN, 0); - } - - /* adr on selected, to allow GO from master */ - zr36050_read(ptr, 0); -} - -/* ========================================================================= - CODEC API FUNCTIONS - - this functions are accessed by the master via the API structure - ========================================================================= */ - -/* set compression/expansion mode and launches codec - - this should be the last call from the master before starting processing */ -static int zr36050_set_mode(struct videocodec *codec, int mode) -{ - struct zr36050 *ptr = (struct zr36050 *)codec->data; - struct zoran *zr = videocodec_to_zoran(codec); - - zrdev_dbg(zr, "%s: set_mode %d call\n", ptr->name, mode); - - if ((mode != CODEC_DO_EXPANSION) && (mode != CODEC_DO_COMPRESSION)) - return -EINVAL; - - ptr->mode = mode; - zr36050_init(ptr); - - return 0; -} - -/* set picture size (norm is ignored as the codec doesn't know about it) */ -static int zr36050_set_video(struct videocodec *codec, const struct tvnorm *norm, - struct vfe_settings *cap, struct vfe_polarity *pol) -{ - struct zr36050 *ptr = (struct zr36050 *)codec->data; - struct zoran *zr = videocodec_to_zoran(codec); - int size; - - zrdev_dbg(zr, "%s: set_video %d.%d, %d/%d-%dx%d (0x%x) q%d call\n", - ptr->name, norm->h_start, norm->v_start, - cap->x, cap->y, cap->width, cap->height, - cap->decimation, cap->quality); - /* if () return -EINVAL; - * trust the master driver that it knows what it does - so - * we allow invalid startx/y and norm for now ... */ - ptr->width = cap->width / (cap->decimation & 0xff); - ptr->height = cap->height / ((cap->decimation >> 8) & 0xff); - - /* (KM) JPEG quality */ - size = ptr->width * ptr->height; - size *= 16; /* size in bits */ - /* apply quality setting */ - size = size * cap->quality / 200; - - /* Minimum: 1kb */ - if (size < 8192) - size = 8192; - /* Maximum: 7/8 of code buffer */ - if (size > ptr->total_code_vol * 7) - size = ptr->total_code_vol * 7; - - ptr->real_code_vol = size >> 3; /* in bytes */ - - /* Set max_block_vol here (previously in zr36050_init, moved - * here for consistency with zr36060 code */ - zr36050_write(ptr, ZR050_MBCV, ptr->max_block_vol); - - return 0; -} - -/* additional control functions */ -static int zr36050_control(struct videocodec *codec, int type, int size, void *data) -{ - struct zr36050 *ptr = (struct zr36050 *)codec->data; - struct zoran *zr = videocodec_to_zoran(codec); - int *ival = (int *)data; - - zrdev_dbg(zr, "%s: control %d call with %d byte\n", ptr->name, type, - size); - - switch (type) { - case CODEC_G_STATUS: /* get last status */ - if (size != sizeof(int)) - return -EFAULT; - zr36050_read_status1(ptr); - *ival = ptr->status1; - break; - - case CODEC_G_CODEC_MODE: - if (size != sizeof(int)) - return -EFAULT; - *ival = CODEC_MODE_BJPG; - break; - - case CODEC_S_CODEC_MODE: - if (size != sizeof(int)) - return -EFAULT; - if (*ival != CODEC_MODE_BJPG) - return -EINVAL; - /* not needed, do nothing */ - return 0; - - case CODEC_G_VFE: - case CODEC_S_VFE: - /* not needed, do nothing */ - return 0; - - case CODEC_S_MMAP: - /* not available, give an error */ - return -ENXIO; - - case CODEC_G_JPEG_TDS_BYTE: /* get target volume in byte */ - if (size != sizeof(int)) - return -EFAULT; - *ival = ptr->total_code_vol; - break; - - case CODEC_S_JPEG_TDS_BYTE: /* get target volume in byte */ - if (size != sizeof(int)) - return -EFAULT; - ptr->total_code_vol = *ival; - /* (Kieran Morrissey) - * code copied from zr36060.c to ensure proper bitrate */ - ptr->real_code_vol = (ptr->total_code_vol * 6) >> 3; - break; - - case CODEC_G_JPEG_SCALE: /* get scaling factor */ - if (size != sizeof(int)) - return -EFAULT; - *ival = zr36050_read_scalefactor(ptr); - break; - - case CODEC_S_JPEG_SCALE: /* set scaling factor */ - if (size != sizeof(int)) - return -EFAULT; - ptr->scalefact = *ival; - break; - - case CODEC_G_JPEG_APP_DATA: { /* get appn marker data */ - struct jpeg_app_marker *app = data; - - if (size != sizeof(struct jpeg_app_marker)) - return -EFAULT; - - *app = ptr->app; - break; - } - - case CODEC_S_JPEG_APP_DATA: { /* set appn marker data */ - struct jpeg_app_marker *app = data; - - if (size != sizeof(struct jpeg_app_marker)) - return -EFAULT; - - ptr->app = *app; - break; - } - - case CODEC_G_JPEG_COM_DATA: { /* get comment marker data */ - struct jpeg_com_marker *com = data; - - if (size != sizeof(struct jpeg_com_marker)) - return -EFAULT; - - *com = ptr->com; - break; - } - - case CODEC_S_JPEG_COM_DATA: { /* set comment marker data */ - struct jpeg_com_marker *com = data; - - if (size != sizeof(struct jpeg_com_marker)) - return -EFAULT; - - ptr->com = *com; - break; - } - - default: - return -EINVAL; - } - - return size; -} - -/* ========================================================================= - Exit and unregister function: - - Deinitializes Zoran's JPEG processor - ========================================================================= */ - -static int zr36050_unset(struct videocodec *codec) -{ - struct zr36050 *ptr = codec->data; - struct zoran *zr = videocodec_to_zoran(codec); - - if (ptr) { - /* do wee need some codec deinit here, too ???? */ - - zrdev_dbg(zr, "%s: finished codec #%d\n", ptr->name, - ptr->num); - kfree(ptr); - codec->data = NULL; - - zr36050_codecs--; - return 0; - } - - return -EFAULT; -} - -/* ========================================================================= - Setup and registry function: - - Initializes Zoran's JPEG processor - - Also sets pixel size, average code size, mode (compr./decompr.) - (the given size is determined by the processor with the video interface) - ========================================================================= */ - -static int zr36050_setup(struct videocodec *codec) -{ - struct zr36050 *ptr; - struct zoran *zr = videocodec_to_zoran(codec); - int res; - - zrdev_dbg(zr, "zr36050: initializing MJPEG subsystem #%d.\n", - zr36050_codecs); - - if (zr36050_codecs == MAX_CODECS) { - zrdev_err(zr, - "zr36050: Can't attach more codecs!\n"); - return -ENOSPC; - } - //mem structure init - ptr = kzalloc(sizeof(*ptr), GFP_KERNEL); - codec->data = ptr; - if (!ptr) - return -ENOMEM; - - snprintf(ptr->name, sizeof(ptr->name), "zr36050[%d]", - zr36050_codecs); - ptr->num = zr36050_codecs++; - ptr->codec = codec; - - //testing - res = zr36050_basic_test(ptr); - if (res < 0) { - zr36050_unset(codec); - return res; - } - //final setup - memcpy(ptr->h_samp_ratio, zr36050_decimation_h, 8); - memcpy(ptr->v_samp_ratio, zr36050_decimation_v, 8); - - ptr->bitrate_ctrl = 0; /* 0 or 1 - fixed file size flag - * (what is the difference?) */ - ptr->mode = CODEC_DO_COMPRESSION; - ptr->width = 384; - ptr->height = 288; - ptr->total_code_vol = 16000; - ptr->max_block_vol = 240; - ptr->scalefact = 0x100; - ptr->dri = 1; - - /* no app/com marker by default */ - ptr->app.appn = 0; - ptr->app.len = 0; - ptr->com.len = 0; - - zr36050_init(ptr); - - zrdev_info(zr, "%s: codec attached and running\n", - ptr->name); - - return 0; -} - -static const struct videocodec zr36050_codec = { - .name = "zr36050", - .magic = 0L, // magic not used - .flags = - CODEC_FLAG_JPEG | CODEC_FLAG_HARDWARE | CODEC_FLAG_ENCODER | - CODEC_FLAG_DECODER, - .type = CODEC_TYPE_ZR36050, - .setup = zr36050_setup, // functionality - .unset = zr36050_unset, - .set_mode = zr36050_set_mode, - .set_video = zr36050_set_video, - .control = zr36050_control, - // others are not used -}; - -/* ========================================================================= - HOOK IN DRIVER AS KERNEL MODULE - ========================================================================= */ - -int zr36050_init_module(void) -{ - zr36050_codecs = 0; - return videocodec_register(&zr36050_codec); -} - -void zr36050_cleanup_module(void) -{ - if (zr36050_codecs) { - pr_debug("zr36050: something's wrong - %d codecs left somehow.\n", - zr36050_codecs); - } - videocodec_unregister(&zr36050_codec); -} diff --git a/drivers/staging/media/zoran/zr36050.h b/drivers/staging/media/zoran/zr36050.h deleted file mode 100644 index f9b58f4c77b9..000000000000 --- a/drivers/staging/media/zoran/zr36050.h +++ /dev/null @@ -1,165 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Zoran ZR36050 basic configuration functions - header file - * - * Copyright (C) 2001 Wolfgang Scherr <scherr@net4you.at> - */ - -#ifndef ZR36050_H -#define ZR36050_H - -#include "videocodec.h" - -/* data stored for each zoran jpeg codec chip */ -struct zr36050 { - char name[32]; - int num; - /* io datastructure */ - struct videocodec *codec; - // last coder status - __u8 status1; - // actual coder setup - int mode; - - __u16 width; - __u16 height; - - __u16 bitrate_ctrl; - - __u32 total_code_vol; - __u32 real_code_vol; - __u16 max_block_vol; - - __u8 h_samp_ratio[8]; - __u8 v_samp_ratio[8]; - __u16 scalefact; - __u16 dri; - - /* com/app marker */ - struct jpeg_com_marker com; - struct jpeg_app_marker app; -}; - -/* zr36050 register addresses */ -#define ZR050_GO 0x000 -#define ZR050_HARDWARE 0x002 -#define ZR050_MODE 0x003 -#define ZR050_OPTIONS 0x004 -#define ZR050_MBCV 0x005 -#define ZR050_MARKERS_EN 0x006 -#define ZR050_INT_REQ_0 0x007 -#define ZR050_INT_REQ_1 0x008 -#define ZR050_TCV_NET_HI 0x009 -#define ZR050_TCV_NET_MH 0x00a -#define ZR050_TCV_NET_ML 0x00b -#define ZR050_TCV_NET_LO 0x00c -#define ZR050_TCV_DATA_HI 0x00d -#define ZR050_TCV_DATA_MH 0x00e -#define ZR050_TCV_DATA_ML 0x00f -#define ZR050_TCV_DATA_LO 0x010 -#define ZR050_SF_HI 0x011 -#define ZR050_SF_LO 0x012 -#define ZR050_AF_HI 0x013 -#define ZR050_AF_M 0x014 -#define ZR050_AF_LO 0x015 -#define ZR050_ACV_HI 0x016 -#define ZR050_ACV_MH 0x017 -#define ZR050_ACV_ML 0x018 -#define ZR050_ACV_LO 0x019 -#define ZR050_ACT_HI 0x01a -#define ZR050_ACT_MH 0x01b -#define ZR050_ACT_ML 0x01c -#define ZR050_ACT_LO 0x01d -#define ZR050_ACV_TURN_HI 0x01e -#define ZR050_ACV_TURN_MH 0x01f -#define ZR050_ACV_TURN_ML 0x020 -#define ZR050_ACV_TURN_LO 0x021 -#define ZR050_STATUS_0 0x02e -#define ZR050_STATUS_1 0x02f - -#define ZR050_SOF_IDX 0x040 -#define ZR050_SOS1_IDX 0x07a -#define ZR050_SOS2_IDX 0x08a -#define ZR050_SOS3_IDX 0x09a -#define ZR050_SOS4_IDX 0x0aa -#define ZR050_DRI_IDX 0x0c0 -#define ZR050_DNL_IDX 0x0c6 -#define ZR050_DQT_IDX 0x0cc -#define ZR050_DHT_IDX 0x1d4 -#define ZR050_APP_IDX 0x380 -#define ZR050_COM_IDX 0x3c0 - -/* zr36050 hardware register bits */ - -#define ZR050_HW_BSWD 0x80 -#define ZR050_HW_MSTR 0x40 -#define ZR050_HW_DMA 0x20 -#define ZR050_HW_CFIS_1_CLK 0x00 -#define ZR050_HW_CFIS_2_CLK 0x04 -#define ZR050_HW_CFIS_3_CLK 0x08 -#define ZR050_HW_CFIS_4_CLK 0x0C -#define ZR050_HW_CFIS_5_CLK 0x10 -#define ZR050_HW_CFIS_6_CLK 0x14 -#define ZR050_HW_CFIS_7_CLK 0x18 -#define ZR050_HW_CFIS_8_CLK 0x1C -#define ZR050_HW_BELE 0x01 - -/* zr36050 mode register bits */ - -#define ZR050_MO_COMP 0x80 -#define ZR050_MO_ATP 0x40 -#define ZR050_MO_PASS2 0x20 -#define ZR050_MO_TLM 0x10 -#define ZR050_MO_DCONLY 0x08 -#define ZR050_MO_BRC 0x04 - -#define ZR050_MO_ATP 0x40 -#define ZR050_MO_PASS2 0x20 -#define ZR050_MO_TLM 0x10 -#define ZR050_MO_DCONLY 0x08 - -/* zr36050 option register bits */ - -#define ZR050_OP_NSCN_1 0x00 -#define ZR050_OP_NSCN_2 0x20 -#define ZR050_OP_NSCN_3 0x40 -#define ZR050_OP_NSCN_4 0x60 -#define ZR050_OP_NSCN_5 0x80 -#define ZR050_OP_NSCN_6 0xA0 -#define ZR050_OP_NSCN_7 0xC0 -#define ZR050_OP_NSCN_8 0xE0 -#define ZR050_OP_OVF 0x10 - -/* zr36050 markers-enable register bits */ - -#define ZR050_ME_APP 0x80 -#define ZR050_ME_COM 0x40 -#define ZR050_ME_DRI 0x20 -#define ZR050_ME_DQT 0x10 -#define ZR050_ME_DHT 0x08 -#define ZR050_ME_DNL 0x04 -#define ZR050_ME_DQTI 0x02 -#define ZR050_ME_DHTI 0x01 - -/* zr36050 status0/1 register bit masks */ - -#define ZR050_ST_RST_MASK 0x20 -#define ZR050_ST_SOF_MASK 0x02 -#define ZR050_ST_SOS_MASK 0x02 -#define ZR050_ST_DATRDY_MASK 0x80 -#define ZR050_ST_MRKDET_MASK 0x40 -#define ZR050_ST_RFM_MASK 0x10 -#define ZR050_ST_RFD_MASK 0x08 -#define ZR050_ST_END_MASK 0x04 -#define ZR050_ST_TCVOVF_MASK 0x02 -#define ZR050_ST_DATOVF_MASK 0x01 - -/* pixel component idx */ - -#define ZR050_Y_COMPONENT 0 -#define ZR050_U_COMPONENT 1 -#define ZR050_V_COMPONENT 2 - -int zr36050_init_module(void); -void zr36050_cleanup_module(void); -#endif /*fndef ZR36050_H */ diff --git a/drivers/staging/media/zoran/zr36057.h b/drivers/staging/media/zoran/zr36057.h deleted file mode 100644 index a2a75fd9f535..000000000000 --- a/drivers/staging/media/zoran/zr36057.h +++ /dev/null @@ -1,154 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * zr36057.h - zr36057 register offsets - * - * Copyright (C) 1998 Dave Perks <dperks@ibm.net> - */ - -#ifndef _ZR36057_H_ -#define _ZR36057_H_ - -/* Zoran ZR36057 registers */ - -#define ZR36057_VFEHCR 0x000 /* Video Front End, Horizontal Configuration Register */ -#define ZR36057_VFEHCR_HS_POL BIT(30) -#define ZR36057_VFEHCR_H_START 10 -#define ZR36057_VFEHCR_H_END 0 -#define ZR36057_VFEHCR_HMASK 0x3ff - -#define ZR36057_VFEVCR 0x004 /* Video Front End, Vertical Configuration Register */ -#define ZR36057_VFEVCR_VS_POL BIT(30) -#define ZR36057_VFEVCR_V_START 10 -#define ZR36057_VFEVCR_V_END 0 -#define ZR36057_VFEVCR_VMASK 0x3ff - -#define ZR36057_VFESPFR 0x008 /* Video Front End, Scaler and Pixel Format Register */ -#define ZR36057_VFESPFR_EXT_FL BIT(26) -#define ZR36057_VFESPFR_TOP_FIELD BIT(25) -#define ZR36057_VFESPFR_VCLK_POL BIT(24) -#define ZR36057_VFESPFR_H_FILTER 21 -#define ZR36057_VFESPFR_HOR_DCM 14 -#define ZR36057_VFESPFR_VER_DCM 8 -#define ZR36057_VFESPFR_DISP_MODE 6 -#define ZR36057_VFESPFR_YUV422 (0 << 3) -#define ZR36057_VFESPFR_RGB888 (1 << 3) -#define ZR36057_VFESPFR_RGB565 (2 << 3) -#define ZR36057_VFESPFR_RGB555 (3 << 3) -#define ZR36057_VFESPFR_ERR_DIF (1 << 2) -#define ZR36057_VFESPFR_PACK24 (1 << 1) -#define ZR36057_VFESPFR_LITTLE_ENDIAN (1 << 0) - -#define ZR36057_VDTR 0x00c /* Video Display "Top" Register */ - -#define ZR36057_VDBR 0x010 /* Video Display "Bottom" Register */ - -#define ZR36057_VSSFGR 0x014 /* Video Stride, Status, and Frame Grab Register */ -#define ZR36057_VSSFGR_DISP_STRIDE 16 -#define ZR36057_VSSFGR_VID_OVF BIT(8) -#define ZR36057_VSSFGR_SNAP_SHOT BIT(1) -#define ZR36057_VSSFGR_FRAME_GRAB BIT(0) - -#define ZR36057_VDCR 0x018 /* Video Display Configuration Register */ -#define ZR36057_VDCR_VID_EN BIT(31) -#define ZR36057_VDCR_MIN_PIX 24 -#define ZR36057_VDCR_TRITON BIT(24) -#define ZR36057_VDCR_VID_WIN_HT 12 -#define ZR36057_VDCR_VID_WIN_WID 0 - -#define ZR36057_MMTR 0x01c /* Masking Map "Top" Register */ - -#define ZR36057_MMBR 0x020 /* Masking Map "Bottom" Register */ - -#define ZR36057_OCR 0x024 /* Overlay Control Register */ -#define ZR36057_OCR_OVL_ENABLE BIT(15) -#define ZR36057_OCR_MASK_STRIDE 0 - -#define ZR36057_SPGPPCR 0x028 /* System, PCI, and General Purpose Pins Control Register */ -#define ZR36057_SPGPPCR_SOFT_RESET BIT(24) - -#define ZR36057_GPPGCR1 0x02c /* General Purpose Pins and GuestBus Control Register (1) */ - -#define ZR36057_MCSAR 0x030 /* MPEG Code Source Address Register */ - -#define ZR36057_MCTCR 0x034 /* MPEG Code Transfer Control Register */ -#define ZR36057_MCTCR_COD_TIME BIT(30) -#define ZR36057_MCTCR_C_EMPTY BIT(29) -#define ZR36057_MCTCR_C_FLUSH BIT(28) -#define ZR36057_MCTCR_COD_GUEST_ID 20 -#define ZR36057_MCTCR_COD_GUEST_REG 16 - -#define ZR36057_MCMPR 0x038 /* MPEG Code Memory Pointer Register */ - -#define ZR36057_ISR 0x03c /* Interrupt Status Register */ -#define ZR36057_ISR_GIRQ1 BIT(30) -#define ZR36057_ISR_GIRQ0 BIT(29) -#define ZR36057_ISR_COD_REP_IRQ BIT(28) -#define ZR36057_ISR_JPEG_REP_IRQ BIT(27) - -#define ZR36057_ICR 0x040 /* Interrupt Control Register */ -#define ZR36057_ICR_GIRQ1 BIT(30) -#define ZR36057_ICR_GIRQ0 BIT(29) -#define ZR36057_ICR_COD_REP_IRQ BIT(28) -#define ZR36057_ICR_JPEG_REP_IRQ BIT(27) -#define ZR36057_ICR_INT_PIN_EN BIT(24) - -#define ZR36057_I2CBR 0x044 /* I2C Bus Register */ -#define ZR36057_I2CBR_SDA BIT(1) -#define ZR36057_I2CBR_SCL BIT(0) - -#define ZR36057_JMC 0x100 /* JPEG Mode and Control */ -#define ZR36057_JMC_JPG BIT(31) -#define ZR36057_JMC_JPG_EXP_MODE (0 << 29) -#define ZR36057_JMC_JPG_CMP_MODE BIT(29) -#define ZR36057_JMC_MJPG_EXP_MODE (2 << 29) -#define ZR36057_JMC_MJPG_CMP_MODE (3 << 29) -#define ZR36057_JMC_RTBUSY_FB BIT(6) -#define ZR36057_JMC_GO_EN BIT(5) -#define ZR36057_JMC_SYNC_MSTR BIT(4) -#define ZR36057_JMC_FLD_PER_BUFF BIT(3) -#define ZR36057_JMC_VFIFO_FB BIT(2) -#define ZR36057_JMC_CFIFO_FB BIT(1) -#define ZR36057_JMC_STLL_LIT_ENDIAN BIT(0) - -#define ZR36057_JPC 0x104 /* JPEG Process Control */ -#define ZR36057_JPC_P_RESET BIT(7) -#define ZR36057_JPC_COD_TRNS_EN BIT(5) -#define ZR36057_JPC_ACTIVE BIT(0) - -#define ZR36057_VSP 0x108 /* Vertical Sync Parameters */ -#define ZR36057_VSP_VSYNC_SIZE 16 -#define ZR36057_VSP_FRM_TOT 0 - -#define ZR36057_HSP 0x10c /* Horizontal Sync Parameters */ -#define ZR36057_HSP_HSYNC_START 16 -#define ZR36057_HSP_LINE_TOT 0 - -#define ZR36057_FHAP 0x110 /* Field Horizontal Active Portion */ -#define ZR36057_FHAP_NAX 16 -#define ZR36057_FHAP_PAX 0 - -#define ZR36057_FVAP 0x114 /* Field Vertical Active Portion */ -#define ZR36057_FVAP_NAY 16 -#define ZR36057_FVAP_PAY 0 - -#define ZR36057_FPP 0x118 /* Field Process Parameters */ -#define ZR36057_FPP_ODD_EVEN BIT(0) - -#define ZR36057_JCBA 0x11c /* JPEG Code Base Address */ - -#define ZR36057_JCFT 0x120 /* JPEG Code FIFO Threshold */ - -#define ZR36057_JCGI 0x124 /* JPEG Codec Guest ID */ -#define ZR36057_JCGI_JPE_GUEST_ID 4 -#define ZR36057_JCGI_JPE_GUEST_REG 0 - -#define ZR36057_GCR2 0x12c /* GuestBus Control Register (2) */ - -#define ZR36057_POR 0x200 /* Post Office Register */ -#define ZR36057_POR_PO_PEN BIT(25) -#define ZR36057_POR_PO_TIME BIT(24) -#define ZR36057_POR_PO_DIR BIT(23) - -#define ZR36057_STR 0x300 /* "Still" Transfer Register */ - -#endif diff --git a/drivers/staging/media/zoran/zr36060.c b/drivers/staging/media/zoran/zr36060.c deleted file mode 100644 index 7798016f1f96..000000000000 --- a/drivers/staging/media/zoran/zr36060.c +++ /dev/null @@ -1,869 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Zoran ZR36060 basic configuration functions - * - * Copyright (C) 2002 Laurent Pinchart <laurent.pinchart@skynet.be> - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/delay.h> - -#include <linux/types.h> -#include <linux/wait.h> - -/* I/O commands, error codes */ -#include <linux/io.h> - -/* headerfile of this module */ -#include "zr36060.h" - -/* codec io API */ -#include "videocodec.h" - -/* it doesn't make sense to have more than 20 or so, just to prevent some unwanted loops */ -#define MAX_CODECS 20 - -/* amount of chips attached via this driver */ -static int zr36060_codecs; - -static bool low_bitrate; -module_param(low_bitrate, bool, 0); -MODULE_PARM_DESC(low_bitrate, "Buz compatibility option, halves bitrate"); - -/* ========================================================================= - * Local hardware I/O functions: - * read/write via codec layer (registers are located in the master device) - * ========================================================================= - */ - -static u8 zr36060_read(struct zr36060 *ptr, u16 reg) -{ - u8 value = 0; - struct zoran *zr = videocodec_to_zoran(ptr->codec); - - // just in case something is wrong... - if (ptr->codec->master_data->readreg) - value = (ptr->codec->master_data->readreg(ptr->codec, reg)) & 0xff; - else - zrdev_err(zr, "%s: invalid I/O setup, nothing read!\n", ptr->name); - - return value; -} - -static void zr36060_write(struct zr36060 *ptr, u16 reg, u8 value) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - - zrdev_dbg(zr, "0x%02x @0x%04x\n", value, reg); - - // just in case something is wrong... - if (ptr->codec->master_data->writereg) - ptr->codec->master_data->writereg(ptr->codec, reg, value); - else - zrdev_err(zr, "%s: invalid I/O setup, nothing written!\n", ptr->name); -} - -/* ========================================================================= - * Local helper function: - * status read - * ========================================================================= - */ - -/* status is kept in datastructure */ -static u8 zr36060_read_status(struct zr36060 *ptr) -{ - ptr->status = zr36060_read(ptr, ZR060_CFSR); - - zr36060_read(ptr, 0); - return ptr->status; -} - -/* scale factor is kept in datastructure */ -static u16 zr36060_read_scalefactor(struct zr36060 *ptr) -{ - ptr->scalefact = (zr36060_read(ptr, ZR060_SF_HI) << 8) | - (zr36060_read(ptr, ZR060_SF_LO) & 0xFF); - - /* leave 0 selected for an eventually GO from master */ - zr36060_read(ptr, 0); - return ptr->scalefact; -} - -/* wait if codec is ready to proceed (end of processing) or time is over */ -static void zr36060_wait_end(struct zr36060 *ptr) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - int i = 0; - - while (zr36060_read_status(ptr) & ZR060_CFSR_BUSY) { - udelay(1); - if (i++ > 200000) { // 200ms, there is for sure something wrong!!! - zrdev_dbg(zr, - "%s: timeout at wait_end (last status: 0x%02x)\n", - ptr->name, ptr->status); - break; - } - } -} - -/* Basic test of "connectivity", writes/reads to/from memory the SOF marker */ -static int zr36060_basic_test(struct zr36060 *ptr) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - - if ((zr36060_read(ptr, ZR060_IDR_DEV) != 0x33) && - (zr36060_read(ptr, ZR060_IDR_REV) != 0x01)) { - zrdev_err(zr, "%s: attach failed, can't connect to jpeg processor!\n", ptr->name); - return -ENXIO; - } - - zr36060_wait_end(ptr); - if (ptr->status & ZR060_CFSR_BUSY) { - zrdev_err(zr, "%s: attach failed, jpeg processor failed (end flag)!\n", ptr->name); - return -EBUSY; - } - - return 0; /* looks good! */ -} - -/* simple loop for pushing the init datasets */ -static int zr36060_pushit(struct zr36060 *ptr, u16 startreg, u16 len, const char *data) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - int i = 0; - - zrdev_dbg(zr, "%s: write data block to 0x%04x (len=%d)\n", ptr->name, - startreg, len); - while (i < len) - zr36060_write(ptr, startreg++, data[i++]); - - return i; -} - -/* ========================================================================= - * Basic datasets: - * jpeg baseline setup data (you find it on lots places in internet, or just - * extract it from any regular .jpg image...) - * - * Could be variable, but until it's not needed it they are just fixed to save - * memory. Otherwise expand zr36060 structure with arrays, push the values to - * it and initialize from there, as e.g. the linux zr36057/60 driver does it. - * ========================================================================= - */ -static const char zr36060_dqt[0x86] = { - 0xff, 0xdb, //Marker: DQT - 0x00, 0x84, //Length: 2*65+2 - 0x00, //Pq,Tq first table - 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, - 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, - 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, - 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33, - 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44, - 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, - 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, - 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63, - 0x01, //Pq,Tq second table - 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, - 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63 -}; - -static const char zr36060_dht[0x1a4] = { - 0xff, 0xc4, //Marker: DHT - 0x01, 0xa2, //Length: 2*AC, 2*DC - 0x00, //DC first table - 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, - 0x01, //DC second table - 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, - 0x10, //AC first table - 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, - 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, - 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, - 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, - 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, - 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, - 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, - 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, - 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, - 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, - 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, - 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, - 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, - 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, - 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, - 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, - 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, - 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, - 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, - 0xF8, 0xF9, 0xFA, - 0x11, //AC second table - 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, - 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, - 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, - 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, - 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, - 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, - 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, - 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, - 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, - 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, - 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, - 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, - 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, - 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, - 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, - 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, - 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, - 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, - 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, - 0xF9, 0xFA -}; - -/* jpeg baseline setup, this is just fixed in this driver (YUV pictures) */ -#define NO_OF_COMPONENTS 0x3 //Y,U,V -#define BASELINE_PRECISION 0x8 //MCU size (?) -static const char zr36060_tq[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's QT -static const char zr36060_td[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's DC -static const char zr36060_ta[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's AC - -/* horizontal 422 decimation setup (maybe we support 411 or so later, too) */ -static const char zr36060_decimation_h[8] = { 2, 1, 1, 0, 0, 0, 0, 0 }; -static const char zr36060_decimation_v[8] = { 1, 1, 1, 0, 0, 0, 0, 0 }; - -/* SOF (start of frame) segment depends on width, height and sampling ratio of each color component */ -static int zr36060_set_sof(struct zr36060 *ptr) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - char sof_data[34]; // max. size of register set - int i; - - zrdev_dbg(zr, "%s: write SOF (%dx%d, %d components)\n", ptr->name, - ptr->width, ptr->height, NO_OF_COMPONENTS); - sof_data[0] = 0xff; - sof_data[1] = 0xc0; - sof_data[2] = 0x00; - sof_data[3] = (3 * NO_OF_COMPONENTS) + 8; - sof_data[4] = BASELINE_PRECISION; // only '8' possible with zr36060 - sof_data[5] = (ptr->height) >> 8; - sof_data[6] = (ptr->height) & 0xff; - sof_data[7] = (ptr->width) >> 8; - sof_data[8] = (ptr->width) & 0xff; - sof_data[9] = NO_OF_COMPONENTS; - for (i = 0; i < NO_OF_COMPONENTS; i++) { - sof_data[10 + (i * 3)] = i; // index identifier - sof_data[11 + (i * 3)] = (ptr->h_samp_ratio[i] << 4) | - (ptr->v_samp_ratio[i]); // sampling ratios - sof_data[12 + (i * 3)] = zr36060_tq[i]; // Q table selection - } - return zr36060_pushit(ptr, ZR060_SOF_IDX, - (3 * NO_OF_COMPONENTS) + 10, sof_data); -} - -/* SOS (start of scan) segment depends on the used scan components of each color component */ -static int zr36060_set_sos(struct zr36060 *ptr) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - char sos_data[16]; // max. size of register set - int i; - - zrdev_dbg(zr, "%s: write SOS\n", ptr->name); - sos_data[0] = 0xff; - sos_data[1] = 0xda; - sos_data[2] = 0x00; - sos_data[3] = 2 + 1 + (2 * NO_OF_COMPONENTS) + 3; - sos_data[4] = NO_OF_COMPONENTS; - for (i = 0; i < NO_OF_COMPONENTS; i++) { - sos_data[5 + (i * 2)] = i; // index - sos_data[6 + (i * 2)] = (zr36060_td[i] << 4) | - zr36060_ta[i]; // AC/DC tbl.sel. - } - sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 2] = 00; // scan start - sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 3] = 0x3f; - sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 4] = 00; - return zr36060_pushit(ptr, ZR060_SOS_IDX, - 4 + 1 + (2 * NO_OF_COMPONENTS) + 3, - sos_data); -} - -/* DRI (define restart interval) */ -static int zr36060_set_dri(struct zr36060 *ptr) -{ - struct zoran *zr = videocodec_to_zoran(ptr->codec); - char dri_data[6]; // max. size of register set - - zrdev_dbg(zr, "%s: write DRI\n", ptr->name); - dri_data[0] = 0xff; - dri_data[1] = 0xdd; - dri_data[2] = 0x00; - dri_data[3] = 0x04; - dri_data[4] = (ptr->dri) >> 8; - dri_data[5] = (ptr->dri) & 0xff; - return zr36060_pushit(ptr, ZR060_DRI_IDX, 6, dri_data); -} - -/* Setup compression/decompression of Zoran's JPEG processor ( see also zoran 36060 manual ) - * ... sorry for the spaghetti code ... - */ -static void zr36060_init(struct zr36060 *ptr) -{ - int sum = 0; - long bitcnt, tmp; - struct zoran *zr = videocodec_to_zoran(ptr->codec); - - if (ptr->mode == CODEC_DO_COMPRESSION) { - zrdev_dbg(zr, "%s: COMPRESSION SETUP\n", ptr->name); - - zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SYNC_RST); - - /* 060 communicates with 067 in master mode */ - zr36060_write(ptr, ZR060_CIR, ZR060_CIR_CODE_MSTR); - - /* Compression with or without variable scale factor */ - /*FIXME: What about ptr->bitrate_ctrl? */ - zr36060_write(ptr, ZR060_CMR, ZR060_CMR_COMP | ZR060_CMR_PASS2 | ZR060_CMR_BRB); - - /* Must be zero */ - zr36060_write(ptr, ZR060_MBZ, 0x00); - zr36060_write(ptr, ZR060_TCR_HI, 0x00); - zr36060_write(ptr, ZR060_TCR_LO, 0x00); - - /* Disable all IRQs - no DataErr means autoreset */ - zr36060_write(ptr, ZR060_IMR, 0); - - /* volume control settings */ - zr36060_write(ptr, ZR060_SF_HI, ptr->scalefact >> 8); - zr36060_write(ptr, ZR060_SF_LO, ptr->scalefact & 0xff); - - zr36060_write(ptr, ZR060_AF_HI, 0xff); - zr36060_write(ptr, ZR060_AF_M, 0xff); - zr36060_write(ptr, ZR060_AF_LO, 0xff); - - /* setup the variable jpeg tables */ - sum += zr36060_set_sof(ptr); - sum += zr36060_set_sos(ptr); - sum += zr36060_set_dri(ptr); - -/* setup the fixed jpeg tables - maybe variable, though - (see table init section above) */ - sum += zr36060_pushit(ptr, ZR060_DQT_IDX, sizeof(zr36060_dqt), zr36060_dqt); - sum += zr36060_pushit(ptr, ZR060_DHT_IDX, sizeof(zr36060_dht), zr36060_dht); - zr36060_write(ptr, ZR060_APP_IDX, 0xff); - zr36060_write(ptr, ZR060_APP_IDX + 1, 0xe0 + ptr->app.appn); - zr36060_write(ptr, ZR060_APP_IDX + 2, 0x00); - zr36060_write(ptr, ZR060_APP_IDX + 3, ptr->app.len + 2); - sum += zr36060_pushit(ptr, ZR060_APP_IDX + 4, 60, ptr->app.data) + 4; - zr36060_write(ptr, ZR060_COM_IDX, 0xff); - zr36060_write(ptr, ZR060_COM_IDX + 1, 0xfe); - zr36060_write(ptr, ZR060_COM_IDX + 2, 0x00); - zr36060_write(ptr, ZR060_COM_IDX + 3, ptr->com.len + 2); - sum += zr36060_pushit(ptr, ZR060_COM_IDX + 4, 60, ptr->com.data) + 4; - - /* setup misc. data for compression (target code sizes) */ - - /* size of compressed code to reach without header data */ - sum = ptr->real_code_vol - sum; - bitcnt = sum << 3; /* need the size in bits */ - - tmp = bitcnt >> 16; - zrdev_dbg(zr, - "%s: code: csize=%d, tot=%d, bit=%ld, highbits=%ld\n", - ptr->name, sum, ptr->real_code_vol, bitcnt, tmp); - zr36060_write(ptr, ZR060_TCV_NET_HI, tmp >> 8); - zr36060_write(ptr, ZR060_TCV_NET_MH, tmp & 0xff); - tmp = bitcnt & 0xffff; - zr36060_write(ptr, ZR060_TCV_NET_ML, tmp >> 8); - zr36060_write(ptr, ZR060_TCV_NET_LO, tmp & 0xff); - - bitcnt -= bitcnt >> 7; // bits without stuffing - bitcnt -= ((bitcnt * 5) >> 6); // bits without eob - - tmp = bitcnt >> 16; - zrdev_dbg(zr, "%s: code: nettobit=%ld, highnettobits=%ld\n", - ptr->name, bitcnt, tmp); - zr36060_write(ptr, ZR060_TCV_DATA_HI, tmp >> 8); - zr36060_write(ptr, ZR060_TCV_DATA_MH, tmp & 0xff); - tmp = bitcnt & 0xffff; - zr36060_write(ptr, ZR060_TCV_DATA_ML, tmp >> 8); - zr36060_write(ptr, ZR060_TCV_DATA_LO, tmp & 0xff); - - /* JPEG markers to be included in the compressed stream */ - zr36060_write(ptr, ZR060_MER, - ZR060_MER_DQT | ZR060_MER_DHT | - ((ptr->com.len > 0) ? ZR060_MER_COM : 0) | - ((ptr->app.len > 0) ? ZR060_MER_APP : 0)); - - /* Setup the Video Frontend */ - /* Limit pixel range to 16..235 as per CCIR-601 */ - zr36060_write(ptr, ZR060_VCR, ZR060_VCR_RANGE); - - } else { - zrdev_dbg(zr, "%s: EXPANSION SETUP\n", ptr->name); - - zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SYNC_RST); - - /* 060 communicates with 067 in master mode */ - zr36060_write(ptr, ZR060_CIR, ZR060_CIR_CODE_MSTR); - - /* Decompression */ - zr36060_write(ptr, ZR060_CMR, 0); - - /* Must be zero */ - zr36060_write(ptr, ZR060_MBZ, 0x00); - zr36060_write(ptr, ZR060_TCR_HI, 0x00); - zr36060_write(ptr, ZR060_TCR_LO, 0x00); - - /* Disable all IRQs - no DataErr means autoreset */ - zr36060_write(ptr, ZR060_IMR, 0); - - /* setup misc. data for expansion */ - zr36060_write(ptr, ZR060_MER, 0); - -/* setup the fixed jpeg tables - maybe variable, though - (see table init section above) */ - zr36060_pushit(ptr, ZR060_DHT_IDX, sizeof(zr36060_dht), zr36060_dht); - - /* Setup the Video Frontend */ - //zr36060_write(ptr, ZR060_VCR, ZR060_VCR_FI_EXT); - //this doesn't seem right and doesn't work... - zr36060_write(ptr, ZR060_VCR, ZR060_VCR_RANGE); - } - - /* Load the tables */ - zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SYNC_RST | ZR060_LOAD_LOAD); - zr36060_wait_end(ptr); - zrdev_dbg(zr, "%s: Status after table preload: 0x%02x\n", - ptr->name, ptr->status); - - if (ptr->status & ZR060_CFSR_BUSY) { - zrdev_err(zr, "%s: init aborted!\n", ptr->name); - return; // something is wrong, its timed out!!!! - } -} - -/* ========================================================================= - * CODEC API FUNCTIONS - * this functions are accessed by the master via the API structure - * ========================================================================= - */ - -/* set compressiion/expansion mode and launches codec - - * this should be the last call from the master before starting processing - */ -static int zr36060_set_mode(struct videocodec *codec, int mode) -{ - struct zr36060 *ptr = (struct zr36060 *)codec->data; - struct zoran *zr = videocodec_to_zoran(codec); - - zrdev_dbg(zr, "%s: set_mode %d call\n", ptr->name, mode); - - if (mode != CODEC_DO_EXPANSION && mode != CODEC_DO_COMPRESSION) - return -EINVAL; - - ptr->mode = mode; - zr36060_init(ptr); - - return 0; -} - -/* set picture size (norm is ignored as the codec doesn't know about it) */ -static int zr36060_set_video(struct videocodec *codec, const struct tvnorm *norm, - struct vfe_settings *cap, struct vfe_polarity *pol) -{ - struct zr36060 *ptr = (struct zr36060 *)codec->data; - struct zoran *zr = videocodec_to_zoran(codec); - u32 reg; - int size; - - zrdev_dbg(zr, "%s: set_video %d/%d-%dx%d (%%%d) call\n", ptr->name, - cap->x, cap->y, cap->width, cap->height, cap->decimation); - - /* if () return -EINVAL; - * trust the master driver that it knows what it does - so - * we allow invalid startx/y and norm for now ... - */ - ptr->width = cap->width / (cap->decimation & 0xff); - ptr->height = cap->height / (cap->decimation >> 8); - - zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SYNC_RST); - - /* Note that VSPol/HSPol bits in zr36060 have the opposite - * meaning of their zr360x7 counterparts with the same names - * N.b. for VSPol this is only true if FIVEdge = 0 (default, - * left unchanged here - in accordance with datasheet). - */ - reg = (!pol->vsync_pol ? ZR060_VPR_VS_POL : 0) - | (!pol->hsync_pol ? ZR060_VPR_HS_POL : 0) - | (pol->field_pol ? ZR060_VPR_FI_POL : 0) - | (pol->blank_pol ? ZR060_VPR_BL_POL : 0) - | (pol->subimg_pol ? ZR060_VPR_S_IMG_POL : 0) - | (pol->poe_pol ? ZR060_VPR_POE_POL : 0) - | (pol->pvalid_pol ? ZR060_VPR_P_VAL_POL : 0) - | (pol->vclk_pol ? ZR060_VPR_VCLK_POL : 0); - zr36060_write(ptr, ZR060_VPR, reg); - - reg = 0; - switch (cap->decimation & 0xff) { - default: - case 1: - break; - - case 2: - reg |= ZR060_SR_H_SCALE2; - break; - - case 4: - reg |= ZR060_SR_H_SCALE4; - break; - } - - switch (cap->decimation >> 8) { - default: - case 1: - break; - - case 2: - reg |= ZR060_SR_V_SCALE; - break; - } - zr36060_write(ptr, ZR060_SR, reg); - - zr36060_write(ptr, ZR060_BCR_Y, 0x00); - zr36060_write(ptr, ZR060_BCR_U, 0x80); - zr36060_write(ptr, ZR060_BCR_V, 0x80); - - /* sync generator */ - - reg = norm->ht - 1; /* Vtotal */ - zr36060_write(ptr, ZR060_SGR_VTOTAL_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_SGR_VTOTAL_LO, (reg >> 0) & 0xff); - - reg = norm->wt - 1; /* Htotal */ - zr36060_write(ptr, ZR060_SGR_HTOTAL_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_SGR_HTOTAL_LO, (reg >> 0) & 0xff); - - reg = 6 - 1; /* VsyncSize */ - zr36060_write(ptr, ZR060_SGR_VSYNC, reg); - - //reg = 30 - 1; /* HsyncSize */ -///*CP*/ reg = (zr->params.norm == 1 ? 57 : 68); - reg = 68; - zr36060_write(ptr, ZR060_SGR_HSYNC, reg); - - reg = norm->v_start - 1; /* BVstart */ - zr36060_write(ptr, ZR060_SGR_BVSTART, reg); - - reg += norm->ha / 2; /* BVend */ - zr36060_write(ptr, ZR060_SGR_BVEND_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_SGR_BVEND_LO, (reg >> 0) & 0xff); - - reg = norm->h_start - 1; /* BHstart */ - zr36060_write(ptr, ZR060_SGR_BHSTART, reg); - - reg += norm->wa; /* BHend */ - zr36060_write(ptr, ZR060_SGR_BHEND_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_SGR_BHEND_LO, (reg >> 0) & 0xff); - - /* active area */ - reg = cap->y + norm->v_start; /* Vstart */ - zr36060_write(ptr, ZR060_AAR_VSTART_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_AAR_VSTART_LO, (reg >> 0) & 0xff); - - reg += cap->height; /* Vend */ - zr36060_write(ptr, ZR060_AAR_VEND_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_AAR_VEND_LO, (reg >> 0) & 0xff); - - reg = cap->x + norm->h_start; /* Hstart */ - zr36060_write(ptr, ZR060_AAR_HSTART_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_AAR_HSTART_LO, (reg >> 0) & 0xff); - - reg += cap->width; /* Hend */ - zr36060_write(ptr, ZR060_AAR_HEND_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_AAR_HEND_LO, (reg >> 0) & 0xff); - - /* subimage area */ - reg = norm->v_start - 4; /* SVstart */ - zr36060_write(ptr, ZR060_SWR_VSTART_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_SWR_VSTART_LO, (reg >> 0) & 0xff); - - reg += norm->ha / 2 + 8; /* SVend */ - zr36060_write(ptr, ZR060_SWR_VEND_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_SWR_VEND_LO, (reg >> 0) & 0xff); - - reg = norm->h_start /*+ 64 */ - 4; /* SHstart */ - zr36060_write(ptr, ZR060_SWR_HSTART_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_SWR_HSTART_LO, (reg >> 0) & 0xff); - - reg += norm->wa + 8; /* SHend */ - zr36060_write(ptr, ZR060_SWR_HEND_HI, (reg >> 8) & 0xff); - zr36060_write(ptr, ZR060_SWR_HEND_LO, (reg >> 0) & 0xff); - - size = ptr->width * ptr->height; - /* Target compressed field size in bits: */ - size = size * 16; /* uncompressed size in bits */ - /* (Ronald) by default, quality = 100 is a compression - * ratio 1:2. Setting low_bitrate (insmod option) sets - * it to 1:4 (instead of 1:2, zr36060 max) as limit because the - * buz can't handle more at decimation=1... Use low_bitrate if - * you have a Buz, unless you know what you're doing - */ - size = size * cap->quality / (low_bitrate ? 400 : 200); - /* Lower limit (arbitrary, 1 KB) */ - if (size < 8192) - size = 8192; - /* Upper limit: 7/8 of the code buffers */ - if (size > ptr->total_code_vol * 7) - size = ptr->total_code_vol * 7; - - ptr->real_code_vol = size >> 3; /* in bytes */ - - /* the MBCVR is the *maximum* block volume, according to the - * JPEG ISO specs, this shouldn't be used, since that allows - * for the best encoding quality. So set it to it's max value - */ - reg = ptr->max_block_vol; - zr36060_write(ptr, ZR060_MBCVR, reg); - - return 0; -} - -/* additional control functions */ -static int zr36060_control(struct videocodec *codec, int type, int size, void *data) -{ - struct zr36060 *ptr = (struct zr36060 *)codec->data; - struct zoran *zr = videocodec_to_zoran(codec); - int *ival = (int *)data; - - zrdev_dbg(zr, "%s: control %d call with %d byte\n", ptr->name, type, - size); - - switch (type) { - case CODEC_G_STATUS: /* get last status */ - if (size != sizeof(int)) - return -EFAULT; - zr36060_read_status(ptr); - *ival = ptr->status; - break; - - case CODEC_G_CODEC_MODE: - if (size != sizeof(int)) - return -EFAULT; - *ival = CODEC_MODE_BJPG; - break; - - case CODEC_S_CODEC_MODE: - if (size != sizeof(int)) - return -EFAULT; - if (*ival != CODEC_MODE_BJPG) - return -EINVAL; - /* not needed, do nothing */ - return 0; - - case CODEC_G_VFE: - case CODEC_S_VFE: - /* not needed, do nothing */ - return 0; - - case CODEC_S_MMAP: - /* not available, give an error */ - return -ENXIO; - - case CODEC_G_JPEG_TDS_BYTE: /* get target volume in byte */ - if (size != sizeof(int)) - return -EFAULT; - *ival = ptr->total_code_vol; - break; - - case CODEC_S_JPEG_TDS_BYTE: /* get target volume in byte */ - if (size != sizeof(int)) - return -EFAULT; - ptr->total_code_vol = *ival; - ptr->real_code_vol = (ptr->total_code_vol * 6) >> 3; - break; - - case CODEC_G_JPEG_SCALE: /* get scaling factor */ - if (size != sizeof(int)) - return -EFAULT; - *ival = zr36060_read_scalefactor(ptr); - break; - - case CODEC_S_JPEG_SCALE: /* set scaling factor */ - if (size != sizeof(int)) - return -EFAULT; - ptr->scalefact = *ival; - break; - - case CODEC_G_JPEG_APP_DATA: { /* get appn marker data */ - struct jpeg_app_marker *app = data; - - if (size != sizeof(struct jpeg_app_marker)) - return -EFAULT; - - *app = ptr->app; - break; - } - - case CODEC_S_JPEG_APP_DATA: { /* set appn marker data */ - struct jpeg_app_marker *app = data; - - if (size != sizeof(struct jpeg_app_marker)) - return -EFAULT; - - ptr->app = *app; - break; - } - - case CODEC_G_JPEG_COM_DATA: { /* get comment marker data */ - struct jpeg_com_marker *com = data; - - if (size != sizeof(struct jpeg_com_marker)) - return -EFAULT; - - *com = ptr->com; - break; - } - - case CODEC_S_JPEG_COM_DATA: { /* set comment marker data */ - struct jpeg_com_marker *com = data; - - if (size != sizeof(struct jpeg_com_marker)) - return -EFAULT; - - ptr->com = *com; - break; - } - - default: - return -EINVAL; - } - - return size; -} - -/* ========================================================================= - * Exit and unregister function: - * Deinitializes Zoran's JPEG processor - * ========================================================================= - */ -static int zr36060_unset(struct videocodec *codec) -{ - struct zr36060 *ptr = codec->data; - struct zoran *zr = videocodec_to_zoran(codec); - - if (ptr) { - /* do wee need some codec deinit here, too ???? */ - - zrdev_dbg(zr, "%s: finished codec #%d\n", ptr->name, ptr->num); - kfree(ptr); - codec->data = NULL; - - zr36060_codecs--; - return 0; - } - - return -EFAULT; -} - -/* ========================================================================= - * Setup and registry function: - * Initializes Zoran's JPEG processor - * Also sets pixel size, average code size, mode (compr./decompr.) - * (the given size is determined by the processor with the video interface) - * ========================================================================= - */ -static int zr36060_setup(struct videocodec *codec) -{ - struct zr36060 *ptr; - struct zoran *zr = videocodec_to_zoran(codec); - int res; - - zrdev_dbg(zr, "zr36060: initializing MJPEG subsystem #%d.\n", - zr36060_codecs); - - if (zr36060_codecs == MAX_CODECS) { - zrdev_err(zr, "zr36060: Can't attach more codecs!\n"); - return -ENOSPC; - } - //mem structure init - ptr = kzalloc(sizeof(*ptr), GFP_KERNEL); - codec->data = ptr; - if (!ptr) - return -ENOMEM; - - snprintf(ptr->name, sizeof(ptr->name), "zr36060[%d]", zr36060_codecs); - ptr->num = zr36060_codecs++; - ptr->codec = codec; - - //testing - res = zr36060_basic_test(ptr); - if (res < 0) { - zr36060_unset(codec); - return res; - } - //final setup - memcpy(ptr->h_samp_ratio, zr36060_decimation_h, 8); - memcpy(ptr->v_samp_ratio, zr36060_decimation_v, 8); - - ptr->bitrate_ctrl = 0; /* 0 or 1 - fixed file size flag (what is the difference?) */ - ptr->mode = CODEC_DO_COMPRESSION; - ptr->width = 384; - ptr->height = 288; - ptr->total_code_vol = 16000; /* CHECKME */ - ptr->real_code_vol = (ptr->total_code_vol * 6) >> 3; - ptr->max_block_vol = 240; /* CHECKME, was 120 is 240 */ - ptr->scalefact = 0x100; - ptr->dri = 1; /* CHECKME, was 8 is 1 */ - - /* by default, no COM or APP markers - app should set those */ - ptr->com.len = 0; - ptr->app.appn = 0; - ptr->app.len = 0; - - zr36060_init(ptr); - - zrdev_info(zr, "%s: codec attached and running\n", ptr->name); - - return 0; -} - -static const struct videocodec zr36060_codec = { - .name = "zr36060", - .magic = 0L, // magic not used - .flags = - CODEC_FLAG_JPEG | CODEC_FLAG_HARDWARE | CODEC_FLAG_ENCODER | - CODEC_FLAG_DECODER | CODEC_FLAG_VFE, - .type = CODEC_TYPE_ZR36060, - .setup = zr36060_setup, // functionality - .unset = zr36060_unset, - .set_mode = zr36060_set_mode, - .set_video = zr36060_set_video, - .control = zr36060_control, - // others are not used -}; - -int zr36060_init_module(void) -{ - zr36060_codecs = 0; - return videocodec_register(&zr36060_codec); -} - -void zr36060_cleanup_module(void) -{ - if (zr36060_codecs) { - pr_debug("zr36060: something's wrong - %d codecs left somehow.\n", - zr36060_codecs); - } - - /* however, we can't just stay alive */ - videocodec_unregister(&zr36060_codec); -} diff --git a/drivers/staging/media/zoran/zr36060.h b/drivers/staging/media/zoran/zr36060.h deleted file mode 100644 index fbf5429534ac..000000000000 --- a/drivers/staging/media/zoran/zr36060.h +++ /dev/null @@ -1,203 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Zoran ZR36060 basic configuration functions - header file - * - * Copyright (C) 2002 Laurent Pinchart <laurent.pinchart@skynet.be> - */ - -#ifndef ZR36060_H -#define ZR36060_H - -#include "videocodec.h" - -/* data stored for each zoran jpeg codec chip */ -struct zr36060 { - char name[32]; - int num; - /* io datastructure */ - struct videocodec *codec; - // last coder status - __u8 status; - // actual coder setup - int mode; - - __u16 width; - __u16 height; - - __u16 bitrate_ctrl; - - __u32 total_code_vol; - __u32 real_code_vol; - __u16 max_block_vol; - - __u8 h_samp_ratio[8]; - __u8 v_samp_ratio[8]; - __u16 scalefact; - __u16 dri; - - /* app/com marker data */ - struct jpeg_app_marker app; - struct jpeg_com_marker com; -}; - -/* ZR36060 register addresses */ -#define ZR060_LOAD 0x000 -#define ZR060_CFSR 0x001 -#define ZR060_CIR 0x002 -#define ZR060_CMR 0x003 -#define ZR060_MBZ 0x004 -#define ZR060_MBCVR 0x005 -#define ZR060_MER 0x006 -#define ZR060_IMR 0x007 -#define ZR060_ISR 0x008 -#define ZR060_TCV_NET_HI 0x009 -#define ZR060_TCV_NET_MH 0x00a -#define ZR060_TCV_NET_ML 0x00b -#define ZR060_TCV_NET_LO 0x00c -#define ZR060_TCV_DATA_HI 0x00d -#define ZR060_TCV_DATA_MH 0x00e -#define ZR060_TCV_DATA_ML 0x00f -#define ZR060_TCV_DATA_LO 0x010 -#define ZR060_SF_HI 0x011 -#define ZR060_SF_LO 0x012 -#define ZR060_AF_HI 0x013 -#define ZR060_AF_M 0x014 -#define ZR060_AF_LO 0x015 -#define ZR060_ACV_HI 0x016 -#define ZR060_ACV_MH 0x017 -#define ZR060_ACV_ML 0x018 -#define ZR060_ACV_LO 0x019 -#define ZR060_ACT_HI 0x01a -#define ZR060_ACT_MH 0x01b -#define ZR060_ACT_ML 0x01c -#define ZR060_ACT_LO 0x01d -#define ZR060_ACV_TURN_HI 0x01e -#define ZR060_ACV_TURN_MH 0x01f -#define ZR060_ACV_TURN_ML 0x020 -#define ZR060_ACV_TURN_LO 0x021 -#define ZR060_IDR_DEV 0x022 -#define ZR060_IDR_REV 0x023 -#define ZR060_TCR_HI 0x024 -#define ZR060_TCR_LO 0x025 -#define ZR060_VCR 0x030 -#define ZR060_VPR 0x031 -#define ZR060_SR 0x032 -#define ZR060_BCR_Y 0x033 -#define ZR060_BCR_U 0x034 -#define ZR060_BCR_V 0x035 -#define ZR060_SGR_VTOTAL_HI 0x036 -#define ZR060_SGR_VTOTAL_LO 0x037 -#define ZR060_SGR_HTOTAL_HI 0x038 -#define ZR060_SGR_HTOTAL_LO 0x039 -#define ZR060_SGR_VSYNC 0x03a -#define ZR060_SGR_HSYNC 0x03b -#define ZR060_SGR_BVSTART 0x03c -#define ZR060_SGR_BHSTART 0x03d -#define ZR060_SGR_BVEND_HI 0x03e -#define ZR060_SGR_BVEND_LO 0x03f -#define ZR060_SGR_BHEND_HI 0x040 -#define ZR060_SGR_BHEND_LO 0x041 -#define ZR060_AAR_VSTART_HI 0x042 -#define ZR060_AAR_VSTART_LO 0x043 -#define ZR060_AAR_VEND_HI 0x044 -#define ZR060_AAR_VEND_LO 0x045 -#define ZR060_AAR_HSTART_HI 0x046 -#define ZR060_AAR_HSTART_LO 0x047 -#define ZR060_AAR_HEND_HI 0x048 -#define ZR060_AAR_HEND_LO 0x049 -#define ZR060_SWR_VSTART_HI 0x04a -#define ZR060_SWR_VSTART_LO 0x04b -#define ZR060_SWR_VEND_HI 0x04c -#define ZR060_SWR_VEND_LO 0x04d -#define ZR060_SWR_HSTART_HI 0x04e -#define ZR060_SWR_HSTART_LO 0x04f -#define ZR060_SWR_HEND_HI 0x050 -#define ZR060_SWR_HEND_LO 0x051 - -#define ZR060_SOF_IDX 0x060 -#define ZR060_SOS_IDX 0x07a -#define ZR060_DRI_IDX 0x0c0 -#define ZR060_DQT_IDX 0x0cc -#define ZR060_DHT_IDX 0x1d4 -#define ZR060_APP_IDX 0x380 -#define ZR060_COM_IDX 0x3c0 - -/* ZR36060 LOAD register bits */ - -#define ZR060_LOAD_LOAD BIT(7) -#define ZR060_LOAD_SYNC_RST BIT(0) - -/* ZR36060 Code FIFO Status register bits */ - -#define ZR060_CFSR_BUSY BIT(7) -#define ZR060_CFSR_C_BUSY BIT(2) -#define ZR060_CFSR_CFIFO (3 << 0) - -/* ZR36060 Code Interface register */ - -#define ZR060_CIR_CODE16 BIT(7) -#define ZR060_CIR_ENDIAN BIT(6) -#define ZR060_CIR_CFIS BIT(2) -#define ZR060_CIR_CODE_MSTR BIT(0) - -/* ZR36060 Codec Mode register */ - -#define ZR060_CMR_COMP BIT(7) -#define ZR060_CMR_ATP BIT(6) -#define ZR060_CMR_PASS2 BIT(5) -#define ZR060_CMR_TLM BIT(4) -#define ZR060_CMR_BRB BIT(2) -#define ZR060_CMR_FSF BIT(1) - -/* ZR36060 Markers Enable register */ - -#define ZR060_MER_APP BIT(7) -#define ZR060_MER_COM BIT(6) -#define ZR060_MER_DRI BIT(5) -#define ZR060_MER_DQT BIT(4) -#define ZR060_MER_DHT BIT(3) - -/* ZR36060 Interrupt Mask register */ - -#define ZR060_IMR_EOAV BIT(3) -#define ZR060_IMR_EOI BIT(2) -#define ZR060_IMR_END BIT(1) -#define ZR060_IMR_DATA_ERR BIT(0) - -/* ZR36060 Interrupt Status register */ - -#define ZR060_ISR_PRO_CNT (3 << 6) -#define ZR060_ISR_EOAV BIT(3) -#define ZR060_ISR_EOI BIT(2) -#define ZR060_ISR_END BIT(1) -#define ZR060_ISR_DATA_ERR BIT(0) - -/* ZR36060 Video Control register */ - -#define ZR060_VCR_VIDEO8 BIT(7) -#define ZR060_VCR_RANGE BIT(6) -#define ZR060_VCR_FI_DET BIT(3) -#define ZR060_VCR_FI_VEDGE BIT(2) -#define ZR060_VCR_FI_EXT BIT(1) -#define ZR060_VCR_SYNC_MSTR BIT(0) - -/* ZR36060 Video Polarity register */ - -#define ZR060_VPR_VCLK_POL BIT(7) -#define ZR060_VPR_P_VAL_POL BIT(6) -#define ZR060_VPR_POE_POL BIT(5) -#define ZR060_VPR_S_IMG_POL BIT(4) -#define ZR060_VPR_BL_POL BIT(3) -#define ZR060_VPR_FI_POL BIT(2) -#define ZR060_VPR_HS_POL BIT(1) -#define ZR060_VPR_VS_POL BIT(0) - -/* ZR36060 Scaling register */ - -#define ZR060_SR_V_SCALE BIT(2) -#define ZR060_SR_H_SCALE2 BIT(0) -#define ZR060_SR_H_SCALE4 (2 << 0) - -int zr36060_init_module(void); -void zr36060_cleanup_module(void); -#endif /*fndef ZR36060_H */ |