diff options
344 files changed, 18260 insertions, 4319 deletions
diff --git a/Documentation/ABI/testing/debugfs-cec-error-inj b/Documentation/ABI/testing/debugfs-cec-error-inj index 122b65c5fe62..4c3596c6d25b 100644 --- a/Documentation/ABI/testing/debugfs-cec-error-inj +++ b/Documentation/ABI/testing/debugfs-cec-error-inj @@ -1,6 +1,6 @@ What: /sys/kernel/debug/cec/*/error-inj Date: March 2018 -Contact: Hans Verkuil <hans.verkuil@cisco.com> +Contact: Hans Verkuil <hverkuil-cisco@xs4all.nl> Description: The CEC Framework allows for CEC error injection commands through diff --git a/Documentation/devicetree/bindings/media/allegro.txt b/Documentation/devicetree/bindings/media/allegro.txt new file mode 100644 index 000000000000..a92e2fbf26c9 --- /dev/null +++ b/Documentation/devicetree/bindings/media/allegro.txt @@ -0,0 +1,43 @@ +Device-tree bindings for the Allegro DVT video IP codecs present in the Xilinx +ZynqMP SoC. The IP core may either be a H.264/H.265 encoder or H.264/H.265 +decoder ip core. + +Each actual codec engines is controlled by a microcontroller (MCU). Host +software uses a provided mailbox interface to communicate with the MCU. The +MCU share an interrupt. + +Required properties: + - compatible: value should be one of the following + "allegro,al5e-1.1", "allegro,al5e": encoder IP core + "allegro,al5d-1.1", "allegro,al5d": decoder IP core + - reg: base and length of the memory mapped register region and base and + length of the memory mapped sram + - reg-names: must include "regs" and "sram" + - interrupts: shared interrupt from the MCUs to the processing system + - clocks: must contain an entry for each entry in clock-names + - clock-names: must include "core_clk", "mcu_clk", "m_axi_core_aclk", + "m_axi_mcu_aclk", "s_axi_lite_aclk" + +Example: + al5e: video-codec@a0009000 { + compatible = "allegro,al5e-1.1", "allegro,al5e"; + reg = <0 0xa0009000 0 0x1000>, + <0 0xa0000000 0 0x8000>; + reg-names = "regs", "sram"; + interrupts = <0 96 4>; + clocks = <&xlnx_vcu 0>, <&xlnx_vcu 1>, + <&clkc 71>, <&clkc 71>, <&clkc 71>; + clock-names = "core_clk", "mcu_clk", "m_axi_core_aclk", + "m_axi_mcu_aclk", "s_axi_lite_aclk" + }; + al5d: video-codec@a0029000 { + compatible = "allegro,al5d-1.1", "allegro,al5d"; + reg = <0 0xa0029000 0 0x1000>, + <0 0xa0020000 0 0x8000>; + reg-names = "regs", "sram"; + interrupts = <0 96 4>; + clocks = <&xlnx_vcu 2>, <&xlnx_vcu 3>, + <&clkc 71>, <&clkc 71>, <&clkc 71>; + clock-names = "core_clk", "mcu_clk", "m_axi_core_aclk", + "m_axi_mcu_aclk", "s_axi_lite_aclk" + }; diff --git a/Documentation/devicetree/bindings/media/amlogic,vdec.txt b/Documentation/devicetree/bindings/media/amlogic,vdec.txt new file mode 100644 index 000000000000..aabdd01bcf32 --- /dev/null +++ b/Documentation/devicetree/bindings/media/amlogic,vdec.txt @@ -0,0 +1,71 @@ +Amlogic Video Decoder +================================ + +The video decoding IP lies within the DOS memory region, +except for the hardware bitstream parser that makes use of an undocumented +region. + +It makes use of the following blocks: + +- ESPARSER is a bitstream parser that outputs to a VIFIFO. Further VDEC blocks +then feed from this VIFIFO. +- VDEC_1 can decode MPEG-1, MPEG-2, MPEG-4 part 2, MJPEG, H.263, H.264, VC-1. +- VDEC_HEVC can decode HEVC and VP9. + +Both VDEC_1 and VDEC_HEVC share the "vdec" IRQ and as such cannot run +concurrently. + +Device Tree Bindings: +--------------------- + +VDEC: Video Decoder +-------------------------- + +Required properties: +- compatible: value should be different for each SoC family as : + - GXBB (S905) : "amlogic,gxbb-vdec" + - GXL (S905X, S905D) : "amlogic,gxl-vdec" + - GXM (S912) : "amlogic,gxm-vdec" +- reg: base address and size of he following memory-mapped regions : + - dos + - esparser +- reg-names: should contain the names of the previous memory regions +- interrupts: should contain the following IRQs: + - vdec + - esparser +- interrupt-names: should contain the names of the previous interrupts +- amlogic,ao-sysctrl: should point to the AOBUS sysctrl node +- amlogic,canvas: should point to a canvas provider node +- clocks: should contain the following clocks : + - dos_parser + - dos + - vdec_1 + - vdec_hevc +- clock-names: should contain the names of the previous clocks +- resets: should contain the parser reset +- reset-names: should be "esparser" + +Example: + +vdec: video-decoder@c8820000 { + compatible = "amlogic,gxbb-vdec"; + reg = <0x0 0xc8820000 0x0 0x10000>, + <0x0 0xc110a580 0x0 0xe4>; + reg-names = "dos", "esparser"; + + interrupts = <GIC_SPI 44 IRQ_TYPE_EDGE_RISING>, + <GIC_SPI 32 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "vdec", "esparser"; + + amlogic,ao-sysctrl = <&sysctrl_AO>; + amlogic,canvas = <&canvas>; + + clocks = <&clkc CLKID_DOS_PARSER>, + <&clkc CLKID_DOS>, + <&clkc CLKID_VDEC_1>, + <&clkc CLKID_VDEC_HEVC>; + clock-names = "dos_parser", "dos", "vdec_1", "vdec_hevc"; + + resets = <&reset RESET_PARSER>; + reset-names = "esparser"; +}; diff --git a/Documentation/devicetree/bindings/media/marvell,mmp2-ccic.txt b/Documentation/devicetree/bindings/media/marvell,mmp2-ccic.txt new file mode 100644 index 000000000000..7ec2c8c8a3b9 --- /dev/null +++ b/Documentation/devicetree/bindings/media/marvell,mmp2-ccic.txt @@ -0,0 +1,50 @@ +Marvell MMP2 camera host interface + +Required properties: + - compatible: Should be "marvell,mmp2-ccic". + - reg: Register base and size. + - interrupts: The interrupt number. + - #clock-cells: Must be 0. + +Optional properties: + - clocks: Reference to the input clock as specified by + Documentation/devicetree/bindings/clock/clock-bindings.txt. + - clock-names: Names of the clocks used; "axi" for the AXI bus interface, + "func" for the peripheral clock and "phy" for the parallel + video bus interface. + - clock-output-names: Optional clock source for sensors. Shall be "mclk". + +Required subnodes: + - port: The parallel bus interface port with a single endpoint linked to + the sensor's endpoint as described in + Documentation/devicetree/bindings/media/video-interfaces.txt. + +Required endpoint properties: + - bus-type: data bus type, <5> or <6> for Parallel or Bt.656 respectively + - pclk-sample: pixel clock polarity + - hsync-active: horizontal synchronization polarity (only required for + parallel bus) + - vsync-active: vertical synchronization polarity (only required for + parallel bus) + +Example: + + camera0: camera@d420a000 { + compatible = "marvell,mmp2-ccic"; + reg = <0xd420a000 0x800>; + interrupts = <42>; + clocks = <&soc_clocks MMP2_CLK_CCIC0>; + clock-names = "axi"; + #clock-cells = <0>; + clock-output-names = "mclk"; + + port { + camera0_0: endpoint { + remote-endpoint = <&ov7670_0>; + bus-type = <5>; /* Parallel */ + hsync-active = <1>; /* Active high */ + vsync-active = <1>; /* Active high */ + pclk-sample = <0>; /* Falling */ + }; + }; + }; diff --git a/Documentation/devicetree/bindings/media/sun6i-csi.txt b/Documentation/devicetree/bindings/media/sun6i-csi.txt index 0dd540bb03db..a2e3e56f0257 100644 --- a/Documentation/devicetree/bindings/media/sun6i-csi.txt +++ b/Documentation/devicetree/bindings/media/sun6i-csi.txt @@ -6,6 +6,7 @@ Allwinner V3s SoC features a CSI module(CSI1) with parallel interface. Required properties: - compatible: value must be one of: * "allwinner,sun6i-a31-csi" + * "allwinner,sun8i-a83t-csi" * "allwinner,sun8i-h3-csi" * "allwinner,sun8i-v3s-csi" * "allwinner,sun50i-a64-csi" diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 33a65a45e319..1acf806b62bf 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -49,6 +49,8 @@ patternProperties: description: Aeroflex Gaisler AB "^al,.*": description: Annapurna Labs + "^allegro,.*": + description: Allegro DVT "^allo,.*": description: Allo.com "^allwinner,.*": diff --git a/Documentation/media/kapi/dtv-core.rst b/Documentation/media/kapi/dtv-core.rst index ac005b46f23e..82c5b85ed9b1 100644 --- a/Documentation/media/kapi/dtv-core.rst +++ b/Documentation/media/kapi/dtv-core.rst @@ -11,12 +11,12 @@ Digital TV devices are implemented by several different drivers: - Frontend drivers that are usually implemented as two separate drivers: - - A tuner driver that implements the logic with commands the part of the - hardware with is responsible to tune into a digital TV transponder or + - A tuner driver that implements the logic which commands the part of + the hardware responsible for tuning into a digital TV transponder or physical channel. The output of a tuner is usually a baseband or Intermediate Frequency (IF) signal; - - A demodulator driver (a.k.a "demod") that implements the logic with + - A demodulator driver (a.k.a "demod") that implements the logic which commands the digital TV decoding hardware. The output of a demod is a digital stream, with multiple audio, video and data channels typically multiplexed using MPEG Transport Stream [#f1]_. diff --git a/Documentation/media/uapi/cec/cec-api.rst b/Documentation/media/uapi/cec/cec-api.rst index b614bf81aa20..0780ba07995a 100644 --- a/Documentation/media/uapi/cec/cec-api.rst +++ b/Documentation/media/uapi/cec/cec-api.rst @@ -39,7 +39,7 @@ Revision and Copyright ********************** Authors: -- Verkuil, Hans <hans.verkuil@cisco.com> +- Verkuil, Hans <hverkuil-cisco@xs4all.nl> - Initial version. diff --git a/Documentation/media/uapi/cec/cec-ioc-g-mode.rst b/Documentation/media/uapi/cec/cec-ioc-g-mode.rst index c53bb5f73f0d..d0902f356d65 100644 --- a/Documentation/media/uapi/cec/cec-ioc-g-mode.rst +++ b/Documentation/media/uapi/cec/cec-ioc-g-mode.rst @@ -294,7 +294,8 @@ EINVAL The requested mode is invalid. EPERM - Monitor mode is requested without having root permissions + Monitor mode is requested, but the process does have the ``CAP_NET_ADMIN`` + capability. EBUSY Someone else is already an exclusive follower or initiator. diff --git a/Documentation/media/uapi/cec/cec-ioc-receive.rst b/Documentation/media/uapi/cec/cec-ioc-receive.rst index c3a685ff05cb..4137903d672e 100644 --- a/Documentation/media/uapi/cec/cec-ioc-receive.rst +++ b/Documentation/media/uapi/cec/cec-ioc-receive.rst @@ -223,6 +223,18 @@ View On' messages from initiator 0xf ('Unregistered') to destination 0 ('TV'). result of the :ref:`ioctl CEC_TRANSMIT <CEC_TRANSMIT>`, and once via :ref:`ioctl CEC_RECEIVE <CEC_RECEIVE>`. + * .. _`CEC-MSG-FL-RAW`: + + - ``CEC_MSG_FL_RAW`` + - 2 + - Normally CEC messages are validated before transmitting them. If this + flag is set when :ref:`ioctl CEC_TRANSMIT <CEC_TRANSMIT>` is called, + then no validation takes place and the message is transmitted as-is. + This is useful when debugging CEC issues. + This flag is only allowed if the process has the ``CAP_SYS_RAWIO`` + capability. If that is not set, then the ``EPERM`` error code is + returned. + .. tabularcolumns:: |p{5.6cm}|p{0.9cm}|p{11.0cm}| @@ -358,7 +370,8 @@ ENOTTY EPERM The CEC adapter is not configured, i.e. :ref:`ioctl CEC_ADAP_S_LOG_ADDRS <CEC_ADAP_S_LOG_ADDRS>` - has never been called. + has never been called, or ``CEC_MSG_FL_RAW`` was used from a process that + did not have the ``CAP_SYS_RAWIO`` capability. ENONET The CEC adapter is not configured, i.e. :ref:`ioctl CEC_ADAP_S_LOG_ADDRS <CEC_ADAP_S_LOG_ADDRS>` diff --git a/Documentation/media/uapi/mediactl/media-ioc-enum-links.rst b/Documentation/media/uapi/mediactl/media-ioc-enum-links.rst index a982f16e55a4..b827ebc398f8 100644 --- a/Documentation/media/uapi/mediactl/media-ioc-enum-links.rst +++ b/Documentation/media/uapi/mediactl/media-ioc-enum-links.rst @@ -84,6 +84,11 @@ returned during the enumeration process. - Pointer to a links array allocated by the application. Ignored if NULL. + * - __u32 + - ``reserved[4]`` + - Reserved for future extensions. Drivers and applications must set + the array to zero. + .. c:type:: media_pad_desc @@ -135,7 +140,7 @@ returned during the enumeration process. - Link flags, see :ref:`media-link-flag` for more details. * - __u32 - - ``reserved[4]`` + - ``reserved[2]`` - Reserved for future extensions. Drivers and applications must set the array to zero. diff --git a/Documentation/media/uapi/v4l/biblio.rst b/Documentation/media/uapi/v4l/biblio.rst index ec33768c055e..8f4eb8823d82 100644 --- a/Documentation/media/uapi/v4l/biblio.rst +++ b/Documentation/media/uapi/v4l/biblio.rst @@ -122,6 +122,15 @@ ITU BT.1119 :author: International Telecommunication Union (http://www.itu.ch) +.. _h264: + +ITU-T Rec. H.264 Specification (04/2017 Edition) +================================================ + +:title: ITU-T Recommendation H.264 "Advanced Video Coding for Generic Audiovisual Services" + +:author: International Telecommunication Union (http://www.itu.ch) + .. _jfif: JFIF diff --git a/Documentation/media/uapi/v4l/ext-ctrls-codec.rst b/Documentation/media/uapi/v4l/ext-ctrls-codec.rst index 4a8446203085..d6ea2ffd65c5 100644 --- a/Documentation/media/uapi/v4l/ext-ctrls-codec.rst +++ b/Documentation/media/uapi/v4l/ext-ctrls-codec.rst @@ -759,6 +759,32 @@ enum v4l2_mpeg_video_h264_level - +.. _v4l2-mpeg-video-mpeg2-level: + +``V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL`` + (enum) + +enum v4l2_mpeg_video_mpeg2_level - + The level information for the MPEG2 elementary stream. Applicable to + MPEG2 codecs. Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_MPEG2_LEVEL_LOW`` + - Low Level (LL) + * - ``V4L2_MPEG_VIDEO_MPEG2_LEVEL_MAIN`` + - Main Level (ML) + * - ``V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH_1440`` + - High-1440 Level (H-14) + * - ``V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH`` + - High Level (HL) + + + .. _v4l2-mpeg-video-mpeg4-level: ``V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL`` @@ -845,6 +871,36 @@ enum v4l2_mpeg_video_h264_profile - +.. _v4l2-mpeg-video-mpeg2-profile: + +``V4L2_CID_MPEG_VIDEO_MPEG2_PROFILE`` + (enum) + +enum v4l2_mpeg_video_mpeg2_profile - + The profile information for MPEG2. Applicable to MPEG2 codecs. + Possible values are: + + + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + + * - ``V4L2_MPEG_VIDEO_MPEG2_PROFILE_SIMPLE`` + - Simple profile (SP) + * - ``V4L2_MPEG_VIDEO_MPEG2_PROFILE_MAIN`` + - Main profile (MP) + * - ``V4L2_MPEG_VIDEO_MPEG2_PROFILE_SNR_SCALABLE`` + - SNR Scalable profile (SNR) + * - ``V4L2_MPEG_VIDEO_MPEG2_PROFILE_SPATIALLY_SCALABLE`` + - Spatially Scalable profile (Spt) + * - ``V4L2_MPEG_VIDEO_MPEG2_PROFILE_HIGH`` + - High profile (HP) + * - ``V4L2_MPEG_VIDEO_MPEG2_PROFILE_MULTIVIEW`` + - Multi-view profile (MVP) + + + .. _v4l2-mpeg-video-mpeg4-profile: ``V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE`` @@ -1395,6 +1451,575 @@ enum v4l2_mpeg_video_h264_hierarchical_coding_type - - Layer number +.. _v4l2-mpeg-h264: + +``V4L2_CID_MPEG_VIDEO_H264_SPS (struct)`` + Specifies the sequence parameter set (as extracted from the + bitstream) for the associated H264 slice data. This includes the + necessary parameters for configuring a stateless hardware decoding + pipeline for H264. The bitstream parameters are defined according + to :ref:`h264`, section 7.4.2.1.1 "Sequence Parameter Set Data + Semantics". For further documentation, refer to the above + specification, unless there is an explicit comment stating + otherwise. + + .. note:: + + This compound control is not yet part of the public kernel API and + it is expected to change. + +.. c:type:: v4l2_ctrl_h264_sps + +.. cssclass:: longtable + +.. flat-table:: struct v4l2_ctrl_h264_sps + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - __u8 + - ``profile_idc`` + - + * - __u8 + - ``constraint_set_flags`` + - See :ref:`Sequence Parameter Set Constraints Set Flags <h264_sps_constraints_set_flags>` + * - __u8 + - ``level_idc`` + - + * - __u8 + - ``seq_parameter_set_id`` + - + * - __u8 + - ``chroma_format_idc`` + - + * - __u8 + - ``bit_depth_luma_minus8`` + - + * - __u8 + - ``bit_depth_chroma_minus8`` + - + * - __u8 + - ``log2_max_frame_num_minus4`` + - + * - __u8 + - ``pic_order_cnt_type`` + - + * - __u8 + - ``log2_max_pic_order_cnt_lsb_minus4`` + - + * - __u8 + - ``max_num_ref_frames`` + - + * - __u8 + - ``num_ref_frames_in_pic_order_cnt_cycle`` + - + * - __s32 + - ``offset_for_ref_frame[255]`` + - + * - __s32 + - ``offset_for_non_ref_pic`` + - + * - __s32 + - ``offset_for_top_to_bottom_field`` + - + * - __u16 + - ``pic_width_in_mbs_minus1`` + - + * - __u16 + - ``pic_height_in_map_units_minus1`` + - + * - __u32 + - ``flags`` + - See :ref:`Sequence Parameter Set Flags <h264_sps_flags>` + +.. _h264_sps_constraints_set_flags: + +``Sequence Parameter Set Constraints Set Flags`` + +.. cssclass:: longtable + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - ``V4L2_H264_SPS_CONSTRAINT_SET0_FLAG`` + - 0x00000001 + - + * - ``V4L2_H264_SPS_CONSTRAINT_SET1_FLAG`` + - 0x00000002 + - + * - ``V4L2_H264_SPS_CONSTRAINT_SET2_FLAG`` + - 0x00000004 + - + * - ``V4L2_H264_SPS_CONSTRAINT_SET3_FLAG`` + - 0x00000008 + - + * - ``V4L2_H264_SPS_CONSTRAINT_SET4_FLAG`` + - 0x00000010 + - + * - ``V4L2_H264_SPS_CONSTRAINT_SET5_FLAG`` + - 0x00000020 + - + +.. _h264_sps_flags: + +``Sequence Parameter Set Flags`` + +.. cssclass:: longtable + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - ``V4L2_H264_SPS_FLAG_SEPARATE_COLOUR_PLANE`` + - 0x00000001 + - + * - ``V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS`` + - 0x00000002 + - + * - ``V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO`` + - 0x00000004 + - + * - ``V4L2_H264_SPS_FLAG_GAPS_IN_FRAME_NUM_VALUE_ALLOWED`` + - 0x00000008 + - + * - ``V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY`` + - 0x00000010 + - + * - ``V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD`` + - 0x00000020 + - + * - ``V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE`` + - 0x00000040 + - + +``V4L2_CID_MPEG_VIDEO_H264_PPS (struct)`` + Specifies the picture parameter set (as extracted from the + bitstream) for the associated H264 slice data. This includes the + necessary parameters for configuring a stateless hardware decoding + pipeline for H264. The bitstream parameters are defined according + to :ref:`h264`, section 7.4.2.2 "Picture Parameter Set RBSP + Semantics". For further documentation, refer to the above + specification, unless there is an explicit comment stating + otherwise. + + .. note:: + + This compound control is not yet part of the public kernel API and + it is expected to change. + +.. c:type:: v4l2_ctrl_h264_pps + +.. cssclass:: longtable + +.. flat-table:: struct v4l2_ctrl_h264_pps + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - __u8 + - ``pic_parameter_set_id`` + - + * - __u8 + - ``seq_parameter_set_id`` + - + * - __u8 + - ``num_slice_groups_minus1`` + - + * - __u8 + - ``num_ref_idx_l0_default_active_minus1`` + - + * - __u8 + - ``num_ref_idx_l1_default_active_minus1`` + - + * - __u8 + - ``weighted_bipred_idc`` + - + * - __s8 + - ``pic_init_qp_minus26`` + - + * - __s8 + - ``pic_init_qs_minus26`` + - + * - __s8 + - ``chroma_qp_index_offset`` + - + * - __s8 + - ``second_chroma_qp_index_offset`` + - + * - __u16 + - ``flags`` + - See :ref:`Picture Parameter Set Flags <h264_pps_flags>` + +.. _h264_pps_flags: + +``Picture Parameter Set Flags`` + +.. cssclass:: longtable + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - ``V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE`` + - 0x00000001 + - + * - ``V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT`` + - 0x00000002 + - + * - ``V4L2_H264_PPS_FLAG_WEIGHTED_PRED`` + - 0x00000004 + - + * - ``V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT`` + - 0x00000008 + - + * - ``V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED`` + - 0x00000010 + - + * - ``V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT`` + - 0x00000020 + - + * - ``V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE`` + - 0x00000040 + - + * - ``V4L2_H264_PPS_FLAG_PIC_SCALING_MATRIX_PRESENT`` + - 0x00000080 + - + +``V4L2_CID_MPEG_VIDEO_H264_SCALING_MATRIX (struct)`` + Specifies the scaling matrix (as extracted from the bitstream) for + the associated H264 slice data. The bitstream parameters are + defined according to :ref:`h264`, section 7.4.2.1.1.1 "Scaling + List Semantics". For further documentation, refer to the above + specification, unless there is an explicit comment stating + otherwise. + + .. note:: + + This compound control is not yet part of the public kernel API and + it is expected to change. + +.. c:type:: v4l2_ctrl_h264_scaling_matrix + +.. cssclass:: longtable + +.. flat-table:: struct v4l2_ctrl_h264_scaling_matrix + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - __u8 + - ``scaling_list_4x4[6][16]`` + - + * - __u8 + - ``scaling_list_8x8[6][64]`` + - + +``V4L2_CID_MPEG_VIDEO_H264_SLICE_PARAMS (struct)`` + Specifies the slice parameters (as extracted from the bitstream) + for the associated H264 slice data. This includes the necessary + parameters for configuring a stateless hardware decoding pipeline + for H264. The bitstream parameters are defined according to + :ref:`h264`, section 7.4.3 "Slice Header Semantics". For further + documentation, refer to the above specification, unless there is + an explicit comment stating otherwise. + + .. note:: + + This compound control is not yet part of the public kernel API + and it is expected to change. + + This structure is expected to be passed as an array, with one + entry for each slice included in the bitstream buffer. + +.. c:type:: v4l2_ctrl_h264_slice_params + +.. cssclass:: longtable + +.. flat-table:: struct v4l2_ctrl_h264_slice_params + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - __u32 + - ``size`` + - + * - __u32 + - ``header_bit_size`` + - + * - __u16 + - ``first_mb_in_slice`` + - + * - __u8 + - ``slice_type`` + - + * - __u8 + - ``pic_parameter_set_id`` + - + * - __u8 + - ``colour_plane_id`` + - + * - __u8 + - ``redundant_pic_cnt`` + - + * - __u16 + - ``frame_num`` + - + * - __u16 + - ``idr_pic_id`` + - + * - __u16 + - ``pic_order_cnt_lsb`` + - + * - __s32 + - ``delta_pic_order_cnt_bottom`` + - + * - __s32 + - ``delta_pic_order_cnt0`` + - + * - __s32 + - ``delta_pic_order_cnt1`` + - + * - struct :c:type:`v4l2_h264_pred_weight_table` + - ``pred_weight_table`` + - + * - __u32 + - ``dec_ref_pic_marking_bit_size`` + - + * - __u32 + - ``pic_order_cnt_bit_size`` + - + * - __u8 + - ``cabac_init_idc`` + - + * - __s8 + - ``slice_qp_delta`` + - + * - __s8 + - ``slice_qs_delta`` + - + * - __u8 + - ``disable_deblocking_filter_idc`` + - + * - __s8 + - ``slice_alpha_c0_offset_div2`` + - + * - __s8 + - ``slice_beta_offset_div2`` + - + * - __u8 + - ``num_ref_idx_l0_active_minus1`` + - + * - __u8 + - ``num_ref_idx_l1_active_minus1`` + - + * - __u32 + - ``slice_group_change_cycle`` + - + * - __u8 + - ``ref_pic_list0[32]`` + - Reference picture list after applying the per-slice modifications + * - __u8 + - ``ref_pic_list1[32]`` + - Reference picture list after applying the per-slice modifications + * - __u32 + - ``flags`` + - See :ref:`Slice Parameter Flags <h264_slice_flags>` + +.. _h264_slice_flags: + +``Slice Parameter Set Flags`` + +.. cssclass:: longtable + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - ``V4L2_H264_SLICE_FLAG_FIELD_PIC`` + - 0x00000001 + - + * - ``V4L2_H264_SLICE_FLAG_BOTTOM_FIELD`` + - 0x00000002 + - + * - ``V4L2_H264_SLICE_FLAG_DIRECT_SPATIAL_MV_PRED`` + - 0x00000004 + - + * - ``V4L2_H264_SLICE_FLAG_SP_FOR_SWITCH`` + - 0x00000008 + - + +``Prediction Weight Table`` + + The bitstream parameters are defined according to :ref:`h264`, + section 7.4.3.2 "Prediction Weight Table Semantics". For further + documentation, refer to the above specification, unless there is + an explicit comment stating otherwise. + +.. c:type:: v4l2_h264_pred_weight_table + +.. cssclass:: longtable + +.. flat-table:: struct v4l2_h264_pred_weight_table + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - __u16 + - ``luma_log2_weight_denom`` + - + * - __u16 + - ``chroma_log2_weight_denom`` + - + * - struct :c:type:`v4l2_h264_weight_factors` + - ``weight_factors[2]`` + - The weight factors at index 0 are the weight factors for the reference + list 0, the one at index 1 for the reference list 1. + +.. c:type:: v4l2_h264_weight_factors + +.. cssclass:: longtable + +.. flat-table:: struct v4l2_h264_weight_factors + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - __s16 + - ``luma_weight[32]`` + - + * - __s16 + - ``luma_offset[32]`` + - + * - __s16 + - ``chroma_weight[32][2]`` + - + * - __s16 + - ``chroma_offset[32][2]`` + - + +``V4L2_CID_MPEG_VIDEO_H264_DECODE_PARAMS (struct)`` + Specifies the decode parameters (as extracted from the bitstream) + for the associated H264 slice data. This includes the necessary + parameters for configuring a stateless hardware decoding pipeline + for H264. The bitstream parameters are defined according to + :ref:`h264`. For further documentation, refer to the above + specification, unless there is an explicit comment stating + otherwise. + + .. note:: + + This compound control is not yet part of the public kernel API and + it is expected to change. + +.. c:type:: v4l2_ctrl_h264_decode_params + +.. cssclass:: longtable + +.. flat-table:: struct v4l2_ctrl_h264_decode_params + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - struct :c:type:`v4l2_h264_dpb_entry` + - ``dpb[16]`` + - + * - __u16 + - ``num_slices`` + - Number of slices needed to decode the current frame + * - __u16 + - ``nal_ref_idc`` + - NAL reference ID value coming from the NAL Unit header + * - __u8 + - ``ref_pic_list_p0[32]`` + - Backward reference list used by P-frames in the original bitstream order + * - __u8 + - ``ref_pic_list_b0[32]`` + - Backward reference list used by B-frames in the original bitstream order + * - __u8 + - ``ref_pic_list_b1[32]`` + - Forward reference list used by B-frames in the original bitstream order + * - __s32 + - ``top_field_order_cnt`` + - Picture Order Count for the coded top field + * - __s32 + - ``bottom_field_order_cnt`` + - Picture Order Count for the coded bottom field + * - __u32 + - ``flags`` + - See :ref:`Decode Parameters Flags <h264_decode_params_flags>` + +.. _h264_decode_params_flags: + +``Decode Parameters Flags`` + +.. cssclass:: longtable + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - ``V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC`` + - 0x00000001 + - That picture is an IDR picture + +.. c:type:: v4l2_h264_dpb_entry + +.. cssclass:: longtable + +.. flat-table:: struct v4l2_h264_dpb_entry + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - __u64 + - ``reference_ts`` + - Timestamp of the V4L2 capture buffer to use as reference, used + with B-coded and P-coded frames. The timestamp refers to the + ``timestamp`` field in struct :c:type:`v4l2_buffer`. Use the + :c:func:`v4l2_timeval_to_ns()` function to convert the struct + :c:type:`timeval` in struct :c:type:`v4l2_buffer` to a __u64. + * - __u16 + - ``frame_num`` + - + * - __u16 + - ``pic_num`` + - + * - __s32 + - ``top_field_order_cnt`` + - + * - __s32 + - ``bottom_field_order_cnt`` + - + * - __u32 + - ``flags`` + - See :ref:`DPB Entry Flags <h264_dpb_flags>` + +.. _h264_dpb_flags: + +``DPB Entries Flags`` + +.. cssclass:: longtable + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + :widths: 1 1 2 + + * - ``V4L2_H264_DPB_ENTRY_FLAG_VALID`` + - 0x00000001 + - The DPB entry is valid and should be considered + * - ``V4L2_H264_DPB_ENTRY_FLAG_ACTIVE`` + - 0x00000002 + - The DPB entry is currently being used as a reference frame + * - ``V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM`` + - 0x00000004 + - The DPB entry is a long term reference frame .. _v4l2-mpeg-mpeg2: diff --git a/Documentation/media/uapi/v4l/field-order.rst b/Documentation/media/uapi/v4l/field-order.rst index d640e922a974..c422bebe4314 100644 --- a/Documentation/media/uapi/v4l/field-order.rst +++ b/Documentation/media/uapi/v4l/field-order.rst @@ -51,6 +51,11 @@ determined by the video standard. Hence the distinction between temporal and spatial order of fields. The diagrams below should make this clearer. +In V4L it is assumed that all video cameras transmit fields on the media +bus in the same order they were captured, so if the top field was +captured first (is the older field), the top field is also transmitted +first on the bus. + All video capture and output devices must report the current field order. Some drivers may permit the selection of a different order, to this end applications initialize the ``field`` field of struct @@ -101,10 +106,10 @@ enum v4l2_field * - ``V4L2_FIELD_INTERLACED`` - 4 - Images contain both fields, interleaved line by line. The temporal - order of the fields (whether the top or bottom field is first - transmitted) depends on the current video standard. M/NTSC - transmits the bottom field first, all other standards the top - field first. + order of the fields (whether the top or bottom field is older) + depends on the current video standard. In M/NTSC the bottom + field is the older field. In all other standards the top field + is the older field. * - ``V4L2_FIELD_SEQ_TB`` - 5 - Images contain both fields, the top field lines are stored first @@ -135,11 +140,11 @@ enum v4l2_field * - ``V4L2_FIELD_INTERLACED_TB`` - 8 - Images contain both fields, interleaved line by line, top field - first. The top field is transmitted first. + first. The top field is the older field. * - ``V4L2_FIELD_INTERLACED_BT`` - 9 - Images contain both fields, interleaved line by line, top field - first. The bottom field is transmitted first. + first. The bottom field is the older field. diff --git a/Documentation/media/uapi/v4l/pixfmt-compressed.rst b/Documentation/media/uapi/v4l/pixfmt-compressed.rst index 6c961cfb74da..4b701fc7653e 100644 --- a/Documentation/media/uapi/v4l/pixfmt-compressed.rst +++ b/Documentation/media/uapi/v4l/pixfmt-compressed.rst @@ -52,6 +52,31 @@ Compressed Formats - ``V4L2_PIX_FMT_H264_MVC`` - 'M264' - H264 MVC video elementary stream. + * .. _V4L2-PIX-FMT-H264-SLICE-RAW: + + - ``V4L2_PIX_FMT_H264_SLICE_RAW`` + - 'S264' + - H264 parsed slice data, without the start code and as + extracted from the H264 bitstream. This format is adapted for + stateless video decoders that implement an H264 pipeline + (using the :ref:`mem2mem` and :ref:`media-request-api`). + Metadata associated with the frame to decode are required to + be passed through the ``V4L2_CID_MPEG_VIDEO_H264_SPS``, + ``V4L2_CID_MPEG_VIDEO_H264_PPS``, + ``V4L2_CID_MPEG_VIDEO_H264_SCALING_MATRIX``, + ``V4L2_CID_MPEG_VIDEO_H264_SLICE_PARAMS`` and + ``V4L2_CID_MPEG_VIDEO_H264_DECODE_PARAMS`` controls. See the + :ref:`associated Codec Control IDs <v4l2-mpeg-h264>`. Exactly + one output and one capture buffer must be provided for use + with this pixel format. The output buffer must contain the + appropriate number of macroblocks to decode a full + corresponding frame to the matching capture buffer. + + .. note:: + + This format is not yet part of the public kernel API and it + is expected to change. + * .. _V4L2-PIX-FMT-H263: - ``V4L2_PIX_FMT_H263`` diff --git a/Documentation/media/uapi/v4l/pixfmt-v4l2-mplane.rst b/Documentation/media/uapi/v4l/pixfmt-v4l2-mplane.rst index 5688c816e334..db43dda5aafb 100644 --- a/Documentation/media/uapi/v4l/pixfmt-v4l2-mplane.rst +++ b/Documentation/media/uapi/v4l/pixfmt-v4l2-mplane.rst @@ -31,7 +31,20 @@ describing all planes of that format. * - __u32 - ``sizeimage`` - - Maximum size in bytes required for image data in this plane. + - Maximum size in bytes required for image data in this plane, + set by the driver. When the image consists of variable length + compressed data this is the number of bytes required by the + codec to support the worst-case compression scenario. + + The driver will set the value for uncompressed images. + + Clients are allowed to set the sizeimage field for variable length + compressed data flagged with ``V4L2_FMT_FLAG_COMPRESSED`` at + :ref:`VIDIOC_ENUM_FMT`, but the driver may ignore it and set the + value itself, or it may modify the provided value based on + alignment requirements or minimum/maximum size requirements. + If the client wants to leave this to the driver, then it should + set sizeimage to 0. * - __u32 - ``bytesperline`` - Distance in bytes between the leftmost pixels in two adjacent diff --git a/Documentation/media/uapi/v4l/pixfmt-v4l2.rst b/Documentation/media/uapi/v4l/pixfmt-v4l2.rst index 71eebfc6d853..da6da2ef139a 100644 --- a/Documentation/media/uapi/v4l/pixfmt-v4l2.rst +++ b/Documentation/media/uapi/v4l/pixfmt-v4l2.rst @@ -89,7 +89,18 @@ Single-planar format structure - Size in bytes of the buffer to hold a complete image, set by the driver. Usually this is ``bytesperline`` times ``height``. When the image consists of variable length compressed data this is the - maximum number of bytes required to hold an image. + number of bytes required by the codec to support the worst-case + compression scenario. + + The driver will set the value for uncompressed images. + + Clients are allowed to set the sizeimage field for variable length + compressed data flagged with ``V4L2_FMT_FLAG_COMPRESSED`` at + :ref:`VIDIOC_ENUM_FMT`, but the driver may ignore it and set the + value itself, or it may modify the provided value based on + alignment requirements or minimum/maximum size requirements. + If the client wants to leave this to the driver, then it should + set sizeimage to 0. * - __u32 - ``colorspace`` - Image colorspace, from enum :c:type:`v4l2_colorspace`. diff --git a/Documentation/media/uapi/v4l/vidioc-queryctrl.rst b/Documentation/media/uapi/v4l/vidioc-queryctrl.rst index f824162d0ea9..dc500632095d 100644 --- a/Documentation/media/uapi/v4l/vidioc-queryctrl.rst +++ b/Documentation/media/uapi/v4l/vidioc-queryctrl.rst @@ -443,6 +443,36 @@ See also the examples in :ref:`control`. - n/a - A struct :c:type:`v4l2_ctrl_mpeg2_quantization`, containing MPEG-2 quantization matrices for stateless video decoders. + * - ``V4L2_CTRL_TYPE_H264_SPS`` + - n/a + - n/a + - n/a + - A struct :c:type:`v4l2_ctrl_h264_sps`, containing H264 + sequence parameters for stateless video decoders. + * - ``V4L2_CTRL_TYPE_H264_PPS`` + - n/a + - n/a + - n/a + - A struct :c:type:`v4l2_ctrl_h264_pps`, containing H264 + picture parameters for stateless video decoders. + * - ``V4L2_CTRL_TYPE_H264_SCALING_MATRIX`` + - n/a + - n/a + - n/a + - A struct :c:type:`v4l2_ctrl_h264_scaling_matrix`, containing H264 + scaling matrices for stateless video decoders. + * - ``V4L2_CTRL_TYPE_H264_SLICE_PARAMS`` + - n/a + - n/a + - n/a + - A struct :c:type:`v4l2_ctrl_h264_slice_params`, containing H264 + slice parameters for stateless video decoders. + * - ``V4L2_CTRL_TYPE_H264_DECODE_PARAMS`` + - n/a + - n/a + - n/a + - A struct :c:type:`v4l2_ctrl_h264_decode_params`, containing H264 + decode parameters for stateless video decoders. .. tabularcolumns:: |p{6.6cm}|p{2.2cm}|p{8.7cm}| diff --git a/Documentation/media/videodev2.h.rst.exceptions b/Documentation/media/videodev2.h.rst.exceptions index 64d348e67df9..55cbe324b9fc 100644 --- a/Documentation/media/videodev2.h.rst.exceptions +++ b/Documentation/media/videodev2.h.rst.exceptions @@ -136,6 +136,11 @@ replace symbol V4L2_CTRL_TYPE_U32 :c:type:`v4l2_ctrl_type` replace symbol V4L2_CTRL_TYPE_U8 :c:type:`v4l2_ctrl_type` replace symbol V4L2_CTRL_TYPE_MPEG2_SLICE_PARAMS :c:type:`v4l2_ctrl_type` replace symbol V4L2_CTRL_TYPE_MPEG2_QUANTIZATION :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_H264_SPS :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_H264_PPS :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_H264_SCALING_MATRIX :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_H264_SLICE_PARAMS :c:type:`v4l2_ctrl_type` +replace symbol V4L2_CTRL_TYPE_H264_DECODE_PARAMS :c:type:`v4l2_ctrl_type` # V4L2 capability defines replace define V4L2_CAP_VIDEO_CAPTURE device-capabilities diff --git a/MAINTAINERS b/MAINTAINERS index 57f496cff999..6a3bac28ebb4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -668,6 +668,13 @@ S: Maintained F: Documentation/i2c/busses/i2c-ali1563 F: drivers/i2c/busses/i2c-ali1563.c +ALLEGRO DVT VIDEO IP CORE DRIVER +M: Michael Tretter <m.tretter@pengutronix.de> +R: Pengutronix Kernel Team <kernel@pengutronix.de> +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/staging/media/allegro-dvt/ + ALLWINNER SECURITY SYSTEM M: Corentin Labbe <clabbe.montjoie@gmail.com> L: linux-crypto@vger.kernel.org @@ -910,7 +917,7 @@ F: drivers/iio/adc/ad7768-1.c F: Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.txt ANALOG DEVICES INC AD9389B DRIVER -M: Hans Verkuil <hans.verkuil@cisco.com> +M: Hans Verkuil <hverkuil-cisco@xs4all.nl> L: linux-media@vger.kernel.org S: Maintained F: drivers/media/i2c/ad9389b* @@ -942,19 +949,19 @@ S: Maintained F: drivers/media/i2c/adv748x/* ANALOG DEVICES INC ADV7511 DRIVER -M: Hans Verkuil <hans.verkuil@cisco.com> +M: Hans Verkuil <hverkuil-cisco@xs4all.nl> L: linux-media@vger.kernel.org S: Maintained F: drivers/media/i2c/adv7511* ANALOG DEVICES INC ADV7604 DRIVER -M: Hans Verkuil <hans.verkuil@cisco.com> +M: Hans Verkuil <hverkuil-cisco@xs4all.nl> L: linux-media@vger.kernel.org S: Maintained F: drivers/media/i2c/adv7604* ANALOG DEVICES INC ADV7842 DRIVER -M: Hans Verkuil <hans.verkuil@cisco.com> +M: Hans Verkuil <hverkuil-cisco@xs4all.nl> L: linux-media@vger.kernel.org S: Maintained F: drivers/media/i2c/adv7842* @@ -2344,7 +2351,7 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained ARM/TEGRA HDMI CEC SUBSYSTEM SUPPORT -M: Hans Verkuil <hans.verkuil@cisco.com> +M: Hans Verkuil <hverkuil-cisco@xs4all.nl> L: linux-tegra@vger.kernel.org L: linux-media@vger.kernel.org S: Maintained @@ -3677,7 +3684,7 @@ F: drivers/crypto/ccree/ W: https://developer.arm.com/products/system-ip/trustzone-cryptocell/cryptocell-700-family CEC FRAMEWORK -M: Hans Verkuil <hans.verkuil@cisco.com> +M: Hans Verkuil <hverkuil-cisco@xs4all.nl> L: linux-media@vger.kernel.org T: git git://linuxtv.org/media_tree.git W: http://linuxtv.org @@ -3694,7 +3701,7 @@ F: Documentation/devicetree/bindings/media/cec.txt F: Documentation/ABI/testing/debugfs-cec-error-inj CEC GPIO DRIVER -M: Hans Verkuil <hans.verkuil@cisco.com> +M: Hans Verkuil <hverkuil-cisco@xs4all.nl> L: linux-media@vger.kernel.org T: git git://linuxtv.org/media_tree.git W: http://linuxtv.org @@ -3970,7 +3977,7 @@ S: Supported F: drivers/platform/x86/classmate-laptop.c COBALT MEDIA DRIVER -M: Hans Verkuil <hans.verkuil@cisco.com> +M: Hans Verkuil <hverkuil-cisco@xs4all.nl> L: linux-media@vger.kernel.org T: git git://linuxtv.org/media_tree.git W: https://linuxtv.org @@ -6705,7 +6712,7 @@ F: drivers/gnss/ F: include/linux/gnss.h GO7007 MPEG CODEC -M: Hans Verkuil <hans.verkuil@cisco.com> +M: Hans Verkuil <hverkuil-cisco@xs4all.nl> L: linux-media@vger.kernel.org S: Maintained F: drivers/media/usb/go7007/ @@ -10228,6 +10235,14 @@ S: Maintained F: drivers/mtd/nand/raw/meson_* F: Documentation/devicetree/bindings/mtd/amlogic,meson-nand.txt +MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS +M: Maxime Jourdan <mjourdan@baylibre.com> +L: linux-media@lists.freedesktop.org +L: linux-amlogic@lists.infradead.org +S: Supported +F: drivers/staging/media/meson/vdec/ +T: git git://linuxtv.org/media_tree.git + METHODE UDPU SUPPORT M: Vladimir Vid <vladimir.vid@sartura.hr> S: Maintained @@ -16683,7 +16698,7 @@ S: Maintained F: drivers/net/ethernet/via/via-velocity.* VICODEC VIRTUAL CODEC DRIVER -M: Hans Verkuil <hans.verkuil@cisco.com> +M: Hans Verkuil <hverkuil-cisco@xs4all.nl> L: linux-media@vger.kernel.org T: git git://linuxtv.org/media_tree.git W: https://linuxtv.org diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 092e7509af9b..21cd9c02960b 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -89,40 +89,7 @@ config MEDIA_CEC_SUPPORT source "drivers/media/cec/Kconfig" -# -# Media controller -# Selectable only for webcam/grabbers, as other drivers don't use it -# - -config MEDIA_CONTROLLER - bool "Media Controller API" - depends on MEDIA_CAMERA_SUPPORT || MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT - help - Enable the media controller API used to query media devices internal - topology and configure it dynamically. - - This API is mostly used by camera interfaces in embedded platforms. - -config MEDIA_CONTROLLER_DVB - bool "Enable Media controller for DVB (EXPERIMENTAL)" - depends on MEDIA_CONTROLLER && DVB_CORE - help - Enable the media controller API support for DVB. - - This is currently experimental. - -config MEDIA_CONTROLLER_REQUEST_API - bool "Enable Media controller Request API (EXPERIMENTAL)" - depends on MEDIA_CONTROLLER && STAGING_MEDIA - default n - help - DO NOT ENABLE THIS OPTION UNLESS YOU KNOW WHAT YOU'RE DOING. - - This option enables the Request API for the Media controller and V4L2 - interfaces. It is currently needed by a few stateless codec drivers. - - There is currently no intention to provide API or ABI stability for - this new API as of yet. +source "drivers/media/mc/Kconfig" # # Video4Linux support @@ -164,7 +131,6 @@ config DVB_MMAP depends on DVB_CORE depends on VIDEO_V4L2=y || VIDEO_V4L2=DVB_CORE select VIDEOBUF2_VMALLOC - default n help This option enables DVB experimental memory-mapped API, which reduces the number of context switches to read DVB buffers, as @@ -190,7 +156,6 @@ config DVB_NET config TTPCI_EEPROM tristate depends on I2C - default n source "drivers/media/dvb-core/Kconfig" diff --git a/drivers/media/Makefile b/drivers/media/Makefile index 4a330d0e5e40..f215f0a89f9e 100644 --- a/drivers/media/Makefile +++ b/drivers/media/Makefile @@ -3,15 +3,6 @@ # Makefile for the kernel multimedia device drivers. # -media-objs := media-device.o media-devnode.o media-entity.o \ - media-request.o - -ifeq ($(CONFIG_MEDIA_CONTROLLER),y) - ifeq ($(CONFIG_USB),y) - media-objs += media-dev-allocator.o - endif -endif - # # I2C drivers should come before other drivers, otherwise they'll fail # when compiled as builtin drivers @@ -20,10 +11,10 @@ obj-y += i2c/ tuners/ obj-$(CONFIG_DVB_CORE) += dvb-frontends/ # -# Now, let's link-in the media core +# Now, let's link-in the media controller core # ifeq ($(CONFIG_MEDIA_CONTROLLER),y) - obj-$(CONFIG_MEDIA_SUPPORT) += media.o + obj-$(CONFIG_MEDIA_SUPPORT) += mc/ endif obj-$(CONFIG_VIDEO_DEV) += v4l2-core/ diff --git a/drivers/media/cec/cec-adap.c b/drivers/media/cec/cec-adap.c index f1261cc2b6fa..ac3683a7b2ab 100644 --- a/drivers/media/cec/cec-adap.c +++ b/drivers/media/cec/cec-adap.c @@ -720,6 +720,7 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg, struct cec_fh *fh, bool block) { struct cec_data *data; + bool is_raw = msg_is_raw(msg); msg->rx_ts = 0; msg->tx_ts = 0; @@ -735,15 +736,10 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg, /* Make sure the timeout isn't 0. */ msg->timeout = 1000; } - if (msg->timeout) - msg->flags &= CEC_MSG_FL_REPLY_TO_FOLLOWERS; - else - msg->flags = 0; + msg->flags &= CEC_MSG_FL_REPLY_TO_FOLLOWERS | CEC_MSG_FL_RAW; - if (msg->len > 1 && msg->msg[1] == CEC_MSG_CDC_MESSAGE) { - msg->msg[2] = adap->phys_addr >> 8; - msg->msg[3] = adap->phys_addr & 0xff; - } + if (!msg->timeout) + msg->flags &= ~CEC_MSG_FL_REPLY_TO_FOLLOWERS; /* Sanity checks */ if (msg->len == 0 || msg->len > CEC_MAX_MSG_SIZE) { @@ -765,44 +761,80 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg, dprintk(1, "%s: can't reply to poll msg\n", __func__); return -EINVAL; } - if (msg->len == 1) { - if (cec_msg_destination(msg) == 0xf) { - dprintk(1, "%s: invalid poll message\n", __func__); + + if (is_raw) { + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + } else { + /* A CDC-Only device can only send CDC messages */ + if ((adap->log_addrs.flags & CEC_LOG_ADDRS_FL_CDC_ONLY) && + (msg->len == 1 || msg->msg[1] != CEC_MSG_CDC_MESSAGE)) { + dprintk(1, "%s: not a CDC message\n", __func__); return -EINVAL; } - if (cec_has_log_addr(adap, cec_msg_destination(msg))) { - /* - * If the destination is a logical address our adapter - * has already claimed, then just NACK this. - * It depends on the hardware what it will do with a - * POLL to itself (some OK this), so it is just as - * easy to handle it here so the behavior will be - * consistent. - */ - msg->tx_ts = ktime_get_ns(); - msg->tx_status = CEC_TX_STATUS_NACK | - CEC_TX_STATUS_MAX_RETRIES; - msg->tx_nack_cnt = 1; - msg->sequence = ++adap->sequence; - if (!msg->sequence) + + if (msg->len >= 4 && msg->msg[1] == CEC_MSG_CDC_MESSAGE) { + msg->msg[2] = adap->phys_addr >> 8; + msg->msg[3] = adap->phys_addr & 0xff; + } + + if (msg->len == 1) { + if (cec_msg_destination(msg) == 0xf) { + dprintk(1, "%s: invalid poll message\n", + __func__); + return -EINVAL; + } + if (cec_has_log_addr(adap, cec_msg_destination(msg))) { + /* + * If the destination is a logical address our + * adapter has already claimed, then just NACK + * this. It depends on the hardware what it will + * do with a POLL to itself (some OK this), so + * it is just as easy to handle it here so the + * behavior will be consistent. + */ + msg->tx_ts = ktime_get_ns(); + msg->tx_status = CEC_TX_STATUS_NACK | + CEC_TX_STATUS_MAX_RETRIES; + msg->tx_nack_cnt = 1; msg->sequence = ++adap->sequence; - return 0; + if (!msg->sequence) + msg->sequence = ++adap->sequence; + return 0; + } + } + if (msg->len > 1 && !cec_msg_is_broadcast(msg) && + cec_has_log_addr(adap, cec_msg_destination(msg))) { + dprintk(1, "%s: destination is the adapter itself\n", + __func__); + return -EINVAL; + } + if (msg->len > 1 && adap->is_configured && + !cec_has_log_addr(adap, cec_msg_initiator(msg))) { + dprintk(1, "%s: initiator has unknown logical address %d\n", + __func__, cec_msg_initiator(msg)); + return -EINVAL; + } + /* + * Special case: allow Ping and IMAGE/TEXT_VIEW_ON to be + * transmitted to a TV, even if the adapter is unconfigured. + * This makes it possible to detect or wake up displays that + * pull down the HPD when in standby. + */ + if (!adap->is_configured && !adap->is_configuring && + (msg->len > 2 || + cec_msg_destination(msg) != CEC_LOG_ADDR_TV || + (msg->len == 2 && msg->msg[1] != CEC_MSG_IMAGE_VIEW_ON && + msg->msg[1] != CEC_MSG_TEXT_VIEW_ON))) { + dprintk(1, "%s: adapter is unconfigured\n", __func__); + return -ENONET; } } - if (msg->len > 1 && !cec_msg_is_broadcast(msg) && - cec_has_log_addr(adap, cec_msg_destination(msg))) { - dprintk(1, "%s: destination is the adapter itself\n", __func__); - return -EINVAL; - } - if (msg->len > 1 && adap->is_configured && - !cec_has_log_addr(adap, cec_msg_initiator(msg))) { - dprintk(1, "%s: initiator has unknown logical address %d\n", - __func__, cec_msg_initiator(msg)); - return -EINVAL; - } + if (!adap->is_configured && !adap->is_configuring) { - if (adap->needs_hpd || msg->msg[0] != 0xf0) { - dprintk(1, "%s: adapter is unconfigured\n", __func__); + if (adap->needs_hpd) { + dprintk(1, "%s: adapter is unconfigured and needs HPD\n", + __func__); return -ENONET; } if (msg->reply) { diff --git a/drivers/media/cec/cec-api.c b/drivers/media/cec/cec-api.c index 156a0d76ab2a..12d676484472 100644 --- a/drivers/media/cec/cec-api.c +++ b/drivers/media/cec/cec-api.c @@ -198,19 +198,11 @@ static long cec_transmit(struct cec_adapter *adap, struct cec_fh *fh, if (copy_from_user(&msg, parg, sizeof(msg))) return -EFAULT; - /* A CDC-Only device can only send CDC messages */ - if ((adap->log_addrs.flags & CEC_LOG_ADDRS_FL_CDC_ONLY) && - (msg.len == 1 || msg.msg[1] != CEC_MSG_CDC_MESSAGE)) - return -EINVAL; - mutex_lock(&adap->lock); if (adap->log_addrs.num_log_addrs == 0) err = -EPERM; else if (adap->is_configuring) err = -ENONET; - else if (!adap->is_configured && - (adap->needs_hpd || msg.msg[0] != 0xf0)) - err = -ENONET; else if (cec_is_busy(adap, fh)) err = -EBUSY; else diff --git a/drivers/media/cec/cec-core.c b/drivers/media/cec/cec-core.c index f5d1578e256a..db7adffcdc76 100644 --- a/drivers/media/cec/cec-core.c +++ b/drivers/media/cec/cec-core.c @@ -128,13 +128,14 @@ static int __must_check cec_devnode_register(struct cec_devnode *devnode, devnode->cdev.owner = owner; kobject_set_name(&devnode->cdev.kobj, "cec%d", devnode->minor); + devnode->registered = true; ret = cdev_device_add(&devnode->cdev, &devnode->dev); if (ret) { + devnode->registered = false; pr_err("%s: cdev_device_add failed\n", __func__); goto clr_bit; } - devnode->registered = true; return 0; clr_bit: diff --git a/drivers/media/cec/cec-priv.h b/drivers/media/cec/cec-priv.h index 804e38f849c7..7bdf855aaecd 100644 --- a/drivers/media/cec/cec-priv.h +++ b/drivers/media/cec/cec-priv.h @@ -20,6 +20,11 @@ /* devnode to cec_adapter */ #define to_cec_adapter(node) container_of(node, struct cec_adapter, devnode) +static inline bool msg_is_raw(const struct cec_msg *msg) +{ + return msg->flags & CEC_MSG_FL_RAW; +} + /* cec-core.c */ extern int cec_debug; int cec_get_device(struct cec_devnode *devnode); diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index 3cf25abf5807..4489744fbbd9 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -205,8 +205,13 @@ static int __vb2_buf_mem_alloc(struct vb2_buffer *vb) * NOTE: mmapped areas should be page aligned */ for (plane = 0; plane < vb->num_planes; ++plane) { + /* Memops alloc requires size to be page aligned. */ unsigned long size = PAGE_ALIGN(vb->planes[plane].length); + /* Did it wrap around? */ + if (size < vb->planes[plane].length) + goto free; + mem_priv = call_ptr_memop(vb, alloc, q->alloc_devs[plane] ? : q->dev, q->dma_attrs, size, q->dma_dir, q->gfp_flags); diff --git a/drivers/media/common/videobuf2/videobuf2-dma-contig.c b/drivers/media/common/videobuf2/videobuf2-dma-contig.c index ecbef266130b..7d77e4d30c8a 100644 --- a/drivers/media/common/videobuf2/videobuf2-dma-contig.c +++ b/drivers/media/common/videobuf2/videobuf2-dma-contig.c @@ -475,8 +475,7 @@ static void *vb2_dc_get_userptr(struct device *dev, unsigned long vaddr, buf->dma_dir = dma_dir; offset = lower_32_bits(offset_in_page(vaddr)); - vec = vb2_create_framevec(vaddr, size, dma_dir == DMA_FROM_DEVICE || - dma_dir == DMA_BIDIRECTIONAL); + vec = vb2_create_framevec(vaddr, size); if (IS_ERR(vec)) { ret = PTR_ERR(vec); goto fail_buf; diff --git a/drivers/media/common/videobuf2/videobuf2-dma-sg.c b/drivers/media/common/videobuf2/videobuf2-dma-sg.c index 4a4c49d6085c..ed706b2a263c 100644 --- a/drivers/media/common/videobuf2/videobuf2-dma-sg.c +++ b/drivers/media/common/videobuf2/videobuf2-dma-sg.c @@ -59,7 +59,7 @@ static int vb2_dma_sg_alloc_compacted(struct vb2_dma_sg_buf *buf, gfp_t gfp_flags) { unsigned int last_page = 0; - int size = buf->size; + unsigned long size = buf->size; while (size > 0) { struct page *pages; @@ -239,8 +239,7 @@ static void *vb2_dma_sg_get_userptr(struct device *dev, unsigned long vaddr, buf->offset = vaddr & ~PAGE_MASK; buf->size = size; buf->dma_sgt = &buf->sg_table; - vec = vb2_create_framevec(vaddr, size, dma_dir == DMA_FROM_DEVICE || - dma_dir == DMA_BIDIRECTIONAL); + vec = vb2_create_framevec(vaddr, size); if (IS_ERR(vec)) goto userptr_fail_pfnvec; buf->vec = vec; diff --git a/drivers/media/common/videobuf2/videobuf2-memops.c b/drivers/media/common/videobuf2/videobuf2-memops.c index c4a85be48ac2..6e9e05153f4e 100644 --- a/drivers/media/common/videobuf2/videobuf2-memops.c +++ b/drivers/media/common/videobuf2/videobuf2-memops.c @@ -26,7 +26,6 @@ * vb2_create_framevec() - map virtual addresses to pfns * @start: Virtual user address where we start mapping * @length: Length of a range to map - * @write: Should we map for writing into the area * * This function allocates and fills in a vector with pfns corresponding to * virtual address range passed in arguments. If pfns have corresponding pages, @@ -35,17 +34,13 @@ * failure. Returned vector needs to be freed via vb2_destroy_pfnvec(). */ struct frame_vector *vb2_create_framevec(unsigned long start, - unsigned long length, - bool write) + unsigned long length) { int ret; unsigned long first, last; unsigned long nr; struct frame_vector *vec; - unsigned int flags = FOLL_FORCE; - - if (write) - flags |= FOLL_WRITE; + unsigned int flags = FOLL_FORCE | FOLL_WRITE; first = start >> PAGE_SHIFT; last = (start + length - 1) >> PAGE_SHIFT; diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index fb9ac7696fc6..40d76eb4c2fe 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -563,11 +563,6 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, void *pb) b->flags |= V4L2_BUF_FLAG_REQUEST_FD; b->request_fd = vbuf->request_fd; } - - if (!q->is_output && - b->flags & V4L2_BUF_FLAG_DONE && - b->flags & V4L2_BUF_FLAG_LAST) - q->last_buffer_dequeued = true; } /* @@ -786,6 +781,11 @@ int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking) ret = vb2_core_dqbuf(q, NULL, b, nonblocking); + if (!q->is_output && + b->flags & V4L2_BUF_FLAG_DONE && + b->flags & V4L2_BUF_FLAG_LAST) + q->last_buffer_dequeued = true; + /* * After calling the VIDIOC_DQBUF V4L2_BUF_FLAG_DONE must be * cleared. diff --git a/drivers/media/common/videobuf2/videobuf2-vmalloc.c b/drivers/media/common/videobuf2/videobuf2-vmalloc.c index 1c6659f7c394..04d51ca63223 100644 --- a/drivers/media/common/videobuf2/videobuf2-vmalloc.c +++ b/drivers/media/common/videobuf2/videobuf2-vmalloc.c @@ -87,8 +87,7 @@ static void *vb2_vmalloc_get_userptr(struct device *dev, unsigned long vaddr, buf->dma_dir = dma_dir; offset = vaddr & ~PAGE_MASK; buf->size = size; - vec = vb2_create_framevec(vaddr, size, dma_dir == DMA_FROM_DEVICE || - dma_dir == DMA_BIDIRECTIONAL); + vec = vb2_create_framevec(vaddr, size); if (IS_ERR(vec)) { ret = PTR_ERR(vec); goto fail_pfnvec_create; diff --git a/drivers/media/dvb-core/Kconfig b/drivers/media/dvb-core/Kconfig index aac4bebb35f7..90e038d5ffd9 100644 --- a/drivers/media/dvb-core/Kconfig +++ b/drivers/media/dvb-core/Kconfig @@ -19,7 +19,6 @@ config DVB_MAX_ADAPTERS config DVB_DYNAMIC_MINORS bool "Dynamic DVB minor allocation" depends on DVB_CORE - default n help If you say Y here, the DVB subsystem will use dynamic minor allocation for any device that uses the DVB major number. @@ -32,7 +31,6 @@ config DVB_DYNAMIC_MINORS config DVB_DEMUX_SECTION_LOSS_LOG bool "Enable DVB demux section packet loss log" depends on DVB_CORE - default n help Enable extra log messages meant to detect packet loss inside the Kernel. @@ -45,7 +43,6 @@ config DVB_DEMUX_SECTION_LOSS_LOG config DVB_ULE_DEBUG bool "Enable DVB net ULE packet debug messages" depends on DVB_CORE - default n help Enable extra log messages meant to detect problems while handling DVB network ULE packet loss inside the Kernel. diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index 847da72d1256..dc43749177df 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -1,5 +1,5 @@ menu "Customise DVB Frontends" - visible if !MEDIA_SUBDRV_AUTOSELECT || COMPILE_TEST + visible if !MEDIA_SUBDRV_AUTOSELECT || COMPILE_TEST || EXPERT comment "Multistandard (satellite) frontends" depends on DVB_CORE @@ -945,5 +945,4 @@ comment "Tools to develop new frontends" config DVB_DUMMY_FE tristate "Dummy frontend driver" depends on DVB_CORE - default n endmenu diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c index cf1a8f77ee02..e05c21d35dc8 100644 --- a/drivers/media/dvb-frontends/rtl2832_sdr.c +++ b/drivers/media/dvb-frontends/rtl2832_sdr.c @@ -428,9 +428,6 @@ static int rtl2832_sdr_querycap(struct file *file, void *fh, strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); strscpy(cap->card, dev->vdev.name, sizeof(cap->card)); usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE | V4L2_CAP_TUNER; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -1242,6 +1239,8 @@ static struct video_device rtl2832_sdr_template = { .release = video_device_release_empty, .fops = &rtl2832_sdr_fops, .ioctl_ops = &rtl2832_sdr_ioctl_ops, + .device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE | V4L2_CAP_TUNER, }; static int rtl2832_sdr_s_ctrl(struct v4l2_ctrl *ctrl) diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index 5dae571e2f62..168c503e9154 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -674,8 +674,11 @@ static const struct dvb_frontend_ops si2168_ops = { .delsys = {SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A}, .info = { .name = "Silicon Labs Si2168", - .symbol_rate_min = 1000000, - .symbol_rate_max = 7200000, + .frequency_min_hz = 48 * MHz, + .frequency_max_hz = 870 * MHz, + .frequency_stepsize_hz = 62500, + .symbol_rate_min = 1000000, + .symbol_rate_max = 7200000, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | diff --git a/drivers/media/dvb-frontends/stv090x.c b/drivers/media/dvb-frontends/stv090x.c index d1261571dbe4..986e585e0103 100644 --- a/drivers/media/dvb-frontends/stv090x.c +++ b/drivers/media/dvb-frontends/stv090x.c @@ -4889,6 +4889,67 @@ static int stv090x_set_gpio(struct dvb_frontend *fe, u8 gpio, u8 dir, return stv090x_write_reg(state, STV090x_GPIOxCFG(gpio), reg); } +static int stv090x_setup_compound(struct stv090x_state *state) +{ + struct stv090x_dev *temp_int; + + temp_int = find_dev(state->i2c, + state->config->address); + + if (temp_int && state->demod_mode == STV090x_DUAL) { + state->internal = temp_int->internal; + state->internal->num_used++; + dprintk(FE_INFO, 1, "Found Internal Structure!"); + } else { + state->internal = kmalloc(sizeof(*state->internal), GFP_KERNEL); + if (!state->internal) + goto error; + temp_int = append_internal(state->internal); + if (!temp_int) { + kfree(state->internal); + goto error; + } + state->internal->num_used = 1; + state->internal->mclk = 0; + state->internal->dev_ver = 0; + state->internal->i2c_adap = state->i2c; + state->internal->i2c_addr = state->config->address; + dprintk(FE_INFO, 1, "Create New Internal Structure!"); + + mutex_init(&state->internal->demod_lock); + mutex_init(&state->internal->tuner_lock); + + if (stv090x_setup(&state->frontend) < 0) { + dprintk(FE_ERROR, 1, "Error setting up device"); + goto err_remove; + } + } + + if (state->internal->dev_ver >= 0x30) + state->frontend.ops.info.caps |= FE_CAN_MULTISTREAM; + + /* workaround for stuck DiSEqC output */ + if (state->config->diseqc_envelope_mode) + stv090x_send_diseqc_burst(&state->frontend, SEC_MINI_A); + + state->config->set_gpio = stv090x_set_gpio; + + dprintk(FE_ERROR, 1, "Probing %s demodulator(%d) Cut=0x%02x", + state->device == STV0900 ? "STV0900" : "STV0903", + state->config->demod, + state->internal->dev_ver); + + return 0; + +error: + kfree(state); + return -ENOMEM; +err_remove: + remove_dev(state->internal); + kfree(state->internal); + return -ENODEV; +} + static const struct dvb_frontend_ops stv090x_ops = { .delsys = { SYS_DVBS, SYS_DVBS2, SYS_DSS }, .info = { @@ -4921,85 +4982,118 @@ static const struct dvb_frontend_ops stv090x_ops = { .read_snr = stv090x_read_cnr, }; +static struct dvb_frontend *stv090x_get_dvb_frontend(struct i2c_client *client) +{ + struct stv090x_state *state = i2c_get_clientdata(client); -struct dvb_frontend *stv090x_attach(struct stv090x_config *config, - struct i2c_adapter *i2c, - enum stv090x_demodulator demod) + dev_dbg(&client->dev, "\n"); + + return &state->frontend; +} + +static int stv090x_probe(struct i2c_client *client, + const struct i2c_device_id *id) { + int ret = 0; + struct stv090x_config *config = client->dev.platform_data; + struct stv090x_state *state = NULL; - struct stv090x_dev *temp_int; - state = kzalloc(sizeof (struct stv090x_state), GFP_KERNEL); - if (state == NULL) + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) { + ret = -ENOMEM; goto error; + } state->verbose = &verbose; state->config = config; - state->i2c = i2c; + state->i2c = client->adapter; state->frontend.ops = stv090x_ops; state->frontend.demodulator_priv = state; - state->demod = demod; - state->demod_mode = config->demod_mode; /* Single or Dual mode */ + state->demod = config->demod; + /* Single or Dual mode */ + state->demod_mode = config->demod_mode; state->device = config->device; - state->rolloff = STV090x_RO_35; /* default */ + /* default */ + state->rolloff = STV090x_RO_35; - temp_int = find_dev(state->i2c, - state->config->address); + ret = stv090x_setup_compound(state); + if (ret) + goto error; - if ((temp_int != NULL) && (state->demod_mode == STV090x_DUAL)) { - state->internal = temp_int->internal; - state->internal->num_used++; - dprintk(FE_INFO, 1, "Found Internal Structure!"); - } else { - state->internal = kmalloc(sizeof(struct stv090x_internal), - GFP_KERNEL); - if (!state->internal) - goto error; - temp_int = append_internal(state->internal); - if (!temp_int) { - kfree(state->internal); - goto error; - } - state->internal->num_used = 1; - state->internal->mclk = 0; - state->internal->dev_ver = 0; - state->internal->i2c_adap = state->i2c; - state->internal->i2c_addr = state->config->address; - dprintk(FE_INFO, 1, "Create New Internal Structure!"); + i2c_set_clientdata(client, state); - mutex_init(&state->internal->demod_lock); - mutex_init(&state->internal->tuner_lock); + /* setup callbacks */ + config->get_dvb_frontend = stv090x_get_dvb_frontend; - if (stv090x_setup(&state->frontend) < 0) { - dprintk(FE_ERROR, 1, "Error setting up device"); - goto err_remove; - } - } + return 0; - if (state->internal->dev_ver >= 0x30) - state->frontend.ops.info.caps |= FE_CAN_MULTISTREAM; +error: + kfree(state); + return ret; +} - /* workaround for stuck DiSEqC output */ - if (config->diseqc_envelope_mode) - stv090x_send_diseqc_burst(&state->frontend, SEC_MINI_A); +static int stv090x_remove(struct i2c_client *client) +{ + struct stv090x_state *state = i2c_get_clientdata(client); + + stv090x_release(&state->frontend); + return 0; +} - config->set_gpio = stv090x_set_gpio; +struct dvb_frontend *stv090x_attach(struct stv090x_config *config, + struct i2c_adapter *i2c, + enum stv090x_demodulator demod) +{ + int ret = 0; + struct stv090x_state *state = NULL; - dprintk(FE_ERROR, 1, "Attaching %s demodulator(%d) Cut=0x%02x", - state->device == STV0900 ? "STV0900" : "STV0903", - demod, - state->internal->dev_ver); + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + goto error; + + state->verbose = &verbose; + state->config = config; + state->i2c = i2c; + state->frontend.ops = stv090x_ops; + state->frontend.demodulator_priv = state; + state->demod = demod; + /* Single or Dual mode */ + state->demod_mode = config->demod_mode; + state->device = config->device; + /* default */ + state->rolloff = STV090x_RO_35; + + ret = stv090x_setup_compound(state); + if (ret) + goto error; return &state->frontend; -err_remove: - remove_dev(state->internal); - kfree(state->internal); error: kfree(state); return NULL; } EXPORT_SYMBOL(stv090x_attach); + +static const struct i2c_device_id stv090x_id_table[] = { + {"stv090x", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, stv090x_id_table); + +static struct i2c_driver stv090x_driver = { + .driver = { + .name = "stv090x", + .suppress_bind_attrs = true, + }, + .probe = stv090x_probe, + .remove = stv090x_remove, + .id_table = stv090x_id_table, +}; + +module_i2c_driver(stv090x_driver); + MODULE_PARM_DESC(verbose, "Set Verbosity level"); MODULE_AUTHOR("Manu Abraham"); MODULE_DESCRIPTION("STV090x Multi-Std Broadcast frontend"); diff --git a/drivers/media/dvb-frontends/stv090x.h b/drivers/media/dvb-frontends/stv090x.h index 13f251a08abd..89f45d9fa427 100644 --- a/drivers/media/dvb-frontends/stv090x.h +++ b/drivers/media/dvb-frontends/stv090x.h @@ -57,6 +57,7 @@ struct stv090x_config { enum stv090x_device device; enum stv090x_mode demod_mode; enum stv090x_clkmode clk_mode; + enum stv090x_demodulator demod; u32 xtal; /* default: 8000000 */ u8 address; /* default: 0x68 */ @@ -93,6 +94,8 @@ struct stv090x_config { /* dir = 0 -> output, dir = 1 -> input/open-drain */ int (*set_gpio)(struct dvb_frontend *fe, u8 gpio, u8 dir, u8 value, u8 xor_value); + + struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *i2c); }; #if IS_REACHABLE(CONFIG_DVB_STV090x) diff --git a/drivers/media/dvb-frontends/stv090x_priv.h b/drivers/media/dvb-frontends/stv090x_priv.h index b22c58968c93..f8ece898c153 100644 --- a/drivers/media/dvb-frontends/stv090x_priv.h +++ b/drivers/media/dvb-frontends/stv090x_priv.h @@ -237,7 +237,7 @@ struct stv090x_state { struct stv090x_internal *internal; struct i2c_adapter *i2c; - const struct stv090x_config *config; + struct stv090x_config *config; struct dvb_frontend frontend; u32 *verbose; /* Cached module verbosity */ diff --git a/drivers/media/dvb-frontends/stv6110x.c b/drivers/media/dvb-frontends/stv6110x.c index 0126cfae2e03..5012d0231652 100644 --- a/drivers/media/dvb-frontends/stv6110x.c +++ b/drivers/media/dvb-frontends/stv6110x.c @@ -333,6 +333,41 @@ static void stv6110x_release(struct dvb_frontend *fe) kfree(stv6110x); } +static void st6110x_init_regs(struct stv6110x_state *stv6110x) +{ + u8 default_regs[] = {0x07, 0x11, 0xdc, 0x85, 0x17, 0x01, 0xe6, 0x1e}; + + memcpy(stv6110x->regs, default_regs, 8); +} + +static void stv6110x_setup_divider(struct stv6110x_state *stv6110x) +{ + switch (stv6110x->config->clk_div) { + default: + case 1: + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], + CTRL2_CO_DIV, + 0); + break; + case 2: + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], + CTRL2_CO_DIV, + 1); + break; + case 4: + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], + CTRL2_CO_DIV, + 2); + break; + case 8: + case 0: + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], + CTRL2_CO_DIV, + 3); + break; + } +} + static const struct dvb_tuner_ops stv6110x_ops = { .info = { .name = "STV6110(A) Silicon Tuner", @@ -342,7 +377,7 @@ static const struct dvb_tuner_ops stv6110x_ops = { .release = stv6110x_release }; -static const struct stv6110x_devctl stv6110x_ctl = { +static struct stv6110x_devctl stv6110x_ctl = { .tuner_init = stv6110x_init, .tuner_sleep = stv6110x_sleep, .tuner_set_mode = stv6110x_set_mode, @@ -356,48 +391,104 @@ static const struct stv6110x_devctl stv6110x_ctl = { .tuner_get_status = stv6110x_get_status, }; +static void stv6110x_set_frontend_opts(struct stv6110x_state *stv6110x) +{ + stv6110x->frontend->tuner_priv = stv6110x; + stv6110x->frontend->ops.tuner_ops = stv6110x_ops; +} + +static struct stv6110x_devctl *stv6110x_get_devctl(struct i2c_client *client) +{ + struct stv6110x_state *stv6110x = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + return stv6110x->devctl; +} + +static int stv6110x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct stv6110x_config *config = client->dev.platform_data; + + struct stv6110x_state *stv6110x; + + stv6110x = kzalloc(sizeof(*stv6110x), GFP_KERNEL); + if (!stv6110x) + return -ENOMEM; + + stv6110x->frontend = config->frontend; + stv6110x->i2c = client->adapter; + stv6110x->config = config; + stv6110x->devctl = &stv6110x_ctl; + + st6110x_init_regs(stv6110x); + stv6110x_setup_divider(stv6110x); + stv6110x_set_frontend_opts(stv6110x); + + dev_info(&stv6110x->i2c->dev, "Probed STV6110x\n"); + + i2c_set_clientdata(client, stv6110x); + + /* setup callbacks */ + config->get_devctl = stv6110x_get_devctl; + + return 0; +} + +static int stv6110x_remove(struct i2c_client *client) +{ + struct stv6110x_state *stv6110x = i2c_get_clientdata(client); + + stv6110x_release(stv6110x->frontend); + return 0; +} + const struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe, const struct stv6110x_config *config, struct i2c_adapter *i2c) { struct stv6110x_state *stv6110x; - u8 default_regs[] = {0x07, 0x11, 0xdc, 0x85, 0x17, 0x01, 0xe6, 0x1e}; - stv6110x = kzalloc(sizeof (struct stv6110x_state), GFP_KERNEL); + stv6110x = kzalloc(sizeof(*stv6110x), GFP_KERNEL); if (!stv6110x) return NULL; + stv6110x->frontend = fe; stv6110x->i2c = i2c; stv6110x->config = config; stv6110x->devctl = &stv6110x_ctl; - memcpy(stv6110x->regs, default_regs, 8); - /* setup divider */ - switch (stv6110x->config->clk_div) { - default: - case 1: - STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 0); - break; - case 2: - STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 1); - break; - case 4: - STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 2); - break; - case 8: - case 0: - STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 3); - break; - } + st6110x_init_regs(stv6110x); + stv6110x_setup_divider(stv6110x); + stv6110x_set_frontend_opts(stv6110x); fe->tuner_priv = stv6110x; fe->ops.tuner_ops = stv6110x_ops; - printk(KERN_INFO "%s: Attaching STV6110x\n", __func__); + dev_info(&stv6110x->i2c->dev, "Attaching STV6110x\n"); return stv6110x->devctl; } EXPORT_SYMBOL(stv6110x_attach); +static const struct i2c_device_id stv6110x_id_table[] = { + {"stv6110x", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, stv6110x_id_table); + +static struct i2c_driver stv6110x_driver = { + .driver = { + .name = "stv6110x", + .suppress_bind_attrs = true, + }, + .probe = stv6110x_probe, + .remove = stv6110x_remove, + .id_table = stv6110x_id_table, +}; + +module_i2c_driver(stv6110x_driver); + MODULE_AUTHOR("Manu Abraham"); MODULE_DESCRIPTION("STV6110x Silicon tuner"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/stv6110x.h b/drivers/media/dvb-frontends/stv6110x.h index 1630e55255fd..1feade3158c2 100644 --- a/drivers/media/dvb-frontends/stv6110x.h +++ b/drivers/media/dvb-frontends/stv6110x.h @@ -15,6 +15,9 @@ struct stv6110x_config { u8 addr; u32 refclk; u8 clk_div; /* divisor value for the output clock */ + struct dvb_frontend *frontend; + + struct stv6110x_devctl* (*get_devctl)(struct i2c_client *i2c); }; enum tuner_mode { diff --git a/drivers/media/dvb-frontends/stv6110x_priv.h b/drivers/media/dvb-frontends/stv6110x_priv.h index 909094df28df..b27769558f78 100644 --- a/drivers/media/dvb-frontends/stv6110x_priv.h +++ b/drivers/media/dvb-frontends/stv6110x_priv.h @@ -54,11 +54,12 @@ #define REFCLOCK_MHz (stv6110x->config->refclk / 1000000) struct stv6110x_state { + struct dvb_frontend *frontend; struct i2c_adapter *i2c; const struct stv6110x_config *config; u8 regs[8]; - const struct stv6110x_devctl *devctl; + struct stv6110x_devctl *devctl; }; #endif /* __STV6110x_PRIV_H */ diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index cb8db944aa41..95d42730745c 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -6,7 +6,7 @@ if VIDEO_V4L2 config VIDEO_IR_I2C - tristate "I2C module for IR" if !MEDIA_SUBDRV_AUTOSELECT + tristate "I2C module for IR" if !MEDIA_SUBDRV_AUTOSELECT || EXPERT depends on I2C && RC_CORE default y help @@ -23,7 +23,7 @@ config VIDEO_IR_I2C # menu "I2C Encoders, decoders, sensors and other helper chips" - visible if !MEDIA_SUBDRV_AUTOSELECT || COMPILE_TEST + visible if !MEDIA_SUBDRV_AUTOSELECT || COMPILE_TEST || EXPERT comment "Audio decoders, processors and mixers" diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c index 3ecf79d242f2..0de946fe2109 100644 --- a/drivers/media/i2c/cx25840/cx25840-core.c +++ b/drivers/media/i2c/cx25840/cx25840-core.c @@ -21,9 +21,11 @@ * * CX23888 DIF support for the HVR1850 * Copyright (C) 2011 Steven Toth <stoth@kernellabs.com> + * + * CX2584x pin to pad mapping and output format configuration support are + * Copyright (C) 2011 Maciej S. Szmigiero <mail@maciej.szmigiero.name> */ - #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> @@ -64,17 +66,17 @@ MODULE_LICENSE("GPL"); static int cx25840_debug; -module_param_named(debug,cx25840_debug, int, 0644); +module_param_named(debug, cx25840_debug, int, 0644); MODULE_PARM_DESC(debug, "Debugging messages [0=Off (default) 1=On]"); - /* ----------------------------------------------------------------------- */ static void cx23888_std_setup(struct i2c_client *client); int cx25840_write(struct i2c_client *client, u16 addr, u8 value) { u8 buffer[3]; + buffer[0] = addr >> 8; buffer[1] = addr & 0xff; buffer[2] = value; @@ -84,6 +86,7 @@ int cx25840_write(struct i2c_client *client, u16 addr, u8 value) int cx25840_write4(struct i2c_client *client, u16 addr, u32 value) { u8 buffer[6]; + buffer[0] = addr >> 8; buffer[1] = addr & 0xff; buffer[2] = value & 0xff; @@ -93,7 +96,7 @@ int cx25840_write4(struct i2c_client *client, u16 addr, u32 value) return i2c_master_send(client, buffer, 6); } -u8 cx25840_read(struct i2c_client * client, u16 addr) +u8 cx25840_read(struct i2c_client *client, u16 addr) { struct i2c_msg msgs[2]; u8 tx_buf[2], rx_buf[1]; @@ -104,13 +107,13 @@ u8 cx25840_read(struct i2c_client * client, u16 addr) msgs[0].addr = client->addr; msgs[0].flags = 0; msgs[0].len = 2; - msgs[0].buf = (char *) tx_buf; + msgs[0].buf = (char *)tx_buf; /* Read data from register */ msgs[1].addr = client->addr; msgs[1].flags = I2C_M_RD; msgs[1].len = 1; - msgs[1].buf = (char *) rx_buf; + msgs[1].buf = (char *)rx_buf; if (i2c_transfer(client->adapter, msgs, 2) < 2) return 0; @@ -118,7 +121,7 @@ u8 cx25840_read(struct i2c_client * client, u16 addr) return rx_buf[0]; } -u32 cx25840_read4(struct i2c_client * client, u16 addr) +u32 cx25840_read4(struct i2c_client *client, u16 addr) { struct i2c_msg msgs[2]; u8 tx_buf[2], rx_buf[4]; @@ -129,13 +132,13 @@ u32 cx25840_read4(struct i2c_client * client, u16 addr) msgs[0].addr = client->addr; msgs[0].flags = 0; msgs[0].len = 2; - msgs[0].buf = (char *) tx_buf; + msgs[0].buf = (char *)tx_buf; /* Read data from registers */ msgs[1].addr = client->addr; msgs[1].flags = I2C_M_RD; msgs[1].len = 4; - msgs[1].buf = (char *) rx_buf; + msgs[1].buf = (char *)rx_buf; if (i2c_transfer(client->adapter, msgs, 2) < 2) return 0; @@ -144,7 +147,7 @@ u32 cx25840_read4(struct i2c_client * client, u16 addr) rx_buf[0]; } -int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned and_mask, +int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned int and_mask, u8 or_value) { return cx25840_write(client, addr, @@ -162,13 +165,14 @@ int cx25840_and_or4(struct i2c_client *client, u16 addr, u32 and_mask, /* ----------------------------------------------------------------------- */ -static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input, - enum cx25840_audio_input aud_input); +static int set_input(struct i2c_client *client, + enum cx25840_video_input vid_input, + enum cx25840_audio_input aud_input); /* ----------------------------------------------------------------------- */ static int cx23885_s_io_pin_config(struct v4l2_subdev *sd, size_t n, - struct v4l2_subdev_io_pin_config *p) + struct v4l2_subdev_io_pin_config *p) { struct i2c_client *client = v4l2_get_subdevdata(sd); int i; @@ -307,13 +311,225 @@ static int cx23885_s_io_pin_config(struct v4l2_subdev *sd, size_t n, return 0; } +static u8 cx25840_function_to_pad(struct i2c_client *client, u8 function) +{ + if (function > CX25840_PAD_VRESET) { + v4l_err(client, "invalid function %u, assuming default\n", + (unsigned int)function); + return 0; + } + + return function; +} + +static void cx25840_set_invert(u8 *pinctrl3, u8 *voutctrl4, u8 function, + u8 pin, bool invert) +{ + switch (function) { + case CX25840_PAD_IRQ_N: + if (invert) + *pinctrl3 &= ~2; + else + *pinctrl3 |= 2; + break; + + case CX25840_PAD_ACTIVE: + if (invert) + *voutctrl4 |= BIT(2); + else + *voutctrl4 &= ~BIT(2); + break; + + case CX25840_PAD_VACTIVE: + if (invert) + *voutctrl4 |= BIT(5); + else + *voutctrl4 &= ~BIT(5); + break; + + case CX25840_PAD_CBFLAG: + if (invert) + *voutctrl4 |= BIT(4); + else + *voutctrl4 &= ~BIT(4); + break; + + case CX25840_PAD_VRESET: + if (invert) + *voutctrl4 |= BIT(0); + else + *voutctrl4 &= ~BIT(0); + break; + } + + if (function != CX25840_PAD_DEFAULT) + return; + + switch (pin) { + case CX25840_PIN_DVALID_PRGM0: + if (invert) + *voutctrl4 |= BIT(6); + else + *voutctrl4 &= ~BIT(6); + break; + + case CX25840_PIN_HRESET_PRGM2: + if (invert) + *voutctrl4 |= BIT(1); + else + *voutctrl4 &= ~BIT(1); + break; + } +} + +static int cx25840_s_io_pin_config(struct v4l2_subdev *sd, size_t n, + struct v4l2_subdev_io_pin_config *p) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + unsigned int i; + u8 pinctrl[6], pinconf[10], voutctrl4; + + for (i = 0; i < 6; i++) + pinctrl[i] = cx25840_read(client, 0x114 + i); + + for (i = 0; i < 10; i++) + pinconf[i] = cx25840_read(client, 0x11c + i); + + voutctrl4 = cx25840_read(client, 0x407); + + for (i = 0; i < n; i++) { + u8 strength = p[i].strength; + + if (strength != CX25840_PIN_DRIVE_SLOW && + strength != CX25840_PIN_DRIVE_MEDIUM && + strength != CX25840_PIN_DRIVE_FAST) { + v4l_err(client, + "invalid drive speed for pin %u (%u), assuming fast\n", + (unsigned int)p[i].pin, + (unsigned int)strength); + + strength = CX25840_PIN_DRIVE_FAST; + } + + switch (p[i].pin) { + case CX25840_PIN_DVALID_PRGM0: + if (p[i].flags & BIT(V4L2_SUBDEV_IO_PIN_DISABLE)) + pinctrl[0] &= ~BIT(6); + else + pinctrl[0] |= BIT(6); + + pinconf[3] &= 0xf0; + pinconf[3] |= cx25840_function_to_pad(client, + p[i].function); + + cx25840_set_invert(&pinctrl[3], &voutctrl4, + p[i].function, + CX25840_PIN_DVALID_PRGM0, + p[i].flags & + BIT(V4L2_SUBDEV_IO_PIN_ACTIVE_LOW)); + + pinctrl[4] &= ~(3 << 2); /* CX25840_PIN_DRIVE_MEDIUM */ + switch (strength) { + case CX25840_PIN_DRIVE_SLOW: + pinctrl[4] |= 1 << 2; + break; + + case CX25840_PIN_DRIVE_FAST: + pinctrl[4] |= 2 << 2; + break; + } + + break; + + case CX25840_PIN_HRESET_PRGM2: + if (p[i].flags & BIT(V4L2_SUBDEV_IO_PIN_DISABLE)) + pinctrl[1] &= ~BIT(0); + else + pinctrl[1] |= BIT(0); + + pinconf[4] &= 0xf0; + pinconf[4] |= cx25840_function_to_pad(client, + p[i].function); + + cx25840_set_invert(&pinctrl[3], &voutctrl4, + p[i].function, + CX25840_PIN_HRESET_PRGM2, + p[i].flags & + BIT(V4L2_SUBDEV_IO_PIN_ACTIVE_LOW)); + + pinctrl[4] &= ~(3 << 2); /* CX25840_PIN_DRIVE_MEDIUM */ + switch (strength) { + case CX25840_PIN_DRIVE_SLOW: + pinctrl[4] |= 1 << 2; + break; + + case CX25840_PIN_DRIVE_FAST: + pinctrl[4] |= 2 << 2; + break; + } + + break; + + case CX25840_PIN_PLL_CLK_PRGM7: + if (p[i].flags & BIT(V4L2_SUBDEV_IO_PIN_DISABLE)) + pinctrl[2] &= ~BIT(2); + else + pinctrl[2] |= BIT(2); + + switch (p[i].function) { + case CX25840_PAD_XTI_X5_DLL: + pinconf[6] = 0; + break; + + case CX25840_PAD_AUX_PLL: + pinconf[6] = 1; + break; + + case CX25840_PAD_VID_PLL: + pinconf[6] = 5; + break; + + case CX25840_PAD_XTI: + pinconf[6] = 2; + break; + + default: + pinconf[6] = 3; + pinconf[6] |= + cx25840_function_to_pad(client, + p[i].function) + << 4; + } + + break; + + default: + v4l_err(client, "invalid or unsupported pin %u\n", + (unsigned int)p[i].pin); + break; + } + } + + cx25840_write(client, 0x407, voutctrl4); + + for (i = 0; i < 6; i++) + cx25840_write(client, 0x114 + i, pinctrl[i]); + + for (i = 0; i < 10; i++) + cx25840_write(client, 0x11c + i, pinconf[i]); + + return 0; +} + static int common_s_io_pin_config(struct v4l2_subdev *sd, size_t n, - struct v4l2_subdev_io_pin_config *pincfg) + struct v4l2_subdev_io_pin_config *pincfg) { struct cx25840_state *state = to_state(sd); if (is_cx2388x(state)) return cx23885_s_io_pin_config(sd, n, pincfg); + else if (is_cx2584x(state)) + return cx25840_s_io_pin_config(sd, n, pincfg); return 0; } @@ -321,8 +537,10 @@ static int common_s_io_pin_config(struct v4l2_subdev *sd, size_t n, static void init_dll1(struct i2c_client *client) { - /* This is the Hauppauge sequence used to - * initialize the Delay Lock Loop 1 (ADC DLL). */ + /* + * This is the Hauppauge sequence used to + * initialize the Delay Lock Loop 1 (ADC DLL). + */ cx25840_write(client, 0x159, 0x23); cx25840_write(client, 0x15a, 0x87); cx25840_write(client, 0x15b, 0x06); @@ -337,8 +555,10 @@ static void init_dll1(struct i2c_client *client) static void init_dll2(struct i2c_client *client) { - /* This is the Hauppauge sequence used to - * initialize the Delay Lock Loop 2 (ADC DLL). */ + /* + * This is the Hauppauge sequence used to + * initialize the Delay Lock Loop 2 (ADC DLL). + */ cx25840_write(client, 0x15d, 0xe3); cx25840_write(client, 0x15e, 0x86); cx25840_write(client, 0x15f, 0x06); @@ -350,7 +570,11 @@ static void init_dll2(struct i2c_client *client) static void cx25836_initialize(struct i2c_client *client) { - /* reset configuration is described on page 3-77 of the CX25836 datasheet */ + /* + *reset configuration is described on page 3-77 + * of the CX25836 datasheet + */ + /* 2. */ cx25840_and_or(client, 0x000, ~0x01, 0x01); cx25840_and_or(client, 0x000, ~0x01, 0x00); @@ -376,10 +600,96 @@ static void cx25836_initialize(struct i2c_client *client) static void cx25840_work_handler(struct work_struct *work) { struct cx25840_state *state = container_of(work, struct cx25840_state, fw_work); + cx25840_loadfw(state->c); wake_up(&state->fw_wait); } +#define CX25840_VCONFIG_SET_BIT(state, opt_msk, voc, idx, bit, oneval) \ + do { \ + if ((state)->vid_config & (opt_msk)) { \ + if (((state)->vid_config & (opt_msk)) == \ + (oneval)) \ + (voc)[idx] |= BIT(bit); \ + else \ + (voc)[idx] &= ~BIT(bit); \ + } \ + } while (0) + +/* apply current vconfig to hardware regs */ +static void cx25840_vconfig_apply(struct i2c_client *client) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + u8 voutctrl[3]; + unsigned int i; + + for (i = 0; i < 3; i++) + voutctrl[i] = cx25840_read(client, 0x404 + i); + + if (state->vid_config & CX25840_VCONFIG_FMT_MASK) + voutctrl[0] &= ~3; + switch (state->vid_config & CX25840_VCONFIG_FMT_MASK) { + case CX25840_VCONFIG_FMT_BT656: + voutctrl[0] |= 1; + break; + + case CX25840_VCONFIG_FMT_VIP11: + voutctrl[0] |= 2; + break; + + case CX25840_VCONFIG_FMT_VIP2: + voutctrl[0] |= 3; + break; + + case CX25840_VCONFIG_FMT_BT601: + /* zero */ + default: + break; + } + + CX25840_VCONFIG_SET_BIT(state, CX25840_VCONFIG_RES_MASK, voutctrl, + 0, 2, CX25840_VCONFIG_RES_10BIT); + CX25840_VCONFIG_SET_BIT(state, CX25840_VCONFIG_VBIRAW_MASK, voutctrl, + 0, 3, CX25840_VCONFIG_VBIRAW_ENABLED); + CX25840_VCONFIG_SET_BIT(state, CX25840_VCONFIG_ANCDATA_MASK, voutctrl, + 0, 4, CX25840_VCONFIG_ANCDATA_ENABLED); + CX25840_VCONFIG_SET_BIT(state, CX25840_VCONFIG_TASKBIT_MASK, voutctrl, + 0, 5, CX25840_VCONFIG_TASKBIT_ONE); + CX25840_VCONFIG_SET_BIT(state, CX25840_VCONFIG_ACTIVE_MASK, voutctrl, + 1, 2, CX25840_VCONFIG_ACTIVE_HORIZONTAL); + CX25840_VCONFIG_SET_BIT(state, CX25840_VCONFIG_VALID_MASK, voutctrl, + 1, 3, CX25840_VCONFIG_VALID_ANDACTIVE); + CX25840_VCONFIG_SET_BIT(state, CX25840_VCONFIG_HRESETW_MASK, voutctrl, + 1, 4, CX25840_VCONFIG_HRESETW_PIXCLK); + + if (state->vid_config & CX25840_VCONFIG_CLKGATE_MASK) + voutctrl[1] &= ~(3 << 6); + switch (state->vid_config & CX25840_VCONFIG_CLKGATE_MASK) { + case CX25840_VCONFIG_CLKGATE_VALID: + voutctrl[1] |= 2; + break; + + case CX25840_VCONFIG_CLKGATE_VALIDACTIVE: + voutctrl[1] |= 3; + break; + + case CX25840_VCONFIG_CLKGATE_NONE: + /* zero */ + default: + break; + } + + CX25840_VCONFIG_SET_BIT(state, CX25840_VCONFIG_DCMODE_MASK, voutctrl, + 2, 0, CX25840_VCONFIG_DCMODE_BYTES); + CX25840_VCONFIG_SET_BIT(state, CX25840_VCONFIG_IDID0S_MASK, voutctrl, + 2, 1, CX25840_VCONFIG_IDID0S_LINECNT); + CX25840_VCONFIG_SET_BIT(state, CX25840_VCONFIG_VIPCLAMP_MASK, voutctrl, + 2, 4, CX25840_VCONFIG_VIPCLAMP_ENABLED); + + for (i = 0; i < 3; i++) + cx25840_write(client, 0x404 + i, voutctrl[i]); +} + static void cx25840_initialize(struct i2c_client *client) { DEFINE_WAIT(wait); @@ -389,8 +699,10 @@ static void cx25840_initialize(struct i2c_client *client) /* datasheet startup in numbered steps, refer to page 3-77 */ /* 2. */ cx25840_and_or(client, 0x803, ~0x10, 0x00); - /* The default of this register should be 4, but I get 0 instead. - * Set this register to 4 manually. */ + /* + * The default of this register should be 4, but I get 0 instead. + * Set this register to 4 manually. + */ cx25840_write(client, 0x000, 0x04); /* 3. */ init_dll1(client); @@ -400,10 +712,12 @@ static void cx25840_initialize(struct i2c_client *client) cx25840_write(client, 0x13c, 0x01); cx25840_write(client, 0x13c, 0x00); /* 5. */ - /* Do the firmware load in a work handler to prevent. - Otherwise the kernel is blocked waiting for the - bit-banging i2c interface to finish uploading the - firmware. */ + /* + * Do the firmware load in a work handler to prevent. + * Otherwise the kernel is blocked waiting for the + * bit-banging i2c interface to finish uploading the + * firmware. + */ INIT_WORK(&state->fw_work, cx25840_work_handler); init_waitqueue_head(&state->fw_wait); q = create_singlethread_workqueue("cx25840_fw"); @@ -446,6 +760,9 @@ static void cx25840_initialize(struct i2c_client *client) /* (re)set input */ set_input(client, state->vid_input, state->aud_input); + if (state->generic_mode) + cx25840_vconfig_apply(client); + /* start microcontroller */ cx25840_and_or(client, 0x803, ~0x10, 0x10); } @@ -632,10 +949,12 @@ static void cx23885_initialize(struct i2c_client *client) cx25840_write(client, 0x160, 0x1d); cx25840_write(client, 0x164, 0x00); - /* Do the firmware load in a work handler to prevent. - Otherwise the kernel is blocked waiting for the - bit-banging i2c interface to finish uploading the - firmware. */ + /* + * Do the firmware load in a work handler to prevent. + * Otherwise the kernel is blocked waiting for the + * bit-banging i2c interface to finish uploading the + * firmware. + */ INIT_WORK(&state->fw_work, cx25840_work_handler); init_waitqueue_head(&state->fw_wait); q = create_singlethread_workqueue("cx25840_fw"); @@ -647,7 +966,8 @@ static void cx23885_initialize(struct i2c_client *client) destroy_workqueue(q); } - /* Call the cx23888 specific std setup func, we no longer rely on + /* + * Call the cx23888 specific std setup func, we no longer rely on * the generic cx24840 func. */ if (is_cx23888(state)) @@ -669,7 +989,9 @@ static void cx23885_initialize(struct i2c_client *client) cx25840_write(client, CX25840_AUD_INT_STAT_REG, 0xff); /* CC raw enable */ - /* - VIP 1.1 control codes - 10bit, blue field enable. + + /* + * - VIP 1.1 control codes - 10bit, blue field enable. * - enable raw data during vertical blanking. * - enable ancillary Data insertion for 656 or VIP. */ @@ -752,10 +1074,12 @@ static void cx231xx_initialize(struct i2c_client *client) /* White crush, Chroma AGC & Chroma Killer enabled */ cx25840_write(client, 0x401, 0xe8); - /* Do the firmware load in a work handler to prevent. - Otherwise the kernel is blocked waiting for the - bit-banging i2c interface to finish uploading the - firmware. */ + /* + * Do the firmware load in a work handler to prevent. + * Otherwise the kernel is blocked waiting for the + * bit-banging i2c interface to finish uploading the + * firmware. + */ INIT_WORK(&state->fw_work, cx25840_work_handler); init_waitqueue_head(&state->fw_wait); q = create_singlethread_workqueue("cx25840_fw"); @@ -800,13 +1124,20 @@ void cx25840_std_setup(struct i2c_client *client) else cx25840_write(client, 0x49f, 0x14); + /* generic mode uses the values that the chip autoconfig would set */ if (std & V4L2_STD_625_50) { hblank = 132; hactive = 720; burst = 93; - vblank = 36; - vactive = 580; - vblank656 = 40; + if (state->generic_mode) { + vblank = 34; + vactive = 576; + vblank656 = 38; + } else { + vblank = 36; + vactive = 580; + vblank656 = 40; + } src_decimation = 0x21f; luma_lpf = 2; @@ -815,6 +1146,10 @@ void cx25840_std_setup(struct i2c_client *client) comb = 0; sc = 0x0a425f; } else if (std == V4L2_STD_PAL_Nc) { + if (state->generic_mode) { + burst = 95; + luma_lpf = 1; + } uv_lpf = 1; comb = 0x20; sc = 556453; @@ -829,12 +1164,20 @@ void cx25840_std_setup(struct i2c_client *client) vactive = 487; luma_lpf = 1; uv_lpf = 1; + if (state->generic_mode) { + vblank = 20; + vblank656 = 24; + } src_decimation = 0x21f; if (std == V4L2_STD_PAL_60) { - vblank = 26; - vblank656 = 26; - burst = 0x5b; + if (!state->generic_mode) { + vblank = 26; + vblank656 = 26; + burst = 0x5b; + } else { + burst = 0x59; + } luma_lpf = 2; comb = 0x20; sc = 688739; @@ -845,8 +1188,10 @@ void cx25840_std_setup(struct i2c_client *client) comb = 0x20; sc = 555452; } else { - vblank = 26; - vblank656 = 26; + if (!state->generic_mode) { + vblank = 26; + vblank656 = 26; + } burst = 0x5b; comb = 0x66; sc = 556063; @@ -867,24 +1212,28 @@ void cx25840_std_setup(struct i2c_client *client) int pll = (28636363L * ((((u64)pll_int) << 25L) + pll_frac)) >> 25L; pll /= pll_post; - v4l_dbg(1, cx25840_debug, client, "PLL = %d.%06d MHz\n", - pll / 1000000, pll % 1000000); - v4l_dbg(1, cx25840_debug, client, "PLL/8 = %d.%06d MHz\n", - pll / 8000000, (pll / 8) % 1000000); + v4l_dbg(1, cx25840_debug, client, + "PLL = %d.%06d MHz\n", + pll / 1000000, pll % 1000000); + v4l_dbg(1, cx25840_debug, client, + "PLL/8 = %d.%06d MHz\n", + pll / 8000000, (pll / 8) % 1000000); fin = ((u64)src_decimation * pll) >> 12; v4l_dbg(1, cx25840_debug, client, - "ADC Sampling freq = %d.%06d MHz\n", - fin / 1000000, fin % 1000000); + "ADC Sampling freq = %d.%06d MHz\n", + fin / 1000000, fin % 1000000); fsc = (((u64)sc) * pll) >> 24L; v4l_dbg(1, cx25840_debug, client, - "Chroma sub-carrier freq = %d.%06d MHz\n", - fsc / 1000000, fsc % 1000000); + "Chroma sub-carrier freq = %d.%06d MHz\n", + fsc / 1000000, fsc % 1000000); - v4l_dbg(1, cx25840_debug, client, "hblank %i, hactive %i, vblank %i, vactive %i, vblank656 %i, src_dec %i, burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x, sc 0x%06x\n", + v4l_dbg(1, cx25840_debug, client, + "hblank %i, hactive %i, vblank %i, vactive %i, vblank656 %i, src_dec %i, burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x, sc 0x%06x\n", hblank, hactive, vblank, vactive, vblank656, - src_decimation, burst, luma_lpf, uv_lpf, comb, sc); + src_decimation, burst, luma_lpf, uv_lpf, + comb, sc); } } @@ -939,10 +1288,10 @@ static void input_change(struct i2c_client *client) /* Follow step 8c and 8d of section 3.16 in the cx25840 datasheet */ if (std & V4L2_STD_SECAM) { cx25840_write(client, 0x402, 0); - } - else { + } else { cx25840_write(client, 0x402, 0x04); - cx25840_write(client, 0x49f, (std & V4L2_STD_NTSC) ? 0x14 : 0x11); + cx25840_write(client, 0x49f, + (std & V4L2_STD_NTSC) ? 0x14 : 0x11); } cx25840_and_or(client, 0x401, ~0x60, 0); cx25840_and_or(client, 0x401, ~0x60, 0x60); @@ -956,13 +1305,14 @@ static void input_change(struct i2c_client *client) if (state->radio) { cx25840_write(client, 0x808, 0xf9); cx25840_write(client, 0x80b, 0x00); - } - else if (std & V4L2_STD_525_60) { - /* Certain Hauppauge PVR150 models have a hardware bug - that causes audio to drop out. For these models the - audio standard must be set explicitly. - To be precise: it affects cards with tuner models - 85, 99 and 112 (model numbers from tveeprom). */ + } else if (std & V4L2_STD_525_60) { + /* + * Certain Hauppauge PVR150 models have a hardware bug + * that causes audio to drop out. For these models the + * audio standard must be set explicitly. + * To be precise: it affects cards with tuner models + * 85, 99 and 112 (model numbers from tveeprom). + */ int hw_fix = state->pvr150_workaround; if (std == V4L2_STD_NTSC_M_JP) { @@ -979,35 +1329,40 @@ static void input_change(struct i2c_client *client) } else if (std & V4L2_STD_PAL) { /* Autodetect audio standard and audio system */ cx25840_write(client, 0x808, 0xff); - /* Since system PAL-L is pretty much non-existent and - not used by any public broadcast network, force - 6.5 MHz carrier to be interpreted as System DK, - this avoids DK audio detection instability */ + /* + * Since system PAL-L is pretty much non-existent and + * not used by any public broadcast network, force + * 6.5 MHz carrier to be interpreted as System DK, + * this avoids DK audio detection instability + */ cx25840_write(client, 0x80b, 0x00); } else if (std & V4L2_STD_SECAM) { /* Autodetect audio standard and audio system */ cx25840_write(client, 0x808, 0xff); - /* If only one of SECAM-DK / SECAM-L is required, then force - 6.5MHz carrier, else autodetect it */ + /* + * If only one of SECAM-DK / SECAM-L is required, then force + * 6.5MHz carrier, else autodetect it + */ if ((std & V4L2_STD_SECAM_DK) && !(std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC))) { /* 6.5 MHz carrier to be interpreted as System DK */ cx25840_write(client, 0x80b, 0x00); - } else if (!(std & V4L2_STD_SECAM_DK) && - (std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC))) { + } else if (!(std & V4L2_STD_SECAM_DK) && + (std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC))) { /* 6.5 MHz carrier to be interpreted as System L */ cx25840_write(client, 0x80b, 0x08); - } else { + } else { /* 6.5 MHz carrier to be autodetected */ cx25840_write(client, 0x80b, 0x10); - } + } } cx25840_and_or(client, 0x810, ~0x01, 0); } -static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input, - enum cx25840_audio_input aud_input) +static int set_input(struct i2c_client *client, + enum cx25840_video_input vid_input, + enum cx25840_audio_input aud_input) { struct cx25840_state *state = to_state(i2c_get_clientdata(client)); u8 is_composite = (vid_input >= CX25840_COMPOSITE1 && @@ -1032,7 +1387,7 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp vid_input); reg = vid_input & 0xff; is_composite = !is_component && - ((vid_input & CX25840_SVIDEO_ON) != CX25840_SVIDEO_ON); + ((vid_input & CX25840_SVIDEO_ON) != CX25840_SVIDEO_ON); v4l_dbg(1, cx25840_debug, client, "mux cfg 0x%x comp=%d\n", reg, is_composite); @@ -1040,8 +1395,10 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp reg = 0xf0 + (vid_input - CX25840_COMPOSITE1); } else { if ((vid_input & ~0xff0) || - luma < CX25840_SVIDEO_LUMA1 || luma > CX25840_SVIDEO_LUMA8 || - chroma < CX25840_SVIDEO_CHROMA4 || chroma > CX25840_SVIDEO_CHROMA8) { + luma < CX25840_SVIDEO_LUMA1 || + luma > CX25840_SVIDEO_LUMA8 || + chroma < CX25840_SVIDEO_CHROMA4 || + chroma > CX25840_SVIDEO_CHROMA8) { v4l_err(client, "0x%04x is not a valid video input!\n", vid_input); return -EINVAL; @@ -1065,12 +1422,24 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp case CX25840_AUDIO_SERIAL: /* do nothing, use serial audio input */ break; - case CX25840_AUDIO4: reg &= ~0x30; break; - case CX25840_AUDIO5: reg &= ~0x30; reg |= 0x10; break; - case CX25840_AUDIO6: reg &= ~0x30; reg |= 0x20; break; - case CX25840_AUDIO7: reg &= ~0xc0; break; - case CX25840_AUDIO8: reg &= ~0xc0; reg |= 0x40; break; - + case CX25840_AUDIO4: + reg &= ~0x30; + break; + case CX25840_AUDIO5: + reg &= ~0x30; + reg |= 0x10; + break; + case CX25840_AUDIO6: + reg &= ~0x30; + reg |= 0x20; + break; + case CX25840_AUDIO7: + reg &= ~0xc0; + break; + case CX25840_AUDIO8: + reg &= ~0xc0; + reg |= 0x40; + break; default: v4l_err(client, "0x%04x is not a valid audio input!\n", aud_input); @@ -1087,7 +1456,6 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp cx25840_and_or(client, 0x401, ~0x6, is_composite ? 0 : 0x02); if (is_cx2388x(state)) { - /* Enable or disable the DIF for tuner use */ if (is_dif) { cx25840_and_or(client, 0x102, ~0x80, 0x80); @@ -1118,15 +1486,23 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp cx25840_write4(client, 0x410, 0xffff0dbf); cx25840_write4(client, 0x414, 0x00137d03); - cx25840_write4(client, state->vbi_regs_offset + 0x42c, 0x42600000); - cx25840_write4(client, state->vbi_regs_offset + 0x430, 0x0000039b); - cx25840_write4(client, state->vbi_regs_offset + 0x438, 0x00000000); - - cx25840_write4(client, state->vbi_regs_offset + 0x440, 0xF8E3E824); - cx25840_write4(client, state->vbi_regs_offset + 0x444, 0x401040dc); - cx25840_write4(client, state->vbi_regs_offset + 0x448, 0xcd3f02a0); - cx25840_write4(client, state->vbi_regs_offset + 0x44c, 0x161f1000); - cx25840_write4(client, state->vbi_regs_offset + 0x450, 0x00000802); + cx25840_write4(client, state->vbi_regs_offset + 0x42c, + 0x42600000); + cx25840_write4(client, state->vbi_regs_offset + 0x430, + 0x0000039b); + cx25840_write4(client, state->vbi_regs_offset + 0x438, + 0x00000000); + + cx25840_write4(client, state->vbi_regs_offset + 0x440, + 0xF8E3E824); + cx25840_write4(client, state->vbi_regs_offset + 0x444, + 0x401040dc); + cx25840_write4(client, state->vbi_regs_offset + 0x448, + 0xcd3f02a0); + cx25840_write4(client, state->vbi_regs_offset + 0x44c, + 0x161f1000); + cx25840_write4(client, state->vbi_regs_offset + 0x450, + 0x00000802); cx25840_write4(client, 0x91c, 0x01000000); cx25840_write4(client, 0x8e0, 0x03063870); @@ -1193,8 +1569,9 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp * Only one of the two will be in use. */ cx25840_write4(client, AFE_CTRL, val); - } else + } else { cx25840_and_or(client, 0x102, ~0x2, 0); + } } state->vid_input = vid_input; @@ -1233,29 +1610,32 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp cx25840_write(client, 0x919, 0x01); } - if (is_cx2388x(state) && ((aud_input == CX25840_AUDIO7) || - (aud_input == CX25840_AUDIO6))) { + if (is_cx2388x(state) && + ((aud_input == CX25840_AUDIO7) || (aud_input == CX25840_AUDIO6))) { /* Configure audio from LR1 or LR2 input */ cx25840_write4(client, 0x910, 0); cx25840_write4(client, 0x8d0, 0x63073); - } else - if (is_cx2388x(state) && (aud_input == CX25840_AUDIO8)) { + } else if (is_cx2388x(state) && (aud_input == CX25840_AUDIO8)) { /* Configure audio from tuner/sif input */ cx25840_write4(client, 0x910, 0x12b000c9); cx25840_write4(client, 0x8d0, 0x1f063870); } if (is_cx23888(state)) { - /* HVR1850 */ - /* AUD_IO_CTRL - I2S Input, Parallel1*/ - /* - Channel 1 src - Parallel1 (Merlin out) */ - /* - Channel 2 src - Parallel2 (Merlin out) */ - /* - Channel 3 src - Parallel3 (Merlin AC97 out) */ - /* - I2S source and dir - Merlin, output */ + /* + * HVR1850 + * + * AUD_IO_CTRL - I2S Input, Parallel1 + * - Channel 1 src - Parallel1 (Merlin out) + * - Channel 2 src - Parallel2 (Merlin out) + * - Channel 3 src - Parallel3 (Merlin AC97 out) + * - I2S source and dir - Merlin, output + */ cx25840_write4(client, 0x124, 0x100); if (!is_dif) { - /* Stop microcontroller if we don't need it + /* + * Stop microcontroller if we don't need it * to avoid audio popping on svideo/composite use. */ cx25840_and_or(client, 0x803, ~0x10, 0x00); @@ -1297,11 +1677,14 @@ static int set_v4lstd(struct i2c_client *client) fmt = 0xc; } - v4l_dbg(1, cx25840_debug, client, "changing video std to fmt %i\n",fmt); + v4l_dbg(1, cx25840_debug, client, + "changing video std to fmt %i\n", fmt); - /* Follow step 9 of section 3.16 in the cx25840 datasheet. - Without this PAL may display a vertical ghosting effect. - This happens for example with the Yuan MPC622. */ + /* + * Follow step 9 of section 3.16 in the cx25840 datasheet. + * Without this PAL may display a vertical ghosting effect. + * This happens for example with the Yuan MPC622. + */ if (fmt >= 4 && fmt < 8) { /* Set format to NTSC-M */ cx25840_and_or(client, 0x400, ~0xf, 1); @@ -1363,14 +1746,15 @@ static int cx25840_s_ctrl(struct v4l2_ctrl *ctrl) /* ----------------------------------------------------------------------- */ static int cx25840_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *fmt = &format->format; struct cx25840_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - int HSC, VSC, Vsrc, Hsrc, filter, Vlines; - int is_50Hz = !(state->std & V4L2_STD_525_60); + u32 hsc, vsc, v_src, h_src, v_add; + int filter; + int is_50hz = !(state->std & V4L2_STD_525_60); if (format->pad || fmt->code != MEDIA_BUS_FMT_FIXED) return -EINVAL; @@ -1379,42 +1763,63 @@ static int cx25840_set_fmt(struct v4l2_subdev *sd, fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; if (is_cx23888(state)) { - Vsrc = (cx25840_read(client, 0x42a) & 0x3f) << 4; - Vsrc |= (cx25840_read(client, 0x429) & 0xf0) >> 4; + v_src = (cx25840_read(client, 0x42a) & 0x3f) << 4; + v_src |= (cx25840_read(client, 0x429) & 0xf0) >> 4; } else { - Vsrc = (cx25840_read(client, 0x476) & 0x3f) << 4; - Vsrc |= (cx25840_read(client, 0x475) & 0xf0) >> 4; + v_src = (cx25840_read(client, 0x476) & 0x3f) << 4; + v_src |= (cx25840_read(client, 0x475) & 0xf0) >> 4; } if (is_cx23888(state)) { - Hsrc = (cx25840_read(client, 0x426) & 0x3f) << 4; - Hsrc |= (cx25840_read(client, 0x425) & 0xf0) >> 4; + h_src = (cx25840_read(client, 0x426) & 0x3f) << 4; + h_src |= (cx25840_read(client, 0x425) & 0xf0) >> 4; } else { - Hsrc = (cx25840_read(client, 0x472) & 0x3f) << 4; - Hsrc |= (cx25840_read(client, 0x471) & 0xf0) >> 4; + h_src = (cx25840_read(client, 0x472) & 0x3f) << 4; + h_src |= (cx25840_read(client, 0x471) & 0xf0) >> 4; } - Vlines = fmt->height + (is_50Hz ? 4 : 7); + if (!state->generic_mode) { + v_add = is_50hz ? 4 : 7; - /* - * We keep 1 margin for the Vsrc < Vlines check since the - * cx23888 reports a Vsrc of 486 instead of 487 for the NTSC - * height. Without that margin the cx23885 fails in this - * check. - */ - if ((fmt->width == 0) || (Vlines == 0) || - (fmt->width * 16 < Hsrc) || (Hsrc < fmt->width) || - (Vlines * 8 < Vsrc) || (Vsrc + 1 < Vlines)) { - v4l_err(client, "%dx%d is not a valid size!\n", - fmt->width, fmt->height); - return -ERANGE; + /* + * cx23888 in 525-line mode is programmed for 486 active lines + * while other chips use 487 active lines. + * + * See reg 0x428 bits [21:12] in cx23888_std_setup() vs + * vactive in cx25840_std_setup(). + */ + if (is_cx23888(state) && !is_50hz) + v_add--; + } else { + v_add = 0; } + + if (h_src == 0 || + v_src <= v_add) { + v4l_err(client, + "chip reported picture size (%u x %u) is far too small\n", + (unsigned int)h_src, (unsigned int)v_src); + /* + * that's the best we can do since the output picture + * size is completely unknown in this case + */ + return -EINVAL; + } + + fmt->width = clamp(fmt->width, (h_src + 15) / 16, h_src); + + if (v_add * 8 >= v_src) + fmt->height = clamp(fmt->height, (u32)1, v_src - v_add); + else + fmt->height = clamp(fmt->height, (v_src - v_add * 8 + 7) / 8, + v_src - v_add); + if (format->which == V4L2_SUBDEV_FORMAT_TRY) return 0; - HSC = (Hsrc * (1 << 20)) / fmt->width - (1 << 20); - VSC = (1 << 16) - (Vsrc * (1 << 9) / Vlines - (1 << 9)); - VSC &= 0x1fff; + hsc = (h_src * (1 << 20)) / fmt->width - (1 << 20); + vsc = (1 << 16) - (v_src * (1 << 9) / (fmt->height + v_add) - (1 << 9)); + vsc &= 0x1fff; if (fmt->width >= 385) filter = 0; @@ -1425,21 +1830,23 @@ static int cx25840_set_fmt(struct v4l2_subdev *sd, else filter = 3; - v4l_dbg(1, cx25840_debug, client, "decoder set size %dx%d -> scale %ux%u\n", - fmt->width, fmt->height, HSC, VSC); + v4l_dbg(1, cx25840_debug, client, + "decoder set size %u x %u with scale %x x %x\n", + (unsigned int)fmt->width, (unsigned int)fmt->height, + (unsigned int)hsc, (unsigned int)vsc); - /* HSCALE=HSC */ + /* HSCALE=hsc */ if (is_cx23888(state)) { - cx25840_write4(client, 0x434, HSC | (1 << 24)); - /* VSCALE=VSC VS_INTRLACE=1 VFILT=filter */ - cx25840_write4(client, 0x438, VSC | (1 << 19) | (filter << 16)); + cx25840_write4(client, 0x434, hsc | (1 << 24)); + /* VSCALE=vsc VS_INTRLACE=1 VFILT=filter */ + cx25840_write4(client, 0x438, vsc | (1 << 19) | (filter << 16)); } else { - cx25840_write(client, 0x418, HSC & 0xff); - cx25840_write(client, 0x419, (HSC >> 8) & 0xff); - cx25840_write(client, 0x41a, HSC >> 16); - /* VSCALE=VSC */ - cx25840_write(client, 0x41c, VSC & 0xff); - cx25840_write(client, 0x41d, VSC >> 8); + cx25840_write(client, 0x418, hsc & 0xff); + cx25840_write(client, 0x419, (hsc >> 8) & 0xff); + cx25840_write(client, 0x41a, hsc >> 16); + /* VSCALE=vsc */ + cx25840_write(client, 0x41c, vsc & 0xff); + cx25840_write(client, 0x41d, vsc >> 8); /* VS_INTRLACE=1 VFILT=filter */ cx25840_write(client, 0x41e, 0x8 | filter); } @@ -1466,23 +1873,25 @@ static void log_video_status(struct i2c_client *client) int vid_input = state->vid_input; v4l_info(client, "Video signal: %spresent\n", - (gen_stat2 & 0x20) ? "" : "not "); + (gen_stat2 & 0x20) ? "" : "not "); v4l_info(client, "Detected format: %s\n", - fmt_strs[gen_stat1 & 0xf]); + fmt_strs[gen_stat1 & 0xf]); v4l_info(client, "Specified standard: %s\n", - vidfmt_sel ? fmt_strs[vidfmt_sel] : "automatic detection"); + vidfmt_sel ? fmt_strs[vidfmt_sel] : "automatic detection"); if (vid_input >= CX25840_COMPOSITE1 && vid_input <= CX25840_COMPOSITE8) { v4l_info(client, "Specified video input: Composite %d\n", - vid_input - CX25840_COMPOSITE1 + 1); + vid_input - CX25840_COMPOSITE1 + 1); } else { - v4l_info(client, "Specified video input: S-Video (Luma In%d, Chroma In%d)\n", - (vid_input & 0xf0) >> 4, (vid_input & 0xf00) >> 8); + v4l_info(client, + "Specified video input: S-Video (Luma In%d, Chroma In%d)\n", + (vid_input & 0xf0) >> 4, (vid_input & 0xf00) >> 8); } - v4l_info(client, "Specified audioclock freq: %d Hz\n", state->audclk_freq); + v4l_info(client, "Specified audioclock freq: %d Hz\n", + state->audclk_freq); } /* ----------------------------------------------------------------------- */ @@ -1501,177 +1910,434 @@ static void log_audio_status(struct i2c_client *client) char *p; switch (mod_det_stat0) { - case 0x00: p = "mono"; break; - case 0x01: p = "stereo"; break; - case 0x02: p = "dual"; break; - case 0x04: p = "tri"; break; - case 0x10: p = "mono with SAP"; break; - case 0x11: p = "stereo with SAP"; break; - case 0x12: p = "dual with SAP"; break; - case 0x14: p = "tri with SAP"; break; - case 0xfe: p = "forced mode"; break; - default: p = "not defined"; + case 0x00: + p = "mono"; + break; + case 0x01: + p = "stereo"; + break; + case 0x02: + p = "dual"; + break; + case 0x04: + p = "tri"; + break; + case 0x10: + p = "mono with SAP"; + break; + case 0x11: + p = "stereo with SAP"; + break; + case 0x12: + p = "dual with SAP"; + break; + case 0x14: + p = "tri with SAP"; + break; + case 0xfe: + p = "forced mode"; + break; + default: + p = "not defined"; } v4l_info(client, "Detected audio mode: %s\n", p); switch (mod_det_stat1) { - case 0x00: p = "not defined"; break; - case 0x01: p = "EIAJ"; break; - case 0x02: p = "A2-M"; break; - case 0x03: p = "A2-BG"; break; - case 0x04: p = "A2-DK1"; break; - case 0x05: p = "A2-DK2"; break; - case 0x06: p = "A2-DK3"; break; - case 0x07: p = "A1 (6.0 MHz FM Mono)"; break; - case 0x08: p = "AM-L"; break; - case 0x09: p = "NICAM-BG"; break; - case 0x0a: p = "NICAM-DK"; break; - case 0x0b: p = "NICAM-I"; break; - case 0x0c: p = "NICAM-L"; break; - case 0x0d: p = "BTSC/EIAJ/A2-M Mono (4.5 MHz FMMono)"; break; - case 0x0e: p = "IF FM Radio"; break; - case 0x0f: p = "BTSC"; break; - case 0x10: p = "high-deviation FM"; break; - case 0x11: p = "very high-deviation FM"; break; - case 0xfd: p = "unknown audio standard"; break; - case 0xfe: p = "forced audio standard"; break; - case 0xff: p = "no detected audio standard"; break; - default: p = "not defined"; + case 0x00: + p = "not defined"; + break; + case 0x01: + p = "EIAJ"; + break; + case 0x02: + p = "A2-M"; + break; + case 0x03: + p = "A2-BG"; + break; + case 0x04: + p = "A2-DK1"; + break; + case 0x05: + p = "A2-DK2"; + break; + case 0x06: + p = "A2-DK3"; + break; + case 0x07: + p = "A1 (6.0 MHz FM Mono)"; + break; + case 0x08: + p = "AM-L"; + break; + case 0x09: + p = "NICAM-BG"; + break; + case 0x0a: + p = "NICAM-DK"; + break; + case 0x0b: + p = "NICAM-I"; + break; + case 0x0c: + p = "NICAM-L"; + break; + case 0x0d: + p = "BTSC/EIAJ/A2-M Mono (4.5 MHz FMMono)"; + break; + case 0x0e: + p = "IF FM Radio"; + break; + case 0x0f: + p = "BTSC"; + break; + case 0x10: + p = "high-deviation FM"; + break; + case 0x11: + p = "very high-deviation FM"; + break; + case 0xfd: + p = "unknown audio standard"; + break; + case 0xfe: + p = "forced audio standard"; + break; + case 0xff: + p = "no detected audio standard"; + break; + default: + p = "not defined"; } v4l_info(client, "Detected audio standard: %s\n", p); v4l_info(client, "Audio microcontroller: %s\n", - (download_ctl & 0x10) ? - ((mute_ctl & 0x2) ? "detecting" : "running") : "stopped"); + (download_ctl & 0x10) ? + ((mute_ctl & 0x2) ? "detecting" : "running") : "stopped"); switch (audio_config >> 4) { - case 0x00: p = "undefined"; break; - case 0x01: p = "BTSC"; break; - case 0x02: p = "EIAJ"; break; - case 0x03: p = "A2-M"; break; - case 0x04: p = "A2-BG"; break; - case 0x05: p = "A2-DK1"; break; - case 0x06: p = "A2-DK2"; break; - case 0x07: p = "A2-DK3"; break; - case 0x08: p = "A1 (6.0 MHz FM Mono)"; break; - case 0x09: p = "AM-L"; break; - case 0x0a: p = "NICAM-BG"; break; - case 0x0b: p = "NICAM-DK"; break; - case 0x0c: p = "NICAM-I"; break; - case 0x0d: p = "NICAM-L"; break; - case 0x0e: p = "FM radio"; break; - case 0x0f: p = "automatic detection"; break; - default: p = "undefined"; + case 0x00: + p = "undefined"; + break; + case 0x01: + p = "BTSC"; + break; + case 0x02: + p = "EIAJ"; + break; + case 0x03: + p = "A2-M"; + break; + case 0x04: + p = "A2-BG"; + break; + case 0x05: + p = "A2-DK1"; + break; + case 0x06: + p = "A2-DK2"; + break; + case 0x07: + p = "A2-DK3"; + break; + case 0x08: + p = "A1 (6.0 MHz FM Mono)"; + break; + case 0x09: + p = "AM-L"; + break; + case 0x0a: + p = "NICAM-BG"; + break; + case 0x0b: + p = "NICAM-DK"; + break; + case 0x0c: + p = "NICAM-I"; + break; + case 0x0d: + p = "NICAM-L"; + break; + case 0x0e: + p = "FM radio"; + break; + case 0x0f: + p = "automatic detection"; + break; + default: + p = "undefined"; } v4l_info(client, "Configured audio standard: %s\n", p); if ((audio_config >> 4) < 0xF) { switch (audio_config & 0xF) { - case 0x00: p = "MONO1 (LANGUAGE A/Mono L+R channel for BTSC, EIAJ, A2)"; break; - case 0x01: p = "MONO2 (LANGUAGE B)"; break; - case 0x02: p = "MONO3 (STEREO forced MONO)"; break; - case 0x03: p = "MONO4 (NICAM ANALOG-Language C/Analog Fallback)"; break; - case 0x04: p = "STEREO"; break; - case 0x05: p = "DUAL1 (AB)"; break; - case 0x06: p = "DUAL2 (AC) (FM)"; break; - case 0x07: p = "DUAL3 (BC) (FM)"; break; - case 0x08: p = "DUAL4 (AC) (AM)"; break; - case 0x09: p = "DUAL5 (BC) (AM)"; break; - case 0x0a: p = "SAP"; break; - default: p = "undefined"; + case 0x00: + p = "MONO1 (LANGUAGE A/Mono L+R channel for BTSC, EIAJ, A2)"; + break; + case 0x01: + p = "MONO2 (LANGUAGE B)"; + break; + case 0x02: + p = "MONO3 (STEREO forced MONO)"; + break; + case 0x03: + p = "MONO4 (NICAM ANALOG-Language C/Analog Fallback)"; + break; + case 0x04: + p = "STEREO"; + break; + case 0x05: + p = "DUAL1 (AB)"; + break; + case 0x06: + p = "DUAL2 (AC) (FM)"; + break; + case 0x07: + p = "DUAL3 (BC) (FM)"; + break; + case 0x08: + p = "DUAL4 (AC) (AM)"; + break; + case 0x09: + p = "DUAL5 (BC) (AM)"; + break; + case 0x0a: + p = "SAP"; + break; + default: + p = "undefined"; } v4l_info(client, "Configured audio mode: %s\n", p); } else { switch (audio_config & 0xF) { - case 0x00: p = "BG"; break; - case 0x01: p = "DK1"; break; - case 0x02: p = "DK2"; break; - case 0x03: p = "DK3"; break; - case 0x04: p = "I"; break; - case 0x05: p = "L"; break; - case 0x06: p = "BTSC"; break; - case 0x07: p = "EIAJ"; break; - case 0x08: p = "A2-M"; break; - case 0x09: p = "FM Radio"; break; - case 0x0f: p = "automatic standard and mode detection"; break; - default: p = "undefined"; + case 0x00: + p = "BG"; + break; + case 0x01: + p = "DK1"; + break; + case 0x02: + p = "DK2"; + break; + case 0x03: + p = "DK3"; + break; + case 0x04: + p = "I"; + break; + case 0x05: + p = "L"; + break; + case 0x06: + p = "BTSC"; + break; + case 0x07: + p = "EIAJ"; + break; + case 0x08: + p = "A2-M"; + break; + case 0x09: + p = "FM Radio"; + break; + case 0x0f: + p = "automatic standard and mode detection"; + break; + default: + p = "undefined"; } v4l_info(client, "Configured audio system: %s\n", p); } if (aud_input) { - v4l_info(client, "Specified audio input: Tuner (In%d)\n", aud_input); + v4l_info(client, "Specified audio input: Tuner (In%d)\n", + aud_input); } else { v4l_info(client, "Specified audio input: External\n"); } switch (pref_mode & 0xf) { - case 0: p = "mono/language A"; break; - case 1: p = "language B"; break; - case 2: p = "language C"; break; - case 3: p = "analog fallback"; break; - case 4: p = "stereo"; break; - case 5: p = "language AC"; break; - case 6: p = "language BC"; break; - case 7: p = "language AB"; break; - default: p = "undefined"; + case 0: + p = "mono/language A"; + break; + case 1: + p = "language B"; + break; + case 2: + p = "language C"; + break; + case 3: + p = "analog fallback"; + break; + case 4: + p = "stereo"; + break; + case 5: + p = "language AC"; + break; + case 6: + p = "language BC"; + break; + case 7: + p = "language AB"; + break; + default: + p = "undefined"; } v4l_info(client, "Preferred audio mode: %s\n", p); if ((audio_config & 0xf) == 0xf) { switch ((afc0 >> 3) & 0x3) { - case 0: p = "system DK"; break; - case 1: p = "system L"; break; - case 2: p = "autodetect"; break; - default: p = "undefined"; + case 0: + p = "system DK"; + break; + case 1: + p = "system L"; + break; + case 2: + p = "autodetect"; + break; + default: + p = "undefined"; } v4l_info(client, "Selected 65 MHz format: %s\n", p); switch (afc0 & 0x7) { - case 0: p = "chroma"; break; - case 1: p = "BTSC"; break; - case 2: p = "EIAJ"; break; - case 3: p = "A2-M"; break; - case 4: p = "autodetect"; break; - default: p = "undefined"; + case 0: + p = "chroma"; + break; + case 1: + p = "BTSC"; + break; + case 2: + p = "EIAJ"; + break; + case 3: + p = "A2-M"; + break; + case 4: + p = "autodetect"; + break; + default: + p = "undefined"; } v4l_info(client, "Selected 45 MHz format: %s\n", p); } } +#define CX25840_VCONFIG_OPTION(state, cfg_in, opt_msk) \ + do { \ + if ((cfg_in) & (opt_msk)) { \ + (state)->vid_config &= ~(opt_msk); \ + (state)->vid_config |= (cfg_in) & (opt_msk); \ + } \ + } while (0) + +/* apply incoming options to the current vconfig */ +static void cx25840_vconfig_add(struct cx25840_state *state, u32 cfg_in) +{ + CX25840_VCONFIG_OPTION(state, cfg_in, CX25840_VCONFIG_FMT_MASK); + CX25840_VCONFIG_OPTION(state, cfg_in, CX25840_VCONFIG_RES_MASK); + CX25840_VCONFIG_OPTION(state, cfg_in, CX25840_VCONFIG_VBIRAW_MASK); + CX25840_VCONFIG_OPTION(state, cfg_in, CX25840_VCONFIG_ANCDATA_MASK); + CX25840_VCONFIG_OPTION(state, cfg_in, CX25840_VCONFIG_TASKBIT_MASK); + CX25840_VCONFIG_OPTION(state, cfg_in, CX25840_VCONFIG_ACTIVE_MASK); + CX25840_VCONFIG_OPTION(state, cfg_in, CX25840_VCONFIG_VALID_MASK); + CX25840_VCONFIG_OPTION(state, cfg_in, CX25840_VCONFIG_HRESETW_MASK); + CX25840_VCONFIG_OPTION(state, cfg_in, CX25840_VCONFIG_CLKGATE_MASK); + CX25840_VCONFIG_OPTION(state, cfg_in, CX25840_VCONFIG_DCMODE_MASK); + CX25840_VCONFIG_OPTION(state, cfg_in, CX25840_VCONFIG_IDID0S_MASK); + CX25840_VCONFIG_OPTION(state, cfg_in, CX25840_VCONFIG_VIPCLAMP_MASK); +} + /* ----------------------------------------------------------------------- */ -/* This load_fw operation must be called to load the driver's firmware. - Without this the audio standard detection will fail and you will - only get mono. +/* + * Initializes the device in the generic mode. + * For cx2584x chips also adds additional video output settings provided + * in @val parameter (CX25840_VCONFIG_*). + * + * The generic mode disables some of the ivtv-related hacks in this driver. + * For cx2584x chips it also enables setting video output configuration while + * setting it according to datasheet defaults by default. + */ +static int cx25840_init(struct v4l2_subdev *sd, u32 val) +{ + struct cx25840_state *state = to_state(sd); - Since loading the firmware is often problematic when the driver is - compiled into the kernel I recommend postponing calling this function - until the first open of the video device. Another reason for - postponing it is that loading this firmware takes a long time (seconds) - due to the slow i2c bus speed. So it will speed up the boot process if - you can avoid loading the fw as long as the video device isn't used. */ -static int cx25840_load_fw(struct v4l2_subdev *sd) + state->generic_mode = true; + + if (is_cx2584x(state)) { + /* set datasheet video output defaults */ + state->vid_config = CX25840_VCONFIG_FMT_BT656 | + CX25840_VCONFIG_RES_8BIT | + CX25840_VCONFIG_VBIRAW_DISABLED | + CX25840_VCONFIG_ANCDATA_ENABLED | + CX25840_VCONFIG_TASKBIT_ONE | + CX25840_VCONFIG_ACTIVE_HORIZONTAL | + CX25840_VCONFIG_VALID_NORMAL | + CX25840_VCONFIG_HRESETW_NORMAL | + CX25840_VCONFIG_CLKGATE_NONE | + CX25840_VCONFIG_DCMODE_DWORDS | + CX25840_VCONFIG_IDID0S_NORMAL | + CX25840_VCONFIG_VIPCLAMP_DISABLED; + + /* add additional settings */ + cx25840_vconfig_add(state, val); + } else { + /* TODO: generic mode needs to be developed for other chips */ + WARN_ON(1); + } + + return 0; +} + +static int cx25840_reset(struct v4l2_subdev *sd, u32 val) { struct cx25840_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); + if (is_cx2583x(state)) + cx25836_initialize(client); + else if (is_cx2388x(state)) + cx23885_initialize(client); + else if (is_cx231xx(state)) + cx231xx_initialize(client); + else + cx25840_initialize(client); + + state->is_initialized = 1; + + return 0; +} + +/* + * This load_fw operation must be called to load the driver's firmware. + * This will load the firmware on the first invocation (further ones are NOP). + * Without this the audio standard detection will fail and you will + * only get mono. + * Alternatively, you can call the reset operation instead of this one. + * + * Since loading the firmware is often problematic when the driver is + * compiled into the kernel I recommend postponing calling this function + * until the first open of the video device. Another reason for + * postponing it is that loading this firmware takes a long time (seconds) + * due to the slow i2c bus speed. So it will speed up the boot process if + * you can avoid loading the fw as long as the video device isn't used. + */ +static int cx25840_load_fw(struct v4l2_subdev *sd) +{ + struct cx25840_state *state = to_state(sd); + if (!state->is_initialized) { /* initialize and load firmware */ - state->is_initialized = 1; - if (is_cx2583x(state)) - cx25836_initialize(client); - else if (is_cx2388x(state)) - cx23885_initialize(client); - else if (is_cx231xx(state)) - cx231xx_initialize(client); - else - cx25840_initialize(client); + cx25840_reset(sd, 0); } return 0; } #ifdef CONFIG_VIDEO_ADV_DEBUG -static int cx25840_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +static int cx25840_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -1680,7 +2346,8 @@ static int cx25840_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register * return 0; } -static int cx25840_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) +static int cx25840_s_register(struct v4l2_subdev *sd, + const struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -1699,7 +2366,7 @@ static int cx25840_s_audio_stream(struct v4l2_subdev *sd, int enable) return 0; v4l_dbg(1, cx25840_debug, client, "%s audio output\n", - enable ? "enable" : "disable"); + enable ? "enable" : "disable"); if (enable) { v = cx25840_read(client, 0x115) | 0x80; @@ -1722,7 +2389,7 @@ static int cx25840_s_stream(struct v4l2_subdev *sd, int enable) u8 v; v4l_dbg(1, cx25840_debug, client, "%s video output\n", - enable ? "enable" : "disable"); + enable ? "enable" : "disable"); /* * It's not clear what should be done for these devices. @@ -1749,7 +2416,7 @@ static int cx25840_s_stream(struct v4l2_subdev *sd, int enable) } /* Query the current detected video format */ -static int cx25840_g_std(struct v4l2_subdev *sd, v4l2_std_id *std) +static int cx25840_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -1775,10 +2442,11 @@ static int cx25840_g_std(struct v4l2_subdev *sd, v4l2_std_id *std) }; u32 fmt = (cx25840_read4(client, 0x40c) >> 8) & 0xf; - *std = stds[ fmt ]; + *std = stds[fmt]; - v4l_dbg(1, cx25840_debug, client, "g_std fmt = %x, v4l2_std_id = 0x%x\n", - fmt, (unsigned int)stds[ fmt ]); + v4l_dbg(1, cx25840_debug, client, + "querystd fmt = %x, v4l2_std_id = 0x%x\n", + fmt, (unsigned int)stds[fmt]); return 0; } @@ -1787,7 +2455,8 @@ static int cx25840_g_input_status(struct v4l2_subdev *sd, u32 *status) { struct i2c_client *client = v4l2_get_subdevdata(sd); - /* A limited function that checks for signal status and returns + /* + * A limited function that checks for signal status and returns * the state. */ @@ -1798,6 +2467,15 @@ static int cx25840_g_input_status(struct v4l2_subdev *sd, u32 *status) return 0; } +static int cx25840_g_std(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + struct cx25840_state *state = to_state(sd); + + *std = state->std; + + return 0; +} + static int cx25840_s_std(struct v4l2_subdev *sd, v4l2_std_id std) { struct cx25840_state *state = to_state(sd); @@ -1827,6 +2505,11 @@ static int cx25840_s_video_routing(struct v4l2_subdev *sd, if (is_cx23888(state)) cx23888_std_setup(client); + if (is_cx2584x(state) && state->generic_mode && config) { + cx25840_vconfig_add(state, config); + cx25840_vconfig_apply(client); + } + return set_input(client, input, state->aud_input); } @@ -1841,7 +2524,8 @@ static int cx25840_s_audio_routing(struct v4l2_subdev *sd, return set_input(client, state->vid_input, input); } -static int cx25840_s_frequency(struct v4l2_subdev *sd, const struct v4l2_frequency *freq) +static int cx25840_s_frequency(struct v4l2_subdev *sd, + const struct v4l2_frequency *freq) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -1864,9 +2548,8 @@ static int cx25840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) if (is_cx2583x(state)) return 0; - vt->capability |= - V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | - V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; + vt->capability |= V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | + V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; mode = cx25840_read(client, 0x804); @@ -1896,54 +2579,46 @@ static int cx25840_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *vt) return 0; switch (vt->audmode) { - case V4L2_TUNER_MODE_MONO: - /* mono -> mono - stereo -> mono - bilingual -> lang1 */ - cx25840_and_or(client, 0x809, ~0xf, 0x00); - break; - case V4L2_TUNER_MODE_STEREO: - case V4L2_TUNER_MODE_LANG1: - /* mono -> mono - stereo -> stereo - bilingual -> lang1 */ - cx25840_and_or(client, 0x809, ~0xf, 0x04); - break; - case V4L2_TUNER_MODE_LANG1_LANG2: - /* mono -> mono - stereo -> stereo - bilingual -> lang1/lang2 */ - cx25840_and_or(client, 0x809, ~0xf, 0x07); - break; - case V4L2_TUNER_MODE_LANG2: - /* mono -> mono - stereo -> stereo - bilingual -> lang2 */ - cx25840_and_or(client, 0x809, ~0xf, 0x01); - break; - default: - return -EINVAL; + case V4L2_TUNER_MODE_MONO: + /* + * mono -> mono + * stereo -> mono + * bilingual -> lang1 + */ + cx25840_and_or(client, 0x809, ~0xf, 0x00); + break; + case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1: + /* + * mono -> mono + * stereo -> stereo + * bilingual -> lang1 + */ + cx25840_and_or(client, 0x809, ~0xf, 0x04); + break; + case V4L2_TUNER_MODE_LANG1_LANG2: + /* + * mono -> mono + * stereo -> stereo + * bilingual -> lang1/lang2 + */ + cx25840_and_or(client, 0x809, ~0xf, 0x07); + break; + case V4L2_TUNER_MODE_LANG2: + /* + * mono -> mono + * stereo -> stereo + * bilingual -> lang2 + */ + cx25840_and_or(client, 0x809, ~0xf, 0x01); + break; + default: + return -EINVAL; } state->audmode = vt->audmode; return 0; } -static int cx25840_reset(struct v4l2_subdev *sd, u32 val) -{ - struct cx25840_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (is_cx2583x(state)) - cx25836_initialize(client); - else if (is_cx2388x(state)) - cx23885_initialize(client); - else if (is_cx231xx(state)) - cx231xx_initialize(client); - else - cx25840_initialize(client); - return 0; -} - static int cx25840_log_status(struct v4l2_subdev *sd) { struct cx25840_state *state = to_state(sd); @@ -5050,6 +5725,8 @@ static const struct v4l2_ctrl_ops cx25840_ctrl_ops = { static const struct v4l2_subdev_core_ops cx25840_core_ops = { .log_status = cx25840_log_status, .reset = cx25840_reset, + /* calling the (optional) init op will turn on the generic mode */ + .init = cx25840_init, .load_fw = cx25840_load_fw, .s_io_pin_config = common_s_io_pin_config, #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -5073,8 +5750,9 @@ static const struct v4l2_subdev_audio_ops cx25840_audio_ops = { }; static const struct v4l2_subdev_video_ops cx25840_video_ops = { - .s_std = cx25840_s_std, .g_std = cx25840_g_std, + .s_std = cx25840_s_std, + .querystd = cx25840_querystd, .s_routing = cx25840_s_video_routing, .s_stream = cx25840_s_stream, .g_input_status = cx25840_g_input_status, @@ -5110,22 +5788,28 @@ static u32 get_cx2388x_ident(struct i2c_client *client) /* Come out of digital power down */ cx25840_write(client, 0x000, 0); - /* Detecting whether the part is cx23885/7/8 is more + /* + * Detecting whether the part is cx23885/7/8 is more * difficult than it needs to be. No ID register. Instead we * probe certain registers indicated in the datasheets to look - * for specific defaults that differ between the silicon designs. */ + * for specific defaults that differ between the silicon designs. + */ /* It's either 885/7 if the IR Tx Clk Divider register exists */ if (cx25840_read4(client, 0x204) & 0xffff) { - /* CX23885 returns bogus repetitive byte values for the DIF, - * which doesn't exist for it. (Ex. 8a8a8a8a or 31313131) */ + /* + * CX23885 returns bogus repetitive byte values for the DIF, + * which doesn't exist for it. (Ex. 8a8a8a8a or 31313131) + */ ret = cx25840_read4(client, 0x300); if (((ret & 0xffff0000) >> 16) == (ret & 0xffff)) { /* No DIF */ ret = CX23885_AV; } else { - /* CX23887 has a broken DIF, but the registers - * appear valid (but unused), good enough to detect. */ + /* + * CX23887 has a broken DIF, but the registers + * appear valid (but unused), good enough to detect. + */ ret = CX23887_AV; } } else if (cx25840_read4(client, 0x300) & 0x0fffffff) { @@ -5157,14 +5841,18 @@ static int cx25840_probe(struct i2c_client *client, if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; - v4l_dbg(1, cx25840_debug, client, "detecting cx25840 client on address 0x%x\n", client->addr << 1); + v4l_dbg(1, cx25840_debug, client, + "detecting cx25840 client on address 0x%x\n", + client->addr << 1); device_id = cx25840_read(client, 0x101) << 8; device_id |= cx25840_read(client, 0x100); v4l_dbg(1, cx25840_debug, client, "device_id = 0x%04x\n", device_id); - /* The high byte of the device ID should be - * 0x83 for the cx2583x and 0x84 for the cx2584x */ + /* + * The high byte of the device ID should be + * 0x83 for the cx2583x and 0x84 for the cx2584x + */ if ((device_id & 0xff00) == 0x8300) { id = CX25836 + ((device_id >> 4) & 0xf) - 6; } else if ((device_id & 0xff00) == 0x8400) { @@ -5178,7 +5866,8 @@ static int cx25840_probe(struct i2c_client *client, v4l_err(client, "likely a confused/unresponsive cx2388[578] A/V decoder found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); - v4l_err(client, "A method to reset it from the cx25840 driver software is not known at this time\n"); + v4l_err(client, + "A method to reset it from the cx25840 driver software is not known at this time\n"); return -ENODEV; } else { v4l_dbg(1, cx25840_debug, client, "cx25840 not found\n"); @@ -5186,7 +5875,7 @@ static int cx25840_probe(struct i2c_client *client, } state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL); - if (state == NULL) + if (!state) return -ENOMEM; sd = &state->sd; @@ -5213,7 +5902,7 @@ static int cx25840_probe(struct i2c_client *client, sd->entity.function = MEDIA_ENT_F_ATV_DECODER; ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(state->pads), - state->pads); + state->pads); if (ret < 0) { v4l_info(client, "failed to initialize media entity!\n"); return ret; @@ -5241,8 +5930,10 @@ static int cx25840_probe(struct i2c_client *client, case CX25841: case CX25842: case CX25843: - /* Note: revision '(device_id & 0x0f) == 2' was never built. The - marking skips from 0x1 == 22 to 0x3 == 23. */ + /* + * Note: revision '(device_id & 0x0f) == 2' was never built. + * The marking skips from 0x1 == 22 to 0x3 == 23. + */ v4l_info(client, "cx25%3x-2%x found @ 0x%x (%s)\n", (device_id & 0xfff0) >> 4, (device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1 @@ -5270,13 +5961,13 @@ static int cx25840_probe(struct i2c_client *client, state->std = V4L2_STD_NTSC_M; v4l2_ctrl_handler_init(&state->hdl, 9); v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops, - V4L2_CID_CONTRAST, 0, 127, 1, 64); + V4L2_CID_CONTRAST, 0, 127, 1, 64); v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops, - V4L2_CID_SATURATION, 0, 127, 1, 64); + V4L2_CID_SATURATION, 0, 127, 1, 64); v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops, - V4L2_CID_HUE, -128, 127, 1, 0); + V4L2_CID_HUE, -128, 127, 1, 0); if (!is_cx2583x(state)) { default_volume = cx25840_read(client, 0x8d4); /* @@ -5288,8 +5979,7 @@ static int cx25840_probe(struct i2c_client *client, /* Bottom out at -96 dB, v4l2 vol range 0x2e00-0x2fff */ default_volume = 228; cx25840_write(client, 0x8d4, 228); - } - else if (default_volume < 20) { + } else if (default_volume < 20) { /* Top out at + 8 dB, v4l2 vol range 0xfe00-0xffff */ default_volume = 20; cx25840_write(client, 0x8d4, 20); @@ -5297,20 +5987,23 @@ static int cx25840_probe(struct i2c_client *client, default_volume = (((228 - default_volume) >> 1) + 23) << 9; state->volume = v4l2_ctrl_new_std(&state->hdl, - &cx25840_audio_ctrl_ops, V4L2_CID_AUDIO_VOLUME, - 0, 65535, 65535 / 100, default_volume); + &cx25840_audio_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, + 0, 65535, 65535 / 100, + default_volume); state->mute = v4l2_ctrl_new_std(&state->hdl, - &cx25840_audio_ctrl_ops, V4L2_CID_AUDIO_MUTE, - 0, 1, 1, 0); + &cx25840_audio_ctrl_ops, + V4L2_CID_AUDIO_MUTE, + 0, 1, 1, 0); v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops, - V4L2_CID_AUDIO_BALANCE, - 0, 65535, 65535 / 100, 32768); + V4L2_CID_AUDIO_BALANCE, + 0, 65535, 65535 / 100, 32768); v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops, - V4L2_CID_AUDIO_BASS, - 0, 65535, 65535 / 100, 32768); + V4L2_CID_AUDIO_BASS, + 0, 65535, 65535 / 100, 32768); v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops, - V4L2_CID_AUDIO_TREBLE, - 0, 65535, 65535 / 100, 32768); + V4L2_CID_AUDIO_TREBLE, + 0, 65535, 65535 / 100, 32768); } sd->ctrl_handler = &state->hdl; if (state->hdl.error) { diff --git a/drivers/media/i2c/cx25840/cx25840-core.h b/drivers/media/i2c/cx25840/cx25840-core.h index 7fa5787635ea..8b89e90687a1 100644 --- a/drivers/media/i2c/cx25840/cx25840-core.h +++ b/drivers/media/i2c/cx25840/cx25840-core.h @@ -7,7 +7,6 @@ #ifndef _CX25840_CORE_H_ #define _CX25840_CORE_H_ - #include <linux/videodev2.h> #include <media/v4l2-device.h> #include <media/v4l2-ctrls.h> @@ -44,10 +43,15 @@ enum cx25840_media_pads { * @mute: audio mute V4L2 control (non-cx2583x devices only) * @pvr150_workaround: whether we enable workaround for Hauppauge PVR150 * hardware bug (audio dropping out) + * @generic_mode: whether we disable ivtv-specific hacks + * this mode gets turned on when the bridge driver calls + * cx25840 subdevice init core op * @radio: set if we are currently in the radio mode, otherwise * the current mode is non-radio (that is, video) * @std: currently set video standard * @vid_input: currently set video input + * @vid_config: currently set video output configuration + * only used in the generic mode * @aud_input: currently set audio input * @audclk_freq: currently set audio sample rate * @audmode: currently set audio mode (when in non-radio mode) @@ -74,9 +78,11 @@ struct cx25840_state { struct v4l2_ctrl *mute; }; int pvr150_workaround; + bool generic_mode; int radio; v4l2_std_id std; enum cx25840_video_input vid_input; + u32 vid_config; enum cx25840_audio_input aud_input; u32 audclk_freq; int audmode; @@ -84,7 +90,7 @@ struct cx25840_state { enum cx25840_model id; u32 rev; int is_initialized; - unsigned vbi_regs_offset; + unsigned int vbi_regs_offset; wait_queue_head_t fw_wait; struct work_struct fw_work; struct cx25840_ir_state *ir_state; @@ -109,6 +115,14 @@ static inline bool is_cx2583x(struct cx25840_state *state) state->id == CX25837; } +static inline bool is_cx2584x(struct cx25840_state *state) +{ + return state->id == CX25840 || + state->id == CX25841 || + state->id == CX25842 || + state->id == CX25843; +} + static inline bool is_cx231xx(struct cx25840_state *state) { return state->id == CX2310X_AV; @@ -142,7 +156,8 @@ int cx25840_write(struct i2c_client *client, u16 addr, u8 value); int cx25840_write4(struct i2c_client *client, u16 addr, u32 value); u8 cx25840_read(struct i2c_client *client, u16 addr); u32 cx25840_read4(struct i2c_client *client, u16 addr); -int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned mask, u8 value); +int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned int mask, + u8 value); int cx25840_and_or4(struct i2c_client *client, u16 addr, u32 and_mask, u32 or_value); void cx25840_std_setup(struct i2c_client *client); @@ -161,9 +176,12 @@ extern const struct v4l2_ctrl_ops cx25840_audio_ctrl_ops; /* ----------------------------------------------------------------------- */ /* cx25850-vbi.c */ int cx25840_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt); -int cx25840_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt); -int cx25840_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt); -int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi); +int cx25840_s_sliced_fmt(struct v4l2_subdev *sd, + struct v4l2_sliced_vbi_format *fmt); +int cx25840_g_sliced_fmt(struct v4l2_subdev *sd, + struct v4l2_sliced_vbi_format *fmt); +int cx25840_decode_vbi_line(struct v4l2_subdev *sd, + struct v4l2_decode_vbi_line *vbi); /* ----------------------------------------------------------------------- */ /* cx25850-ir.c */ diff --git a/drivers/media/i2c/cx25840/cx25840-vbi.c b/drivers/media/i2c/cx25840/cx25840-vbi.c index 643335f0f827..a066d5f0fec9 100644 --- a/drivers/media/i2c/cx25840/cx25840-vbi.c +++ b/drivers/media/i2c/cx25840/cx25840-vbi.c @@ -86,6 +86,7 @@ int cx25840_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format * memset(svbi->service_lines, 0, sizeof(svbi->service_lines)); svbi->service_set = 0; /* we're done if raw VBI is active */ + /* TODO: this will have to be changed for generic_mode VBI */ if ((cx25840_read(client, 0x404) & 0x10) == 0) return 0; @@ -128,6 +129,7 @@ int cx25840_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt) cx25840_write(client, 0x54f, vbi_offset); else cx25840_write(client, 0x47f, vbi_offset); + /* TODO: this will have to be changed for generic_mode VBI */ cx25840_write(client, 0x404, 0x2e); return 0; } @@ -148,6 +150,7 @@ int cx25840_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format * cx25840_std_setup(client); /* Sliced VBI */ + /* TODO: this will have to be changed for generic_mode VBI */ cx25840_write(client, 0x404, 0x32); /* Ancillary data */ cx25840_write(client, 0x406, 0x13); if (is_cx23888(state)) @@ -202,6 +205,7 @@ int cx25840_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format * } cx25840_write(client, state->vbi_regs_offset + 0x43c, 0x16); + /* TODO: this will have to be changed for generic_mode VBI */ if (is_cx23888(state)) cx25840_write(client, 0x428, is_pal ? 0x2a : 0x22); else diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c index 54e80a60aa57..70bb870b1d08 100644 --- a/drivers/media/i2c/ov7740.c +++ b/drivers/media/i2c/ov7740.c @@ -532,7 +532,7 @@ static int ov7740_set_ctrl(struct v4l2_ctrl *ctrl) struct i2c_client *client = v4l2_get_subdevdata(&ov7740->subdev); struct regmap *regmap = ov7740->regmap; int ret; - u8 val = 0; + u8 val; if (!pm_runtime_get_if_in_use(&client->dev)) return 0; @@ -551,6 +551,7 @@ static int ov7740_set_ctrl(struct v4l2_ctrl *ctrl) ret = ov7740_set_contrast(regmap, ctrl->val); break; case V4L2_CID_VFLIP: + val = ctrl->val ? REG0C_IMG_FLIP : 0x00; ret = regmap_update_bits(regmap, REG_REG0C, REG0C_IMG_FLIP, val); break; @@ -561,16 +562,16 @@ static int ov7740_set_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_AUTOGAIN: if (!ctrl->val) - return ov7740_set_gain(regmap, ov7740->gain->val); - - ret = ov7740_set_autogain(regmap, ctrl->val); + ret = ov7740_set_gain(regmap, ov7740->gain->val); + else + ret = ov7740_set_autogain(regmap, ctrl->val); break; case V4L2_CID_EXPOSURE_AUTO: if (ctrl->val == V4L2_EXPOSURE_MANUAL) - return ov7740_set_exp(regmap, ov7740->exposure->val); - - ret = ov7740_set_autoexp(regmap, ctrl->val); + ret = ov7740_set_exp(regmap, ov7740->exposure->val); + else + ret = ov7740_set_autoexp(regmap, ctrl->val); break; default: ret = -EINVAL; @@ -785,7 +786,11 @@ static int ov7740_try_fmt_internal(struct v4l2_subdev *sd, fsize++; } - + if (i >= ARRAY_SIZE(ov7740_framesizes)) { + fsize = &ov7740_framesizes[0]; + fmt->width = fsize->width; + fmt->height = fsize->height; + } if (ret_frmsize != NULL) *ret_frmsize = fsize; @@ -1007,8 +1012,6 @@ static int ov7740_init_controls(struct ov7740 *ov7740) ov7740->gain = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops, V4L2_CID_GAIN, 0, 1023, 1, 500); - if (ov7740->gain) - ov7740->gain->flags |= V4L2_CTRL_FLAG_VOLATILE; ov7740->auto_gain = v4l2_ctrl_new_std(ctrl_hdlr, &ov7740_ctrl_ops, V4L2_CID_AUTOGAIN, 0, 1, 1, 1); @@ -1026,7 +1029,6 @@ static int ov7740_init_controls(struct ov7740 *ov7740) v4l2_ctrl_auto_cluster(2, &ov7740->auto_gain, 0, true); v4l2_ctrl_auto_cluster(2, &ov7740->auto_exposure, V4L2_EXPOSURE_MANUAL, true); - v4l2_ctrl_cluster(2, &ov7740->hflip); if (ctrl_hdlr->error) { ret = ctrl_hdlr->error; diff --git a/drivers/media/i2c/ov8856.c b/drivers/media/i2c/ov8856.c index dbf1095b9440..cd347d6b7b9d 100644 --- a/drivers/media/i2c/ov8856.c +++ b/drivers/media/i2c/ov8856.c @@ -195,11 +195,11 @@ static const struct ov8856_reg mode_3280x2464_regs[] = { {0x3800, 0x00}, {0x3801, 0x00}, {0x3802, 0x00}, - {0x3803, 0x07}, + {0x3803, 0x06}, {0x3804, 0x0c}, {0x3805, 0xdf}, {0x3806, 0x09}, - {0x3807, 0xa6}, + {0x3807, 0xa7}, {0x3808, 0x0c}, {0x3809, 0xd0}, {0x380a, 0x09}, @@ -211,7 +211,7 @@ static const struct ov8856_reg mode_3280x2464_regs[] = { {0x3810, 0x00}, {0x3811, 0x00}, {0x3812, 0x00}, - {0x3813, 0x00}, + {0x3813, 0x01}, {0x3814, 0x01}, {0x3815, 0x01}, {0x3816, 0x00}, @@ -385,11 +385,11 @@ static const struct ov8856_reg mode_1640x1232_regs[] = { {0x3800, 0x00}, {0x3801, 0x00}, {0x3802, 0x00}, - {0x3803, 0x07}, + {0x3803, 0x06}, {0x3804, 0x0c}, {0x3805, 0xdf}, {0x3806, 0x09}, - {0x3807, 0xa6}, + {0x3807, 0xa7}, {0x3808, 0x06}, {0x3809, 0x68}, {0x380a, 0x04}, @@ -401,7 +401,7 @@ static const struct ov8856_reg mode_1640x1232_regs[] = { {0x3810, 0x00}, {0x3811, 0x00}, {0x3812, 0x00}, - {0x3813, 0x00}, + {0x3813, 0x01}, {0x3814, 0x03}, {0x3815, 0x01}, {0x3816, 0x00}, diff --git a/drivers/media/i2c/smiapp/smiapp-quirk.c b/drivers/media/i2c/smiapp/smiapp-quirk.c index e46d72cee566..ab96d6067fc3 100644 --- a/drivers/media/i2c/smiapp/smiapp-quirk.c +++ b/drivers/media/i2c/smiapp/smiapp-quirk.c @@ -194,7 +194,7 @@ static int jt8ev1_post_streamoff(struct smiapp_sensor *sensor) return rval; /* Wait for 1 ms + one line => 2 ms is likely enough */ - usleep_range(2000, 2000); + usleep_range(2000, 2050); /* Restore it */ rval = smiapp_write_8(sensor, 0x3205, 0x00); diff --git a/drivers/media/mc/Kconfig b/drivers/media/mc/Kconfig new file mode 100644 index 000000000000..3b9795cfcb36 --- /dev/null +++ b/drivers/media/mc/Kconfig @@ -0,0 +1,33 @@ +# +# Media controller +# Selectable only for webcam/grabbers, as other drivers don't use it +# + +config MEDIA_CONTROLLER + bool "Media Controller API" + depends on MEDIA_CAMERA_SUPPORT || MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT + help + Enable the media controller API used to query media devices internal + topology and configure it dynamically. + + This API is mostly used by camera interfaces in embedded platforms. + +config MEDIA_CONTROLLER_DVB + bool "Enable Media controller for DVB (EXPERIMENTAL)" + depends on MEDIA_CONTROLLER && DVB_CORE + help + Enable the media controller API support for DVB. + + This is currently experimental. + +config MEDIA_CONTROLLER_REQUEST_API + bool "Enable Media controller Request API (EXPERIMENTAL)" + depends on MEDIA_CONTROLLER && STAGING_MEDIA + help + DO NOT ENABLE THIS OPTION UNLESS YOU KNOW WHAT YOU'RE DOING. + + This option enables the Request API for the Media controller and V4L2 + interfaces. It is currently needed by a few stateless codec drivers. + + There is currently no intention to provide API or ABI stability for + this new API as of yet. diff --git a/drivers/media/mc/Makefile b/drivers/media/mc/Makefile new file mode 100644 index 000000000000..119037f0e686 --- /dev/null +++ b/drivers/media/mc/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 + +mc-objs := mc-device.o mc-devnode.o mc-entity.o \ + mc-request.o + +ifeq ($(CONFIG_USB),y) + mc-objs += mc-dev-allocator.o +endif + +obj-$(CONFIG_MEDIA_SUPPORT) += mc.o diff --git a/drivers/media/media-dev-allocator.c b/drivers/media/mc/mc-dev-allocator.c index ae17887dec59..ae17887dec59 100644 --- a/drivers/media/media-dev-allocator.c +++ b/drivers/media/mc/mc-dev-allocator.c diff --git a/drivers/media/media-device.c b/drivers/media/mc/mc-device.c index 9ae481ddd975..e19df5165e78 100644 --- a/drivers/media/media-device.c +++ b/drivers/media/mc/mc-device.c @@ -494,6 +494,7 @@ static long media_device_enum_links32(struct media_device *mdev, { struct media_links_enum links; compat_uptr_t pads_ptr, links_ptr; + int ret; memset(&links, 0, sizeof(links)); @@ -505,7 +506,14 @@ static long media_device_enum_links32(struct media_device *mdev, links.pads = compat_ptr(pads_ptr); links.links = compat_ptr(links_ptr); - return media_device_enum_links(mdev, &links); + ret = media_device_enum_links(mdev, &links); + if (ret) + return ret; + + if (copy_to_user(ulinks->reserved, links.reserved, + sizeof(ulinks->reserved))) + return -EFAULT; + return 0; } #define MEDIA_IOC_ENUM_LINKS32 _IOWR('|', 0x02, struct media_links_enum32) diff --git a/drivers/media/media-devnode.c b/drivers/media/mc/mc-devnode.c index f11382afe23b..f11382afe23b 100644 --- a/drivers/media/media-devnode.c +++ b/drivers/media/mc/mc-devnode.c diff --git a/drivers/media/media-entity.c b/drivers/media/mc/mc-entity.c index 7c429ce98bae..7c429ce98bae 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/mc/mc-entity.c diff --git a/drivers/media/media-request.c b/drivers/media/mc/mc-request.c index e3fca436c75b..e3fca436c75b 100644 --- a/drivers/media/media-request.c +++ b/drivers/media/mc/mc-request.c diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index c9ef9ff7b0bd..4f386db33a11 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -2647,8 +2647,6 @@ int cx23885_dvb_register(struct cx23885_tsport *port) dev->pci_bus, dev->pci_slot); - err = -ENODEV; - /* dvb stuff */ /* We have to init the queue for each frontend on a port. */ pr_info("%s: cx23885 based dvb card\n", dev->name); diff --git a/drivers/media/pci/ddbridge/Kconfig b/drivers/media/pci/ddbridge/Kconfig index eaac91d14654..dab34fb85c09 100644 --- a/drivers/media/pci/ddbridge/Kconfig +++ b/drivers/media/pci/ddbridge/Kconfig @@ -36,7 +36,6 @@ config DVB_DDBRIDGE_MSIENABLE bool "Enable Message Signaled Interrupts (MSI) per default (EXPERIMENTAL)" depends on DVB_DDBRIDGE depends on PCI_MSI - default n help Use PCI MSI (Message Signaled Interrupts) per default. Enabling this might lead to I2C errors originating from the bridge in conjunction diff --git a/drivers/media/pci/dt3155/Kconfig b/drivers/media/pci/dt3155/Kconfig index d678ced93f17..a3d24b8a719b 100644 --- a/drivers/media/pci/dt3155/Kconfig +++ b/drivers/media/pci/dt3155/Kconfig @@ -3,7 +3,6 @@ config VIDEO_DT3155 tristate "DT3155 frame grabber" depends on PCI && VIDEO_DEV && VIDEO_V4L2 select VIDEOBUF2_DMA_CONTIG - default n help Enables dt3155 device driver for the DataTranslation DT3155 frame grabber. Say Y here if you have this hardware. diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c index 2a52a393fe74..c1d133e17e4b 100644 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c @@ -1174,7 +1174,7 @@ static const struct v4l2_file_operations cio2_v4l2_fops = { static const struct v4l2_ioctl_ops cio2_v4l2_ioctl_ops = { .vidioc_querycap = cio2_v4l2_querycap, - .vidioc_enum_fmt_vid_cap_mplane = cio2_v4l2_enum_fmt, + .vidioc_enum_fmt_vid_cap = cio2_v4l2_enum_fmt, .vidioc_g_fmt_vid_cap_mplane = cio2_v4l2_g_fmt, .vidioc_s_fmt_vid_cap_mplane = cio2_v4l2_s_fmt, .vidioc_try_fmt_vid_cap_mplane = cio2_v4l2_try_fmt, diff --git a/drivers/media/pci/ivtv/Kconfig b/drivers/media/pci/ivtv/Kconfig index 079569955fb4..36c089103cf9 100644 --- a/drivers/media/pci/ivtv/Kconfig +++ b/drivers/media/pci/ivtv/Kconfig @@ -32,7 +32,6 @@ config VIDEO_IVTV config VIDEO_IVTV_DEPRECATED_IOCTLS bool "enable the DVB ioctls abuse on ivtv driver" depends on VIDEO_IVTV - default n help Enable the usage of the a DVB set of ioctls that were abused by IVTV driver for a while. @@ -77,7 +76,6 @@ config VIDEO_FB_IVTV config VIDEO_FB_IVTV_FORCE_PAT bool "force cx23415 framebuffer init with x86 PAT enabled" depends on VIDEO_FB_IVTV && X86_PAT - default n help With PAT enabled, the cx23415 framebuffer driver does not utilize write-combined caching on the framebuffer memory. diff --git a/drivers/media/pci/saa7164/saa7164-core.c b/drivers/media/pci/saa7164/saa7164-core.c index c594aff92e70..9ae04e18e6c6 100644 --- a/drivers/media/pci/saa7164/saa7164-core.c +++ b/drivers/media/pci/saa7164/saa7164-core.c @@ -1112,16 +1112,25 @@ static int saa7164_proc_show(struct seq_file *m, void *v) return 0; } +static struct proc_dir_entry *saa7164_pe; + static int saa7164_proc_create(void) { - struct proc_dir_entry *pe; - - pe = proc_create_single("saa7164", S_IRUGO, NULL, saa7164_proc_show); - if (!pe) + saa7164_pe = proc_create_single("saa7164", 0444, NULL, saa7164_proc_show); + if (!saa7164_pe) return -ENOMEM; return 0; } + +static void saa7164_proc_destroy(void) +{ + if (saa7164_pe) + remove_proc_entry("saa7164", NULL); +} +#else +static int saa7164_proc_create(void) { return 0; } +static void saa7164_proc_destroy(void) {} #endif static int saa7164_thread_function(void *data) @@ -1493,19 +1502,21 @@ static struct pci_driver saa7164_pci_driver = { static int __init saa7164_init(void) { - printk(KERN_INFO "saa7164 driver loaded\n"); + int ret = pci_register_driver(&saa7164_pci_driver); + + if (ret) + return ret; -#ifdef CONFIG_PROC_FS saa7164_proc_create(); -#endif - return pci_register_driver(&saa7164_pci_driver); + + pr_info("saa7164 driver loaded\n"); + + return 0; } static void __exit saa7164_fini(void) { -#ifdef CONFIG_PROC_FS - remove_proc_entry("saa7164", NULL); -#endif + saa7164_proc_destroy(); pci_unregister_driver(&saa7164_pci_driver); } diff --git a/drivers/media/pci/ttpci/av7110.c b/drivers/media/pci/ttpci/av7110.c index e6ee23544a6e..d0cdee1c6eb0 100644 --- a/drivers/media/pci/ttpci/av7110.c +++ b/drivers/media/pci/ttpci/av7110.c @@ -218,7 +218,7 @@ static void recover_arm(struct av7110 *av7110) restart_feeds(av7110); #if IS_ENABLED(CONFIG_DVB_AV7110_IR) - av7110_check_ir_config(av7110, true); + av7110_set_ir_config(av7110); #endif } @@ -250,10 +250,6 @@ static int arm_thread(void *data) if (!av7110->arm_ready) continue; -#if IS_ENABLED(CONFIG_DVB_AV7110_IR) - av7110_check_ir_config(av7110, false); -#endif - if (mutex_lock_interruptible(&av7110->dcomlock)) break; newloops = rdebi(av7110, DEBINOSWAP, STATUS_LOOPS, 0, 2); @@ -659,9 +655,11 @@ static void gpioirq(unsigned long cookie) return; case DATA_IRCOMMAND: - if (av7110->ir.ir_handler) - av7110->ir.ir_handler(av7110, - swahw32(irdebi(av7110, DEBINOSWAP, Reserved, 0, 4))); +#if IS_ENABLED(CONFIG_DVB_AV7110_IR) + av7110_ir_handler(av7110, + swahw32(irdebi(av7110, DEBINOSWAP, Reserved, + 0, 4))); +#endif iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); break; diff --git a/drivers/media/pci/ttpci/av7110.h b/drivers/media/pci/ttpci/av7110.h index 8606ef5ebbe2..809d938ae166 100644 --- a/drivers/media/pci/ttpci/av7110.h +++ b/drivers/media/pci/ttpci/av7110.h @@ -81,23 +81,11 @@ struct av7110; /* infrared remote control */ struct infrared { - u16 key_map[256]; - struct input_dev *input_dev; + struct rc_dev *rcdev; char input_phys[32]; - struct timer_list keyup_timer; - struct tasklet_struct ir_tasklet; - void (*ir_handler)(struct av7110 *av7110, u32 ircom); - u32 ir_command; u32 ir_config; - u32 device_mask; - u8 protocol; - u8 inversion; - u16 last_key; - u16 last_toggle; - bool keypressed; }; - /* place to store all the necessary device information */ struct av7110 { @@ -304,9 +292,10 @@ struct av7110 { extern int ChangePIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, u16 subpid, u16 pcrpid); -extern int av7110_check_ir_config(struct av7110 *av7110, int force); -extern int av7110_ir_init(struct av7110 *av7110); -extern void av7110_ir_exit(struct av7110 *av7110); +void av7110_ir_handler(struct av7110 *av7110, u32 ircom); +int av7110_set_ir_config(struct av7110 *av7110); +int av7110_ir_init(struct av7110 *av7110); +void av7110_ir_exit(struct av7110 *av7110); /* msp3400 i2c subaddresses */ #define MSP_WR_DEM 0x10 diff --git a/drivers/media/pci/ttpci/av7110_ir.c b/drivers/media/pci/ttpci/av7110_ir.c index dfa18878e5f0..432789a3c312 100644 --- a/drivers/media/pci/ttpci/av7110_ir.c +++ b/drivers/media/pci/ttpci/av7110_ir.c @@ -4,379 +4,156 @@ * * Copyright (C) 1999-2003 Holger Waechtler <holger@convergence.de> * Copyright (C) 2003-2007 Oliver Endriss <o.endriss@gmx.de> + * Copyright (C) 2019 Sean Young <sean@mess.org> */ - -#include <linux/types.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/proc_fs.h> #include <linux/kernel.h> -#include <linux/bitops.h> +#include <media/rc-core.h> #include "av7110.h" #include "av7110_hw.h" - -#define AV_CNT 4 - #define IR_RC5 0 #define IR_RCMM 1 #define IR_RC5_EXT 2 /* internal only */ -#define IR_ALL 0xffffffff - -#define UP_TIMEOUT (HZ*7/25) - - -/* Note: enable ir debugging by or'ing debug with 16 */ - -static int ir_protocol[AV_CNT] = { IR_RCMM, IR_RCMM, IR_RCMM, IR_RCMM}; -module_param_array(ir_protocol, int, NULL, 0644); -MODULE_PARM_DESC(ir_protocol, "Infrared protocol: 0 RC5, 1 RCMM (default)"); - -static int ir_inversion[AV_CNT]; -module_param_array(ir_inversion, int, NULL, 0644); -MODULE_PARM_DESC(ir_inversion, "Inversion of infrared signal: 0 not inverted (default), 1 inverted"); - -static uint ir_device_mask[AV_CNT] = { IR_ALL, IR_ALL, IR_ALL, IR_ALL }; -module_param_array(ir_device_mask, uint, NULL, 0644); -MODULE_PARM_DESC(ir_device_mask, "Bitmask of infrared devices: bit 0..31 = device 0..31 (default: all)"); - - -static int av_cnt; -static struct av7110 *av_list[AV_CNT]; - -static u16 default_key_map [256] = { - KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, - KEY_8, KEY_9, KEY_BACK, 0, KEY_POWER, KEY_MUTE, 0, KEY_INFO, - KEY_VOLUMEUP, KEY_VOLUMEDOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - KEY_CHANNELUP, KEY_CHANNELDOWN, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, KEY_TEXT, 0, 0, KEY_TV, 0, 0, 0, 0, 0, KEY_SETUP, 0, 0, - 0, 0, 0, KEY_SUBTITLE, 0, 0, KEY_LANGUAGE, 0, - KEY_RADIO, 0, 0, 0, 0, KEY_EXIT, 0, 0, - KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_OK, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_RED, KEY_GREEN, KEY_YELLOW, - KEY_BLUE, 0, 0, 0, 0, 0, 0, 0, KEY_MENU, KEY_LIST, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, KEY_UP, KEY_UP, KEY_DOWN, KEY_DOWN, - 0, 0, 0, 0, KEY_EPG, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_VCR -}; - - -/* key-up timer */ -static void av7110_emit_keyup(struct timer_list *t) -{ - struct infrared *ir = from_timer(ir, t, keyup_timer); - - if (!ir || !ir->keypressed) - return; - - input_report_key(ir->input_dev, ir->last_key, 0); - input_sync(ir->input_dev); - ir->keypressed = false; -} - - -/* tasklet */ -static void av7110_emit_key(unsigned long parm) +/* interrupt handler */ +void av7110_ir_handler(struct av7110 *av7110, u32 ircom) { - struct infrared *ir = (struct infrared *) parm; - u32 ircom = ir->ir_command; - u8 data; - u8 addr; - u16 toggle; - u16 keycode; - - /* extract device address and data */ - switch (ir->protocol) { - case IR_RC5: /* RC5: 5 bits device address, 6 bits data */ - data = ircom & 0x3f; - addr = (ircom >> 6) & 0x1f; - toggle = ircom & 0x0800; - break; + struct rc_dev *rcdev = av7110->ir.rcdev; + enum rc_proto proto; + u32 command, addr, scancode; + u32 toggle; - case IR_RCMM: /* RCMM: ? bits device address, ? bits data */ - data = ircom & 0xff; - addr = (ircom >> 8) & 0x1f; - toggle = ircom & 0x8000; - break; - - case IR_RC5_EXT: /* extended RC5: 5 bits device address, 7 bits data */ - data = ircom & 0x3f; - addr = (ircom >> 6) & 0x1f; - /* invert 7th data bit for backward compatibility with RC5 keymaps */ - if (!(ircom & 0x1000)) - data |= 0x40; - toggle = ircom & 0x0800; - break; - - default: - printk("%s invalid protocol %x\n", __func__, ir->protocol); - return; - } - - input_event(ir->input_dev, EV_MSC, MSC_RAW, (addr << 16) | data); - input_event(ir->input_dev, EV_MSC, MSC_SCAN, data); - - keycode = ir->key_map[data]; - - dprintk(16, "%s: code %08x -> addr %i data 0x%02x -> keycode %i\n", - __func__, ircom, addr, data, keycode); - - /* check device address */ - if (!(ir->device_mask & (1 << addr))) - return; - - if (!keycode) { - printk ("%s: code %08x -> addr %i data 0x%02x -> unknown key!\n", - __func__, ircom, addr, data); - return; - } - - if (ir->keypressed && - (ir->last_key != keycode || toggle != ir->last_toggle)) - input_event(ir->input_dev, EV_KEY, ir->last_key, 0); - - input_event(ir->input_dev, EV_KEY, keycode, 1); - input_sync(ir->input_dev); - - ir->keypressed = true; - ir->last_key = keycode; - ir->last_toggle = toggle; - - mod_timer(&ir->keyup_timer, jiffies + UP_TIMEOUT); -} - - -/* register with input layer */ -static void input_register_keys(struct infrared *ir) -{ - int i; + dprintk(4, "ir command = %08x\n", ircom); - set_bit(EV_KEY, ir->input_dev->evbit); - set_bit(EV_REP, ir->input_dev->evbit); - set_bit(EV_MSC, ir->input_dev->evbit); + if (rcdev) { + switch (av7110->ir.ir_config) { + case IR_RC5: /* RC5: 5 bits device address, 6 bits command */ + command = ircom & 0x3f; + addr = (ircom >> 6) & 0x1f; + scancode = RC_SCANCODE_RC5(addr, command); + toggle = ircom & 0x0800; + proto = RC_PROTO_RC5; + break; - set_bit(MSC_RAW, ir->input_dev->mscbit); - set_bit(MSC_SCAN, ir->input_dev->mscbit); + case IR_RCMM: /* RCMM: ? bits device address, ? bits command */ + command = ircom & 0xff; + addr = (ircom >> 8) & 0x1f; + scancode = ircom; + toggle = ircom & 0x8000; + proto = RC_PROTO_UNKNOWN; + break; - memset(ir->input_dev->keybit, 0, sizeof(ir->input_dev->keybit)); + case IR_RC5_EXT: + /* + * extended RC5: 5 bits device address, 7 bits command + * + * Extended RC5 uses only one start bit. The second + * start bit is re-assigned bit 6 of the command bit. + */ + command = ircom & 0x3f; + addr = (ircom >> 6) & 0x1f; + if (!(ircom & 0x1000)) + command |= 0x40; + scancode = RC_SCANCODE_RC5(addr, command); + toggle = ircom & 0x0800; + proto = RC_PROTO_RC5; + break; + default: + dprintk(2, "unknown ir config %d\n", + av7110->ir.ir_config); + return; + } - for (i = 0; i < ARRAY_SIZE(ir->key_map); i++) { - if (ir->key_map[i] > KEY_MAX) - ir->key_map[i] = 0; - else if (ir->key_map[i] > KEY_RESERVED) - set_bit(ir->key_map[i], ir->input_dev->keybit); + rc_keydown(rcdev, proto, scancode, toggle != 0); } - - ir->input_dev->keycode = ir->key_map; - ir->input_dev->keycodesize = sizeof(ir->key_map[0]); - ir->input_dev->keycodemax = ARRAY_SIZE(ir->key_map); } -/* check for configuration changes */ -int av7110_check_ir_config(struct av7110 *av7110, int force) +int av7110_set_ir_config(struct av7110 *av7110) { - int i; - int modified = force; - int ret = -ENODEV; - - for (i = 0; i < av_cnt; i++) - if (av7110 == av_list[i]) - break; - - if (i < av_cnt && av7110) { - if ((av7110->ir.protocol & 1) != ir_protocol[i] || - av7110->ir.inversion != ir_inversion[i]) - modified = true; - - if (modified) { - /* protocol */ - if (ir_protocol[i]) { - ir_protocol[i] = 1; - av7110->ir.protocol = IR_RCMM; - av7110->ir.ir_config = 0x0001; - } else if (FW_VERSION(av7110->arm_app) >= 0x2620) { - av7110->ir.protocol = IR_RC5_EXT; - av7110->ir.ir_config = 0x0002; - } else { - av7110->ir.protocol = IR_RC5; - av7110->ir.ir_config = 0x0000; - } - /* inversion */ - if (ir_inversion[i]) { - ir_inversion[i] = 1; - av7110->ir.ir_config |= 0x8000; - } - av7110->ir.inversion = ir_inversion[i]; - /* update ARM */ - ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1, - av7110->ir.ir_config); - } else - ret = 0; + dprintk(4, "ir config = %08x\n", av7110->ir.ir_config); - /* address */ - if (av7110->ir.device_mask != ir_device_mask[i]) - av7110->ir.device_mask = ir_device_mask[i]; - } - - return ret; + return av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1, + av7110->ir.ir_config); } - -/* /proc/av7110_ir interface */ -static ssize_t av7110_ir_proc_write(struct file *file, const char __user *buffer, - size_t count, loff_t *pos) +static int change_protocol(struct rc_dev *rcdev, u64 *rc_type) { - char *page; + struct av7110 *av7110 = rcdev->priv; u32 ir_config; - int size = sizeof ir_config + sizeof av_list[0]->ir.key_map; - int i; - if (count < size) + if (*rc_type & RC_PROTO_BIT_UNKNOWN) { + ir_config = IR_RCMM; + *rc_type = RC_PROTO_UNKNOWN; + } else if (*rc_type & RC_PROTO_BIT_RC5) { + if (FW_VERSION(av7110->arm_app) >= 0x2620) + ir_config = IR_RC5_EXT; + else + ir_config = IR_RC5; + *rc_type = RC_PROTO_BIT_RC5; + } else { return -EINVAL; - - page = vmalloc(size); - if (!page) - return -ENOMEM; - - if (copy_from_user(page, buffer, size)) { - vfree(page); - return -EFAULT; } - memcpy(&ir_config, page, sizeof ir_config); - - for (i = 0; i < av_cnt; i++) { - /* keymap */ - memcpy(av_list[i]->ir.key_map, page + sizeof ir_config, - sizeof(av_list[i]->ir.key_map)); - /* protocol, inversion, address */ - ir_protocol[i] = ir_config & 0x0001; - ir_inversion[i] = ir_config & 0x8000 ? 1 : 0; - if (ir_config & 0x4000) - ir_device_mask[i] = 1 << ((ir_config >> 16) & 0x1f); - else - ir_device_mask[i] = IR_ALL; - /* update configuration */ - av7110_check_ir_config(av_list[i], false); - input_register_keys(&av_list[i]->ir); - } - vfree(page); - return count; -} + if (ir_config == av7110->ir.ir_config) + return 0; -static const struct file_operations av7110_ir_proc_fops = { - .owner = THIS_MODULE, - .write = av7110_ir_proc_write, - .llseek = noop_llseek, -}; + av7110->ir.ir_config = ir_config; -/* interrupt handler */ -static void ir_handler(struct av7110 *av7110, u32 ircom) -{ - dprintk(4, "ir command = %08x\n", ircom); - av7110->ir.ir_command = ircom; - tasklet_schedule(&av7110->ir.ir_tasklet); + return av7110_set_ir_config(av7110); } - int av7110_ir_init(struct av7110 *av7110) { - struct input_dev *input_dev; - static struct proc_dir_entry *e; - int err; - - if (av_cnt >= ARRAY_SIZE(av_list)) - return -ENOSPC; + struct rc_dev *rcdev; + struct pci_dev *pci; + int ret; - av_list[av_cnt++] = av7110; - av7110_check_ir_config(av7110, true); - - timer_setup(&av7110->ir.keyup_timer, av7110_emit_keyup, 0); - - input_dev = input_allocate_device(); - if (!input_dev) + rcdev = rc_allocate_device(RC_DRIVER_SCANCODE); + if (!rcdev) return -ENOMEM; - av7110->ir.input_dev = input_dev; - snprintf(av7110->ir.input_phys, sizeof(av7110->ir.input_phys), - "pci-%s/ir0", pci_name(av7110->dev->pci)); + pci = av7110->dev->pci; - input_dev->name = "DVB on-card IR receiver"; - - input_dev->phys = av7110->ir.input_phys; - input_dev->id.bustype = BUS_PCI; - input_dev->id.version = 2; - if (av7110->dev->pci->subsystem_vendor) { - input_dev->id.vendor = av7110->dev->pci->subsystem_vendor; - input_dev->id.product = av7110->dev->pci->subsystem_device; + snprintf(av7110->ir.input_phys, sizeof(av7110->ir.input_phys), + "pci-%s/ir0", pci_name(pci)); + + rcdev->device_name = av7110->card_name; + rcdev->driver_name = KBUILD_MODNAME; + rcdev->input_phys = av7110->ir.input_phys; + rcdev->input_id.bustype = BUS_PCI; + rcdev->input_id.version = 2; + if (pci->subsystem_vendor) { + rcdev->input_id.vendor = pci->subsystem_vendor; + rcdev->input_id.product = pci->subsystem_device; } else { - input_dev->id.vendor = av7110->dev->pci->vendor; - input_dev->id.product = av7110->dev->pci->device; - } - input_dev->dev.parent = &av7110->dev->pci->dev; - /* initial keymap */ - memcpy(av7110->ir.key_map, default_key_map, sizeof av7110->ir.key_map); - input_register_keys(&av7110->ir); - err = input_register_device(input_dev); - if (err) { - input_free_device(input_dev); - return err; + rcdev->input_id.vendor = pci->vendor; + rcdev->input_id.product = pci->device; } - /* - * Input core's default autorepeat is 33 cps with 250 msec - * delay, let's adjust to numbers more suitable for remote - * control. - */ - input_enable_softrepeat(input_dev, 250, 125); + rcdev->dev.parent = &pci->dev; + rcdev->allowed_protocols = RC_PROTO_BIT_RC5 | RC_PROTO_BIT_UNKNOWN; + rcdev->change_protocol = change_protocol; + rcdev->map_name = RC_MAP_HAUPPAUGE; + rcdev->priv = av7110; - if (av_cnt == 1) { - e = proc_create("av7110_ir", S_IWUSR, NULL, &av7110_ir_proc_fops); - if (e) - proc_set_size(e, 4 + 256 * sizeof(u16)); - } + av7110->ir.rcdev = rcdev; + av7110->ir.ir_config = IR_RC5; + av7110_set_ir_config(av7110); - tasklet_init(&av7110->ir.ir_tasklet, av7110_emit_key, (unsigned long) &av7110->ir); - av7110->ir.ir_handler = ir_handler; + ret = rc_register_device(rcdev); + if (ret) { + av7110->ir.rcdev = NULL; + rc_free_device(rcdev); + } - return 0; + return ret; } - void av7110_ir_exit(struct av7110 *av7110) { - int i; - - if (av_cnt == 0) - return; - - del_timer_sync(&av7110->ir.keyup_timer); - av7110->ir.ir_handler = NULL; - tasklet_kill(&av7110->ir.ir_tasklet); - - for (i = 0; i < av_cnt; i++) - if (av_list[i] == av7110) { - av_list[i] = av_list[av_cnt-1]; - av_list[av_cnt-1] = NULL; - break; - } - - if (av_cnt == 1) - remove_proc_entry("av7110_ir", NULL); - - input_unregister_device(av7110->ir.input_dev); - - av_cnt--; + rc_unregister_device(av7110->ir.rcdev); } //MODULE_AUTHOR("Holger Waechtler <holger@convergence.de>, Oliver Endriss <o.endriss@gmx.de>"); diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index f2b5f27ebacb..8a19654b393a 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -6,7 +6,6 @@ menuconfig V4L_PLATFORM_DRIVERS bool "V4L platform devices" depends on MEDIA_CAMERA_SUPPORT - default n help Say Y here to enable support for platform-specific V4L drivers. @@ -155,7 +154,6 @@ config VIDEO_TI_CAL depends on SOC_DRA7XX || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG select V4L2_FWNODE - default n help Support for the TI CAL (Camera Adaptation Layer) block found on DRA72X SoC. @@ -168,7 +166,6 @@ menuconfig V4L_MEM2MEM_DRIVERS bool "Memory-to-memory multimedia devices" depends on VIDEO_V4L2 depends on MEDIA_CAMERA_SUPPORT - default n help Say Y here to enable selecting drivers for V4L devices that use system memory for both source and destination buffers, as opposed @@ -236,7 +233,6 @@ config VIDEO_MEDIATEK_MDP select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV select VIDEO_MEDIATEK_VPU - default n help It is a v4l2 driver and present in Mediatek MT8173 SoCs. The driver supports for scaling and color space conversion. @@ -252,7 +248,6 @@ config VIDEO_MEDIATEK_VCODEC select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV select VIDEO_MEDIATEK_VPU - default n help Mediatek video codec driver provides HW capability to encode and decode in a range of video formats @@ -276,7 +271,6 @@ config VIDEO_SAMSUNG_S5P_G2D depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV - default n help This is a v4l2 driver for Samsung S5P and EXYNOS4 G2D 2d graphics accelerator. @@ -296,7 +290,6 @@ config VIDEO_SAMSUNG_S5P_MFC depends on VIDEO_DEV && VIDEO_V4L2 depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG - default n help MFC 5.1 and 6.x driver for V4L2 @@ -459,7 +452,6 @@ config VIDEO_ROCKCHIP_RGA depends on ARCH_ROCKCHIP || COMPILE_TEST select VIDEOBUF2_DMA_SG select V4L2_MEM2MEM_DEV - default n help This is a v4l2 driver for Rockchip SOC RGA 2d graphics accelerator. Rockchip RGA is a separate 2D raster graphic acceleration unit. @@ -477,7 +469,6 @@ config VIDEO_TI_VPE select VIDEO_TI_VPDMA select VIDEO_TI_SC select VIDEO_TI_CSC - default n help Support for the TI VPE(Video Processing Engine) block found on DRA7XX SoC. @@ -530,7 +521,6 @@ config VIDEO_VIM2M depends on VIDEO_DEV && VIDEO_V4L2 select VIDEOBUF2_VMALLOC select V4L2_MEM2MEM_DEV - default n help This is a virtual test device for the memory-to-memory driver framework. @@ -542,7 +532,6 @@ endif #V4L_TEST_DRIVERS menuconfig DVB_PLATFORM_DRIVERS bool "DVB platform devices" depends on MEDIA_DIGITAL_TV_SUPPORT - default n help Say Y here to enable support for platform-specific Digital TV drivers. @@ -678,7 +667,6 @@ endif #CEC_PLATFORM_DRIVERS menuconfig SDR_PLATFORM_DRIVERS bool "SDR platform devices" depends on MEDIA_SDR_SUPPORT - default n help Say Y here to enable support for platform-specific SDR Drivers. diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c index 8144fe36ad48..ba093096a5a7 100644 --- a/drivers/media/platform/aspeed-video.c +++ b/drivers/media/platform/aspeed-video.c @@ -187,6 +187,7 @@ enum { VIDEO_STREAMING, VIDEO_FRAME_INPRG, VIDEO_STOPPED, + VIDEO_CLOCKS_ON, }; struct aspeed_video_addr { @@ -440,7 +441,7 @@ static int aspeed_video_start_frame(struct aspeed_video *video) if (!(seq_ctrl & VE_SEQ_CTRL_COMP_BUSY) || !(seq_ctrl & VE_SEQ_CTRL_CAP_BUSY)) { - dev_err(video->dev, "Engine busy; don't start frame\n"); + dev_dbg(video->dev, "Engine busy; don't start frame\n"); return -EBUSY; } @@ -462,8 +463,7 @@ static int aspeed_video_start_frame(struct aspeed_video *video) aspeed_video_write(video, VE_COMP_ADDR, addr); aspeed_video_update(video, VE_INTERRUPT_CTRL, 0, - VE_INTERRUPT_COMP_COMPLETE | - VE_INTERRUPT_CAPTURE_COMPLETE); + VE_INTERRUPT_COMP_COMPLETE); aspeed_video_update(video, VE_SEQ_CTRL, 0, VE_SEQ_CTRL_TRIG_CAPTURE | VE_SEQ_CTRL_TRIG_COMP); @@ -483,19 +483,30 @@ static void aspeed_video_enable_mode_detect(struct aspeed_video *video) static void aspeed_video_off(struct aspeed_video *video) { + if (!test_bit(VIDEO_CLOCKS_ON, &video->flags)) + return; + /* Disable interrupts */ aspeed_video_write(video, VE_INTERRUPT_CTRL, 0); + aspeed_video_write(video, VE_INTERRUPT_STATUS, 0xffffffff); /* Turn off the relevant clocks */ - clk_disable_unprepare(video->vclk); - clk_disable_unprepare(video->eclk); + clk_disable(video->vclk); + clk_disable(video->eclk); + + clear_bit(VIDEO_CLOCKS_ON, &video->flags); } static void aspeed_video_on(struct aspeed_video *video) { + if (test_bit(VIDEO_CLOCKS_ON, &video->flags)) + return; + /* Turn on the relevant clocks */ - clk_prepare_enable(video->eclk); - clk_prepare_enable(video->vclk); + clk_enable(video->eclk); + clk_enable(video->vclk); + + set_bit(VIDEO_CLOCKS_ON, &video->flags); } static void aspeed_video_bufs_done(struct aspeed_video *video, @@ -511,7 +522,7 @@ static void aspeed_video_bufs_done(struct aspeed_video *video, spin_unlock_irqrestore(&video->lock, flags); } -static void aspeed_video_irq_res_change(struct aspeed_video *video) +static void aspeed_video_irq_res_change(struct aspeed_video *video, ulong delay) { dev_dbg(video->dev, "Resolution changed; resetting\n"); @@ -521,7 +532,7 @@ static void aspeed_video_irq_res_change(struct aspeed_video *video) aspeed_video_off(video); aspeed_video_bufs_done(video, VB2_BUF_STATE_ERROR); - schedule_delayed_work(&video->res_work, RESOLUTION_CHANGE_DELAY); + schedule_delayed_work(&video->res_work, delay); } static irqreturn_t aspeed_video_irq(int irq, void *arg) @@ -534,7 +545,7 @@ static irqreturn_t aspeed_video_irq(int irq, void *arg) * re-initialize */ if (sts & VE_INTERRUPT_MODE_DETECT_WD) { - aspeed_video_irq_res_change(video); + aspeed_video_irq_res_change(video, 0); return IRQ_HANDLED; } @@ -544,7 +555,7 @@ static irqreturn_t aspeed_video_irq(int irq, void *arg) VE_INTERRUPT_MODE_DETECT, 0); aspeed_video_write(video, VE_INTERRUPT_STATUS, VE_INTERRUPT_MODE_DETECT); - + sts &= ~VE_INTERRUPT_MODE_DETECT; set_bit(VIDEO_MODE_DETECT_DONE, &video->flags); wake_up_interruptible_all(&video->wait); } else { @@ -552,13 +563,13 @@ static irqreturn_t aspeed_video_irq(int irq, void *arg) * Signal acquired while NOT doing resolution * detection; reset the engine and re-initialize */ - aspeed_video_irq_res_change(video); + aspeed_video_irq_res_change(video, + RESOLUTION_CHANGE_DELAY); return IRQ_HANDLED; } } - if ((sts & VE_INTERRUPT_COMP_COMPLETE) && - (sts & VE_INTERRUPT_CAPTURE_COMPLETE)) { + if (sts & VE_INTERRUPT_COMP_COMPLETE) { struct aspeed_video_buffer *buf; u32 frame_size = aspeed_video_read(video, VE_OFFSET_COMP_STREAM); @@ -587,17 +598,15 @@ static irqreturn_t aspeed_video_irq(int irq, void *arg) VE_SEQ_CTRL_FORCE_IDLE | VE_SEQ_CTRL_TRIG_COMP, 0); aspeed_video_update(video, VE_INTERRUPT_CTRL, - VE_INTERRUPT_COMP_COMPLETE | - VE_INTERRUPT_CAPTURE_COMPLETE, 0); + VE_INTERRUPT_COMP_COMPLETE, 0); aspeed_video_write(video, VE_INTERRUPT_STATUS, - VE_INTERRUPT_COMP_COMPLETE | - VE_INTERRUPT_CAPTURE_COMPLETE); - + VE_INTERRUPT_COMP_COMPLETE); + sts &= ~VE_INTERRUPT_COMP_COMPLETE; if (test_bit(VIDEO_STREAMING, &video->flags) && buf) aspeed_video_start_frame(video); } - return IRQ_HANDLED; + return sts ? IRQ_NONE : IRQ_HANDLED; } static void aspeed_video_check_and_set_polarity(struct aspeed_video *video) @@ -723,27 +732,6 @@ static void aspeed_video_get_resolution(struct aspeed_video *video) det->height = MIN_HEIGHT; video->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL; - /* - * Since we need max buffer size for detection, free the second source - * buffer first. - */ - if (video->srcs[1].size) - aspeed_video_free_buf(video, &video->srcs[1]); - - if (video->srcs[0].size < VE_MAX_SRC_BUFFER_SIZE) { - if (video->srcs[0].size) - aspeed_video_free_buf(video, &video->srcs[0]); - - if (!aspeed_video_alloc_buf(video, &video->srcs[0], - VE_MAX_SRC_BUFFER_SIZE)) { - dev_err(video->dev, - "Failed to allocate source buffers\n"); - return; - } - } - - aspeed_video_write(video, VE_SRC0_ADDR, video->srcs[0].dma); - do { if (tries) { set_current_state(TASK_INTERRUPTIBLE); @@ -758,7 +746,7 @@ static void aspeed_video_get_resolution(struct aspeed_video *video) res_check(video), MODE_DETECT_TIMEOUT); if (!rc) { - dev_err(video->dev, "Timed out; first mode detect\n"); + dev_dbg(video->dev, "Timed out; first mode detect\n"); clear_bit(VIDEO_RES_DETECT, &video->flags); return; } @@ -776,7 +764,7 @@ static void aspeed_video_get_resolution(struct aspeed_video *video) MODE_DETECT_TIMEOUT); clear_bit(VIDEO_RES_DETECT, &video->flags); if (!rc) { - dev_err(video->dev, "Timed out; second mode detect\n"); + dev_dbg(video->dev, "Timed out; second mode detect\n"); return; } @@ -810,7 +798,7 @@ static void aspeed_video_get_resolution(struct aspeed_video *video) } while (invalid_resolution && (tries++ < INVALID_RESOLUTION_RETRIES)); if (invalid_resolution) { - dev_err(video->dev, "Invalid resolution detected\n"); + dev_dbg(video->dev, "Invalid resolution detected\n"); return; } @@ -863,20 +851,14 @@ static void aspeed_video_set_resolution(struct aspeed_video *video) size *= 4; - if (size == video->srcs[0].size / 2) { - aspeed_video_write(video, VE_SRC1_ADDR, - video->srcs[0].dma + size); - } else if (size == video->srcs[0].size) { - if (!aspeed_video_alloc_buf(video, &video->srcs[1], size)) - goto err_mem; - - aspeed_video_write(video, VE_SRC1_ADDR, video->srcs[1].dma); - } else { - aspeed_video_free_buf(video, &video->srcs[0]); + if (size != video->srcs[0].size) { + if (video->srcs[0].size) + aspeed_video_free_buf(video, &video->srcs[0]); + if (video->srcs[1].size) + aspeed_video_free_buf(video, &video->srcs[1]); if (!aspeed_video_alloc_buf(video, &video->srcs[0], size)) goto err_mem; - if (!aspeed_video_alloc_buf(video, &video->srcs[1], size)) goto err_mem; @@ -1445,7 +1427,7 @@ static void aspeed_video_stop_streaming(struct vb2_queue *q) !test_bit(VIDEO_FRAME_INPRG, &video->flags), STOP_TIMEOUT); if (!rc) { - dev_err(video->dev, "Timed out when stopping streaming\n"); + dev_dbg(video->dev, "Timed out when stopping streaming\n"); /* * Need to force stop any DMA and try and get HW into a good @@ -1589,8 +1571,8 @@ static int aspeed_video_init(struct aspeed_video *video) return -ENODEV; } - rc = devm_request_irq(dev, irq, aspeed_video_irq, IRQF_SHARED, - DEVICE_NAME, video); + rc = devm_request_threaded_irq(dev, irq, NULL, aspeed_video_irq, + IRQF_ONESHOT, DEVICE_NAME, video); if (rc < 0) { dev_err(dev, "Unable to request IRQ %d\n", irq); return rc; @@ -1602,31 +1584,46 @@ static int aspeed_video_init(struct aspeed_video *video) return PTR_ERR(video->eclk); } + rc = clk_prepare(video->eclk); + if (rc) + return rc; + video->vclk = devm_clk_get(dev, "vclk"); if (IS_ERR(video->vclk)) { dev_err(dev, "Unable to get VCLK\n"); - return PTR_ERR(video->vclk); + rc = PTR_ERR(video->vclk); + goto err_unprepare_eclk; } + rc = clk_prepare(video->vclk); + if (rc) + goto err_unprepare_eclk; + of_reserved_mem_device_init(dev); rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); if (rc) { dev_err(dev, "Failed to set DMA mask\n"); - of_reserved_mem_device_release(dev); - return rc; + goto err_release_reserved_mem; } if (!aspeed_video_alloc_buf(video, &video->jpeg, VE_JPEG_HEADER_SIZE)) { dev_err(dev, "Failed to allocate DMA for JPEG header\n"); - of_reserved_mem_device_release(dev); - return rc; + goto err_release_reserved_mem; } aspeed_video_init_jpeg_table(video->jpeg.virt, video->yuv420); return 0; + +err_release_reserved_mem: + of_reserved_mem_device_release(dev); + clk_unprepare(video->vclk); +err_unprepare_eclk: + clk_unprepare(video->eclk); + + return rc; } static int aspeed_video_probe(struct platform_device *pdev) @@ -1670,6 +1667,11 @@ static int aspeed_video_remove(struct platform_device *pdev) struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); struct aspeed_video *video = to_aspeed_video(v4l2_dev); + aspeed_video_off(video); + + clk_unprepare(video->vclk); + clk_unprepare(video->eclk); + video_unregister_device(&video->vdev); vb2_queue_release(&video->queue); diff --git a/drivers/media/platform/atmel/atmel-isc-regs.h b/drivers/media/platform/atmel/atmel-isc-regs.h index 8f7f8efc71a7..c1283fb21bf6 100644 --- a/drivers/media/platform/atmel/atmel-isc-regs.h +++ b/drivers/media/platform/atmel/atmel-isc-regs.h @@ -100,13 +100,15 @@ #define ISC_WB_O_RGR 0x00000060 /* ISC White Balance Offset for B, GB Register */ -#define ISC_WB_O_BGR 0x00000064 +#define ISC_WB_O_BGB 0x00000064 /* ISC White Balance Gain for R, GR Register */ #define ISC_WB_G_RGR 0x00000068 /* ISC White Balance Gain for B, GB Register */ -#define ISC_WB_G_BGR 0x0000006c +#define ISC_WB_G_BGB 0x0000006c + +#define ISC_WB_O_ZERO_VAL (1 << 13) /* ISC Color Filter Array Control Register */ #define ISC_CFA_CTRL 0x00000070 diff --git a/drivers/media/platform/atmel/atmel-isc.c b/drivers/media/platform/atmel/atmel-isc.c index 05b9cfb91d20..6f80980214ac 100644 --- a/drivers/media/platform/atmel/atmel-isc.c +++ b/drivers/media/platform/atmel/atmel-isc.c @@ -164,15 +164,22 @@ struct isc_ctrls { u32 brightness; u32 contrast; u8 gamma_index; +#define ISC_WB_NONE 0 +#define ISC_WB_AUTO 1 +#define ISC_WB_ONETIME 2 u8 awb; - u32 r_gain; - u32 b_gain; + /* one for each component : GR, R, GB, B */ + u32 gain[HIST_BAYER]; + u32 offset[HIST_BAYER]; u32 hist_entry[HIST_ENTRIES]; u32 hist_count[HIST_BAYER]; u8 hist_id; u8 hist_stat; +#define HIST_MIN_INDEX 0 +#define HIST_MAX_INDEX 1 + u32 hist_minmax[HIST_BAYER][2]; }; #define ISC_PIPE_LINE_NODE_NUM 11 @@ -203,9 +210,11 @@ struct isc_device { struct fmt_config try_config; struct isc_ctrls ctrls; + struct v4l2_ctrl *do_wb_ctrl; struct work_struct awb_work; struct mutex lock; + spinlock_t awb_lock; struct regmap_field *pipeline[ISC_PIPE_LINE_NODE_NUM]; @@ -392,6 +401,40 @@ module_param(sensor_preferred, uint, 0644); MODULE_PARM_DESC(sensor_preferred, "Sensor is preferred to output the specified format (1-on 0-off), default 1"); +static inline void isc_update_awb_ctrls(struct isc_device *isc) +{ + struct isc_ctrls *ctrls = &isc->ctrls; + + regmap_write(isc->regmap, ISC_WB_O_RGR, + (ISC_WB_O_ZERO_VAL - (ctrls->offset[ISC_HIS_CFG_MODE_R])) | + ((ISC_WB_O_ZERO_VAL - ctrls->offset[ISC_HIS_CFG_MODE_GR]) << 16)); + regmap_write(isc->regmap, ISC_WB_O_BGB, + (ISC_WB_O_ZERO_VAL - (ctrls->offset[ISC_HIS_CFG_MODE_B])) | + ((ISC_WB_O_ZERO_VAL - ctrls->offset[ISC_HIS_CFG_MODE_GB]) << 16)); + regmap_write(isc->regmap, ISC_WB_G_RGR, + ctrls->gain[ISC_HIS_CFG_MODE_R] | + (ctrls->gain[ISC_HIS_CFG_MODE_GR] << 16)); + regmap_write(isc->regmap, ISC_WB_G_BGB, + ctrls->gain[ISC_HIS_CFG_MODE_B] | + (ctrls->gain[ISC_HIS_CFG_MODE_GB] << 16)); +} + +static inline void isc_reset_awb_ctrls(struct isc_device *isc) +{ + int c; + + for (c = ISC_HIS_CFG_MODE_GR; c <= ISC_HIS_CFG_MODE_B; c++) { + /* gains have a fixed point at 9 decimals */ + isc->ctrls.gain[c] = 1 << 9; + /* offsets are in 2's complements, the value + * will be substracted from ISC_WB_O_ZERO_VAL to obtain + * 2's complement of a value between 0 and + * ISC_WB_O_ZERO_VAL >> 1 + */ + isc->ctrls.offset[c] = ISC_WB_O_ZERO_VAL; + } +} + static int isc_wait_clk_stable(struct clk_hw *hw) { struct isc_clk *isc_clk = to_isc_clk(hw); @@ -772,7 +815,9 @@ static void isc_start_dma(struct isc_device *isc) dctrl_dview = isc->config.dctrl_dview; regmap_write(regmap, ISC_DCTRL, dctrl_dview | ISC_DCTRL_IE_IS); + spin_lock(&isc->awb_lock); regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_CAPTURE); + spin_unlock(&isc->awb_lock); } static void isc_set_pipeline(struct isc_device *isc, u32 pipeline) @@ -794,11 +839,11 @@ static void isc_set_pipeline(struct isc_device *isc, u32 pipeline) bay_cfg = isc->config.sd_format->cfa_baycfg; + if (ctrls->awb == ISC_WB_NONE) + isc_reset_awb_ctrls(isc); + regmap_write(regmap, ISC_WB_CFG, bay_cfg); - regmap_write(regmap, ISC_WB_O_RGR, 0x0); - regmap_write(regmap, ISC_WB_O_BGR, 0x0); - regmap_write(regmap, ISC_WB_G_RGR, ctrls->r_gain | (0x1 << 25)); - regmap_write(regmap, ISC_WB_G_BGR, ctrls->b_gain | (0x1 << 25)); + isc_update_awb_ctrls(isc); regmap_write(regmap, ISC_CFA_CFG, bay_cfg | ISC_CFA_CFG_EITPOL); @@ -848,13 +893,13 @@ static void isc_set_histogram(struct isc_device *isc, bool enable) if (enable) { regmap_write(regmap, ISC_HIS_CFG, - ISC_HIS_CFG_MODE_R | + ISC_HIS_CFG_MODE_GR | (isc->config.sd_format->cfa_baycfg << ISC_HIS_CFG_BAYSEL_SHIFT) | ISC_HIS_CFG_RAR); regmap_write(regmap, ISC_HIS_CTRL, ISC_HIS_CTRL_EN); regmap_write(regmap, ISC_INTEN, ISC_INT_HISDONE); - ctrls->hist_id = ISC_HIS_CFG_MODE_R; + ctrls->hist_id = ISC_HIS_CFG_MODE_GR; isc_update_profile(isc); regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ); @@ -897,7 +942,7 @@ static int isc_configure(struct isc_device *isc) isc_set_pipeline(isc, pipeline); /* - * The current implemented histogram is available for RAW R, B, GB + * The current implemented histogram is available for RAW R, B, GB, GR * channels. We need to check if sensor is outputting RAW BAYER */ if (isc->ctrls.awb && @@ -949,6 +994,10 @@ static int isc_start_streaming(struct vb2_queue *vq, unsigned int count) spin_unlock_irqrestore(&isc->dma_queue_lock, flags); + /* if we streaming from RAW, we can do one-shot white balance adj */ + if (ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code)) + v4l2_ctrl_activate(isc->do_wb_ctrl, true); + return 0; err_configure: @@ -973,6 +1022,8 @@ static void isc_stop_streaming(struct vb2_queue *vq) struct isc_buffer *buf; int ret; + v4l2_ctrl_activate(isc->do_wb_ctrl, false); + isc->stop = true; /* Wait until the end of the current frame */ @@ -1433,7 +1484,7 @@ static int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f, ret = v4l2_subdev_call(isc->current_subdev->sd, pad, set_fmt, &pad_cfg, &format); if (ret < 0) - goto isc_try_fmt_err; + goto isc_try_fmt_subdev_err; v4l2_fill_pix_format(pixfmt, &format.format); @@ -1448,6 +1499,7 @@ static int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f, isc_try_fmt_err: v4l2_err(&isc->v4l2_dev, "Could not find any possible format for a working pipeline\n"); +isc_try_fmt_subdev_err: memset(&isc->try_config, 0, sizeof(isc->try_config)); return ret; @@ -1472,6 +1524,12 @@ static int isc_set_fmt(struct isc_device *isc, struct v4l2_format *f) return ret; isc->fmt = *f; + + if (isc->try_config.sd_format && isc->config.sd_format && + isc->try_config.sd_format != isc->config.sd_format) { + isc->ctrls.hist_stat = HIST_INIT; + isc_reset_awb_ctrls(isc); + } /* make the try configuration active */ isc->config = isc->try_config; @@ -1755,7 +1813,7 @@ static irqreturn_t isc_interrupt(int irq, void *dev_id) return ret; } -static void isc_hist_count(struct isc_device *isc) +static void isc_hist_count(struct isc_device *isc, u32 *min, u32 *max) { struct regmap *regmap = isc->regmap; struct isc_ctrls *ctrls = &isc->ctrls; @@ -1763,25 +1821,99 @@ static void isc_hist_count(struct isc_device *isc) u32 *hist_entry = &ctrls->hist_entry[0]; u32 i; + *min = 0; + *max = HIST_ENTRIES; + regmap_bulk_read(regmap, ISC_HIS_ENTRY, hist_entry, HIST_ENTRIES); *hist_count = 0; - for (i = 0; i < HIST_ENTRIES; i++) + /* + * we deliberately ignore the end of the histogram, + * the most white pixels + */ + for (i = 1; i < HIST_ENTRIES; i++) { + if (*hist_entry && !*min) + *min = i; + if (*hist_entry) + *max = i; *hist_count += i * (*hist_entry++); + } + + if (!*min) + *min = 1; } static void isc_wb_update(struct isc_ctrls *ctrls) { u32 *hist_count = &ctrls->hist_count[0]; - u64 g_count = (u64)hist_count[ISC_HIS_CFG_MODE_GB] << 9; - u32 hist_r = hist_count[ISC_HIS_CFG_MODE_R]; - u32 hist_b = hist_count[ISC_HIS_CFG_MODE_B]; + u32 c, offset[4]; + u64 avg = 0; + /* We compute two gains, stretch gain and grey world gain */ + u32 s_gain[4], gw_gain[4]; + + /* + * According to Grey World, we need to set gains for R/B to normalize + * them towards the green channel. + * Thus we want to keep Green as fixed and adjust only Red/Blue + * Compute the average of the both green channels first + */ + avg = (u64)hist_count[ISC_HIS_CFG_MODE_GR] + + (u64)hist_count[ISC_HIS_CFG_MODE_GB]; + avg >>= 1; - if (hist_r) - ctrls->r_gain = div_u64(g_count, hist_r); + /* Green histogram is null, nothing to do */ + if (!avg) + return; - if (hist_b) - ctrls->b_gain = div_u64(g_count, hist_b); + for (c = ISC_HIS_CFG_MODE_GR; c <= ISC_HIS_CFG_MODE_B; c++) { + /* + * the color offset is the minimum value of the histogram. + * we stretch this color to the full range by substracting + * this value from the color component. + */ + offset[c] = ctrls->hist_minmax[c][HIST_MIN_INDEX]; + /* + * The offset is always at least 1. If the offset is 1, we do + * not need to adjust it, so our result must be zero. + * the offset is computed in a histogram on 9 bits (0..512) + * but the offset in register is based on + * 12 bits pipeline (0..4096). + * we need to shift with the 3 bits that the histogram is + * ignoring + */ + ctrls->offset[c] = (offset[c] - 1) << 3; + + /* the offset is then taken and converted to 2's complements */ + if (!ctrls->offset[c]) + ctrls->offset[c] = ISC_WB_O_ZERO_VAL; + + /* + * the stretch gain is the total number of histogram bins + * divided by the actual range of color component (Max - Min) + * If we compute gain like this, the actual color component + * will be stretched to the full histogram. + * We need to shift 9 bits for precision, we have 9 bits for + * decimals + */ + s_gain[c] = (HIST_ENTRIES << 9) / + (ctrls->hist_minmax[c][HIST_MAX_INDEX] - + ctrls->hist_minmax[c][HIST_MIN_INDEX] + 1); + + /* + * Now we have to compute the gain w.r.t. the average. + * Add/lose gain to the component towards the average. + * If it happens that the component is zero, use the + * fixed point value : 1.0 gain. + */ + if (hist_count[c]) + gw_gain[c] = div_u64(avg << 9, hist_count[c]); + else + gw_gain[c] = 1 << 9; + + /* multiply both gains and adjust for decimals */ + ctrls->gain[c] = s_gain[c] * gw_gain[c]; + ctrls->gain[c] >>= 9; + } } static void isc_awb_work(struct work_struct *w) @@ -1792,27 +1924,66 @@ static void isc_awb_work(struct work_struct *w) struct isc_ctrls *ctrls = &isc->ctrls; u32 hist_id = ctrls->hist_id; u32 baysel; + unsigned long flags; + u32 min, max; + + /* streaming is not active anymore */ + if (isc->stop) + return; if (ctrls->hist_stat != HIST_ENABLED) return; - isc_hist_count(isc); + isc_hist_count(isc, &min, &max); + ctrls->hist_minmax[hist_id][HIST_MIN_INDEX] = min; + ctrls->hist_minmax[hist_id][HIST_MAX_INDEX] = max; if (hist_id != ISC_HIS_CFG_MODE_B) { hist_id++; } else { isc_wb_update(ctrls); - hist_id = ISC_HIS_CFG_MODE_R; + hist_id = ISC_HIS_CFG_MODE_GR; } ctrls->hist_id = hist_id; baysel = isc->config.sd_format->cfa_baycfg << ISC_HIS_CFG_BAYSEL_SHIFT; + /* if no more auto white balance, reset controls. */ + if (ctrls->awb == ISC_WB_NONE) + isc_reset_awb_ctrls(isc); + pm_runtime_get_sync(isc->dev); + /* + * only update if we have all the required histograms and controls + * if awb has been disabled, we need to reset registers as well. + */ + if (hist_id == ISC_HIS_CFG_MODE_GR || ctrls->awb == ISC_WB_NONE) { + /* + * It may happen that DMA Done IRQ will trigger while we are + * updating white balance registers here. + * In that case, only parts of the controls have been updated. + * We can avoid that by locking the section. + */ + spin_lock_irqsave(&isc->awb_lock, flags); + isc_update_awb_ctrls(isc); + spin_unlock_irqrestore(&isc->awb_lock, flags); + + /* + * if we are doing just the one time white balance adjustment, + * we are basically done. + */ + if (ctrls->awb == ISC_WB_ONETIME) { + v4l2_info(&isc->v4l2_dev, + "Completed one time white-balance adjustment.\n"); + ctrls->awb = ISC_WB_NONE; + } + } regmap_write(regmap, ISC_HIS_CFG, hist_id | baysel | ISC_HIS_CFG_RAR); isc_update_profile(isc); - regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ); + /* if awb has been disabled, we don't need to start another histogram */ + if (ctrls->awb) + regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ); pm_runtime_put_sync(isc->dev); } @@ -1823,6 +1994,9 @@ static int isc_s_ctrl(struct v4l2_ctrl *ctrl) struct isc_device, ctrls.handler); struct isc_ctrls *ctrls = &isc->ctrls; + if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) + return 0; + switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: ctrls->brightness = ctrl->val & ISC_CBC_BRIGHT_MASK; @@ -1834,11 +2008,33 @@ static int isc_s_ctrl(struct v4l2_ctrl *ctrl) ctrls->gamma_index = ctrl->val; break; case V4L2_CID_AUTO_WHITE_BALANCE: - ctrls->awb = ctrl->val; - if (ctrls->hist_stat != HIST_ENABLED) { - ctrls->r_gain = 0x1 << 9; - ctrls->b_gain = 0x1 << 9; - } + if (ctrl->val == 1) + ctrls->awb = ISC_WB_AUTO; + else + ctrls->awb = ISC_WB_NONE; + + /* we did not configure ISC yet */ + if (!isc->config.sd_format) + break; + + if (ctrls->hist_stat != HIST_ENABLED) + isc_reset_awb_ctrls(isc); + + if (isc->ctrls.awb == ISC_WB_AUTO && + vb2_is_streaming(&isc->vb2_vidq) && + ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code)) + isc_set_histogram(isc, true); + + break; + case V4L2_CID_DO_WHITE_BALANCE: + /* if AWB is enabled, do nothing */ + if (ctrls->awb == ISC_WB_AUTO) + return 0; + + ctrls->awb = ISC_WB_ONETIME; + isc_set_histogram(isc, true); + v4l2_dbg(1, debug, &isc->v4l2_dev, + "One time white-balance started.\n"); break; default: return -EINVAL; @@ -1859,16 +2055,26 @@ static int isc_ctrl_init(struct isc_device *isc) int ret; ctrls->hist_stat = HIST_INIT; + isc_reset_awb_ctrls(isc); - ret = v4l2_ctrl_handler_init(hdl, 4); + ret = v4l2_ctrl_handler_init(hdl, 5); if (ret < 0) return ret; + ctrls->brightness = 0; + ctrls->contrast = 256; + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -1024, 1023, 1, 0); v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -2048, 2047, 1, 256); v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAMMA, 0, GAMMA_MAX, 1, 2); v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); + /* do_white_balance is a button, so min,max,step,default are ignored */ + isc->do_wb_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_DO_WHITE_BALANCE, + 0, 0, 0, 0); + + v4l2_ctrl_activate(isc->do_wb_ctrl, false); + v4l2_ctrl_handler_setup(hdl); return 0; @@ -2031,6 +2237,7 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier) /* Init video dma queues */ INIT_LIST_HEAD(&isc->dma_queue); spin_lock_init(&isc->dma_queue_lock); + spin_lock_init(&isc->awb_lock); ret = isc_formats_init(isc); if (ret < 0) { diff --git a/drivers/media/platform/cec-gpio/cec-gpio.c b/drivers/media/platform/cec-gpio/cec-gpio.c index d2861749d640..5b17d3a31896 100644 --- a/drivers/media/platform/cec-gpio/cec-gpio.c +++ b/drivers/media/platform/cec-gpio/cec-gpio.c @@ -17,7 +17,6 @@ struct cec_gpio { struct gpio_desc *cec_gpio; int cec_irq; bool cec_is_low; - bool cec_have_irq; struct gpio_desc *hpd_gpio; int hpd_irq; @@ -55,9 +54,6 @@ static void cec_gpio_low(struct cec_adapter *adap) if (cec->cec_is_low) return; - if (WARN_ON_ONCE(cec->cec_have_irq)) - free_irq(cec->cec_irq, cec); - cec->cec_have_irq = false; cec->cec_is_low = true; gpiod_set_value(cec->cec_gpio, 0); } @@ -114,14 +110,7 @@ static bool cec_gpio_enable_irq(struct cec_adapter *adap) { struct cec_gpio *cec = cec_get_drvdata(adap); - if (cec->cec_have_irq) - return true; - - if (request_irq(cec->cec_irq, cec_gpio_irq_handler, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - adap->name, cec)) - return false; - cec->cec_have_irq = true; + enable_irq(cec->cec_irq); return true; } @@ -129,9 +118,7 @@ static void cec_gpio_disable_irq(struct cec_adapter *adap) { struct cec_gpio *cec = cec_get_drvdata(adap); - if (cec->cec_have_irq) - free_irq(cec->cec_irq, cec); - cec->cec_have_irq = false; + disable_irq(cec->cec_irq); } static void cec_gpio_status(struct cec_adapter *adap, struct seq_file *file) @@ -139,8 +126,7 @@ static void cec_gpio_status(struct cec_adapter *adap, struct seq_file *file) struct cec_gpio *cec = cec_get_drvdata(adap); seq_printf(file, "mode: %s\n", cec->cec_is_low ? "low-drive" : "read"); - if (cec->cec_have_irq) - seq_printf(file, "using irq: %d\n", cec->cec_irq); + seq_printf(file, "using irq: %d\n", cec->cec_irq); if (cec->hpd_gpio) seq_printf(file, "hpd: %s\n", cec->hpd_is_high ? "high" : "low"); @@ -215,6 +201,14 @@ static int cec_gpio_probe(struct platform_device *pdev) if (IS_ERR(cec->adap)) return PTR_ERR(cec->adap); + ret = devm_request_irq(dev, cec->cec_irq, cec_gpio_irq_handler, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + cec->adap->name, cec); + if (ret) + return ret; + + cec_gpio_disable_irq(cec->adap); + if (cec->hpd_gpio) { cec->hpd_irq = gpiod_to_irq(cec->hpd_gpio); ret = devm_request_threaded_irq(dev, cec->hpd_irq, diff --git a/drivers/media/platform/coda/Makefile b/drivers/media/platform/coda/Makefile index f13adacd924e..54e9a73a92ab 100644 --- a/drivers/media/platform/coda/Makefile +++ b/drivers/media/platform/coda/Makefile @@ -1,7 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only -ccflags-y += -I$(src) -coda-objs := coda-common.o coda-bit.o coda-gdi.o coda-h264.o coda-jpeg.o +coda-objs := coda-common.o coda-bit.o coda-gdi.o coda-h264.o coda-mpeg2.o coda-mpeg4.o coda-jpeg.o obj-$(CONFIG_VIDEO_CODA) += coda.o obj-$(CONFIG_VIDEO_IMX_VDOA) += imx-vdoa.o diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index 976f6aa69f41..06a352659bae 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -98,6 +98,8 @@ static int coda_command_sync(struct coda_ctx *ctx, int cmd) struct coda_dev *dev = ctx->dev; int ret; + lockdep_assert_held(&dev->coda_mutex); + coda_command_async(ctx, cmd); ret = coda_wait_timeout(dev); trace_coda_bit_done(ctx); @@ -112,6 +114,8 @@ int coda_hw_reset(struct coda_ctx *ctx) unsigned int idx; int ret; + lockdep_assert_held(&dev->coda_mutex); + if (!dev->rstc) return -ENOENT; @@ -1039,7 +1043,7 @@ static int coda_start_encoding(struct coda_ctx *ctx) case V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE: value = 0; break; - case V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB: + case V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB: value = (ctx->params.slice_max_mb & CODA_SLICING_SIZE_MASK) << CODA_SLICING_SIZE_OFFSET; @@ -1047,7 +1051,7 @@ static int coda_start_encoding(struct coda_ctx *ctx) << CODA_SLICING_UNIT_OFFSET; value |= 1 & CODA_SLICING_MODE_MASK; break; - case V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES: + case V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES: value = (ctx->params.slice_max_bits & CODA_SLICING_SIZE_MASK) << CODA_SLICING_SIZE_OFFSET; @@ -1452,6 +1456,13 @@ static int coda_prepare_encode(struct coda_ctx *ctx) return 0; } +static char coda_frame_type_char(u32 flags) +{ + return (flags & V4L2_BUF_FLAG_KEYFRAME) ? 'I' : + (flags & V4L2_BUF_FLAG_PFRAME) ? 'P' : + (flags & V4L2_BUF_FLAG_BFRAME) ? 'B' : '?'; +} + static void coda_finish_encode(struct coda_ctx *ctx) { struct vb2_v4l2_buffer *src_buf, *dst_buf; @@ -1491,12 +1502,7 @@ static void coda_finish_encode(struct coda_ctx *ctx) dst_buf->flags &= ~V4L2_BUF_FLAG_KEYFRAME; } - dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp; - dst_buf->field = src_buf->field; - dst_buf->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; - dst_buf->flags |= - src_buf->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; - dst_buf->timecode = src_buf->timecode; + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false); v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); @@ -1508,8 +1514,7 @@ static void coda_finish_encode(struct coda_ctx *ctx) ctx->gopcounter = ctx->params.gop_size - 1; coda_dbg(1, ctx, "job finished: encoded %c frame (%d)\n", - (dst_buf->flags & V4L2_BUF_FLAG_KEYFRAME) ? 'I' : 'P', - dst_buf->sequence); + coda_frame_type_char(dst_buf->flags), dst_buf->sequence); } static void coda_seq_end_work(struct work_struct *work) @@ -1666,6 +1671,8 @@ static int __coda_start_decoding(struct coda_ctx *ctx) u32 val; int ret; + lockdep_assert_held(&dev->coda_mutex); + coda_dbg(1, ctx, "Video Data Order Adapter: %s\n", ctx->use_vdoa ? "Enabled" : "Disabled"); @@ -1804,6 +1811,17 @@ static int __coda_start_decoding(struct coda_ctx *ctx) (top_bottom & 0x3ff); } + if (dev->devtype->product != CODA_DX6) { + u8 profile, level; + + val = coda_read(dev, CODA7_RET_DEC_SEQ_HEADER_REPORT); + profile = val & 0xff; + level = (val >> 8) & 0x7f; + + if (profile || level) + coda_update_profile_level_ctrls(ctx, profile, level); + } + ret = coda_alloc_framebuffers(ctx, q_data_dst, src_fourcc); if (ret < 0) { v4l2_err(&dev->v4l2_dev, "failed to allocate framebuffers\n"); @@ -2236,13 +2254,36 @@ static void coda_finish_decode(struct coda_ctx *ctx) else coda_m2m_buf_done(ctx, dst_buf, VB2_BUF_STATE_DONE); - coda_dbg(1, ctx, "job finished: decoded %c frame (%u/%u)\n", - (dst_buf->flags & V4L2_BUF_FLAG_KEYFRAME) ? 'I' : - ((dst_buf->flags & V4L2_BUF_FLAG_PFRAME) ? 'P' : 'B'), - dst_buf->sequence, ctx->qsequence); + if (decoded_idx >= 0 && + decoded_idx < ctx->num_internal_frames) { + coda_dbg(1, ctx, "job finished: decoded %c frame %u, returned %c frame %u (%u/%u)%s\n", + coda_frame_type_char(ctx->frame_types[decoded_idx]), + ctx->frame_metas[decoded_idx].sequence, + coda_frame_type_char(dst_buf->flags), + ctx->frame_metas[ctx->display_idx].sequence, + dst_buf->sequence, ctx->qsequence, + (dst_buf->flags & V4L2_BUF_FLAG_LAST) ? + " (last)" : ""); + } else { + coda_dbg(1, ctx, "job finished: no frame decoded (%d), returned %c frame %u (%u/%u)%s\n", + decoded_idx, + coda_frame_type_char(dst_buf->flags), + ctx->frame_metas[ctx->display_idx].sequence, + dst_buf->sequence, ctx->qsequence, + (dst_buf->flags & V4L2_BUF_FLAG_LAST) ? + " (last)" : ""); + } } else { - coda_dbg(1, ctx, "job finished: no frame decoded (%u/%u)\n", - ctx->osequence, ctx->qsequence); + if (decoded_idx >= 0 && + decoded_idx < ctx->num_internal_frames) { + coda_dbg(1, ctx, "job finished: decoded %c frame %u, no frame returned (%d)\n", + coda_frame_type_char(ctx->frame_types[decoded_idx]), + ctx->frame_metas[decoded_idx].sequence, + ctx->display_idx); + } else { + coda_dbg(1, ctx, "job finished: no frame decoded (%d) or returned (%d)\n", + decoded_idx, ctx->display_idx); + } } /* The rotator will copy the current display frame next time */ @@ -2297,6 +2338,7 @@ irqreturn_t coda_irq_handler(int irq, void *data) /* read status register to attend the IRQ */ coda_read(dev, CODA_REG_BIT_INT_STATUS); + coda_write(dev, 0, CODA_REG_BIT_INT_REASON); coda_write(dev, CODA_REG_BIT_INT_CLEAR_SET, CODA_REG_BIT_INT_CLEAR); @@ -2304,7 +2346,6 @@ irqreturn_t coda_irq_handler(int irq, void *data) if (ctx == NULL) { v4l2_err(&dev->v4l2_dev, "Instance released before the end of transaction\n"); - mutex_unlock(&dev->coda_mutex); return IRQ_HANDLED; } diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index 6238047273f2..751b0be1c2ea 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -74,7 +74,7 @@ MODULE_PARM_DESC(enable_bwb, "Enable BWB unit for decoding, may crash on certain void coda_write(struct coda_dev *dev, u32 data, u32 reg) { - v4l2_dbg(2, coda_debug, &dev->v4l2_dev, + v4l2_dbg(3, coda_debug, &dev->v4l2_dev, "%s: data=0x%x, reg=0x%x\n", __func__, data, reg); writel(data, dev->regs_base + reg); } @@ -84,7 +84,7 @@ unsigned int coda_read(struct coda_dev *dev, u32 reg) u32 data; data = readl(dev->regs_base + reg); - v4l2_dbg(2, coda_debug, &dev->v4l2_dev, + v4l2_dbg(3, coda_debug, &dev->v4l2_dev, "%s: data=0x%x, reg=0x%x\n", __func__, data, reg); return data; } @@ -1412,7 +1412,7 @@ static int coda_job_ready(void *m2m_priv) return 0; } - coda_dbg(1, ctx, "job ready\n"); + coda_dbg(2, ctx, "job ready\n"); return 1; } @@ -1563,42 +1563,71 @@ static void coda_update_menu_ctrl(struct v4l2_ctrl *ctrl, int value) v4l2_ctrl_unlock(ctrl); } -static void coda_update_h264_profile_ctrl(struct coda_ctx *ctx) +void coda_update_profile_level_ctrls(struct coda_ctx *ctx, u8 profile_idc, + u8 level_idc) { const char * const *profile_names; + const char * const *level_names; + struct v4l2_ctrl *profile_ctrl; + struct v4l2_ctrl *level_ctrl; + const char *codec_name; + u32 profile_cid; + u32 level_cid; int profile; + int level; - profile = coda_h264_profile(ctx->params.h264_profile_idc); - if (profile < 0) { - v4l2_warn(&ctx->dev->v4l2_dev, "Invalid H264 Profile: %u\n", - ctx->params.h264_profile_idc); + switch (ctx->codec->src_fourcc) { + case V4L2_PIX_FMT_H264: + codec_name = "H264"; + profile_cid = V4L2_CID_MPEG_VIDEO_H264_PROFILE; + level_cid = V4L2_CID_MPEG_VIDEO_H264_LEVEL; + profile_ctrl = ctx->h264_profile_ctrl; + level_ctrl = ctx->h264_level_ctrl; + profile = coda_h264_profile(profile_idc); + level = coda_h264_level(level_idc); + break; + case V4L2_PIX_FMT_MPEG2: + codec_name = "MPEG-2"; + profile_cid = V4L2_CID_MPEG_VIDEO_MPEG2_PROFILE; + level_cid = V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL; + profile_ctrl = ctx->mpeg2_profile_ctrl; + level_ctrl = ctx->mpeg2_level_ctrl; + profile = coda_mpeg2_profile(profile_idc); + level = coda_mpeg2_level(level_idc); + break; + case V4L2_PIX_FMT_MPEG4: + codec_name = "MPEG-4"; + profile_cid = V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE; + level_cid = V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL; + profile_ctrl = ctx->mpeg4_profile_ctrl; + level_ctrl = ctx->mpeg4_level_ctrl; + profile = coda_mpeg4_profile(profile_idc); + level = coda_mpeg4_level(level_idc); + break; + default: return; } - coda_update_menu_ctrl(ctx->h264_profile_ctrl, profile); + profile_names = v4l2_ctrl_get_menu(profile_cid); + level_names = v4l2_ctrl_get_menu(level_cid); - profile_names = v4l2_ctrl_get_menu(V4L2_CID_MPEG_VIDEO_H264_PROFILE); - - coda_dbg(1, ctx, "Parsed H264 Profile: %s\n", profile_names[profile]); -} - -static void coda_update_h264_level_ctrl(struct coda_ctx *ctx) -{ - const char * const *level_names; - int level; + if (profile < 0) { + v4l2_warn(&ctx->dev->v4l2_dev, "Invalid %s profile: %u\n", + codec_name, profile_idc); + } else { + coda_dbg(1, ctx, "Parsed %s profile: %s\n", codec_name, + profile_names[profile]); + coda_update_menu_ctrl(profile_ctrl, profile); + } - level = coda_h264_level(ctx->params.h264_level_idc); if (level < 0) { - v4l2_warn(&ctx->dev->v4l2_dev, "Invalid H264 Level: %u\n", - ctx->params.h264_level_idc); - return; + v4l2_warn(&ctx->dev->v4l2_dev, "Invalid %s level: %u\n", + codec_name, level_idc); + } else { + coda_dbg(1, ctx, "Parsed %s level: %s\n", codec_name, + level_names[level]); + coda_update_menu_ctrl(level_ctrl, level); } - - coda_update_menu_ctrl(ctx->h264_level_ctrl, level); - - level_names = v4l2_ctrl_get_menu(V4L2_CID_MPEG_VIDEO_H264_LEVEL); - - coda_dbg(1, ctx, "Parsed H264 Level: %s\n", level_names[level]); } static void coda_buf_queue(struct vb2_buffer *vb) @@ -1631,8 +1660,9 @@ static void coda_buf_queue(struct vb2_buffer *vb) */ if (!ctx->params.h264_profile_idc) { coda_sps_parse_profile(ctx, vb); - coda_update_h264_profile_ctrl(ctx); - coda_update_h264_level_ctrl(ctx); + coda_update_profile_level_ctrls(ctx, + ctx->params.h264_profile_idc, + ctx->params.h264_level_idc); } } @@ -1853,11 +1883,16 @@ static const struct vb2_ops coda_qops = { static int coda_s_ctrl(struct v4l2_ctrl *ctrl) { + const char * const *val_names = v4l2_ctrl_get_menu(ctrl->id); struct coda_ctx *ctx = container_of(ctrl->handler, struct coda_ctx, ctrls); - coda_dbg(1, ctx, "s_ctrl: id = 0x%x, name = \"%s\", val = %d\n", - ctrl->id, ctrl->name, ctrl->val); + if (val_names) + coda_dbg(2, ctx, "s_ctrl: id = 0x%x, name = \"%s\", val = %d (\"%s\")\n", + ctrl->id, ctrl->name, ctrl->val, val_names[ctrl->val]); + else + coda_dbg(2, ctx, "s_ctrl: id = 0x%x, name = \"%s\", val = %d\n", + ctrl->id, ctrl->name, ctrl->val); switch (ctrl->id) { case V4L2_CID_HFLIP: @@ -1919,6 +1954,8 @@ static int coda_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP: ctx->params.mpeg4_inter_qp = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_MPEG2_PROFILE: + case V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL: case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: /* nothing to do, these are fixed */ @@ -2040,7 +2077,7 @@ static void coda_encode_ctrls(struct coda_ctx *ctx) } v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE, - V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES, 0x0, + V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES, 0x0, V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE); v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB, 1, 0x3fffffff, 1, 1); @@ -2098,6 +2135,34 @@ static void coda_decode_ctrls(struct coda_ctx *ctx) &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_LEVEL, max, 0, max); if (ctx->h264_level_ctrl) ctx->h264_level_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + ctx->mpeg2_profile_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, + &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MPEG2_PROFILE, + V4L2_MPEG_VIDEO_MPEG2_PROFILE_HIGH, 0, + V4L2_MPEG_VIDEO_MPEG2_PROFILE_HIGH); + if (ctx->mpeg2_profile_ctrl) + ctx->mpeg2_profile_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + ctx->mpeg2_level_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, + &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL, + V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH, 0, + V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH); + if (ctx->mpeg2_level_ctrl) + ctx->mpeg2_level_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + ctx->mpeg4_profile_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, + &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE, + V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY, 0, + V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY); + if (ctx->mpeg4_profile_ctrl) + ctx->mpeg4_profile_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + ctx->mpeg4_level_ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrls, + &coda_ctrl_ops, V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL, + V4L2_MPEG_VIDEO_MPEG4_LEVEL_5, 0, + V4L2_MPEG_VIDEO_MPEG4_LEVEL_5); + if (ctx->mpeg4_level_ctrl) + ctx->mpeg4_level_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; } static int coda_ctrls_setup(struct coda_ctx *ctx) @@ -2486,9 +2551,12 @@ err_clk_per: static int coda_register_device(struct coda_dev *dev, int i) { struct video_device *vfd = &dev->vfd[i]; + enum coda_inst_type type; + int ret; if (i >= dev->devtype->num_vdevs) return -EINVAL; + type = dev->devtype->vdevs[i]->type; strscpy(vfd->name, dev->devtype->vdevs[i]->name, sizeof(vfd->name)); vfd->fops = &coda_fops; @@ -2504,7 +2572,12 @@ static int coda_register_device(struct coda_dev *dev, int i) v4l2_disable_ioctl(vfd, VIDIOC_G_CROP); v4l2_disable_ioctl(vfd, VIDIOC_S_CROP); - return video_register_device(vfd, VFL_TYPE_GRABBER, 0); + ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); + if (!ret) + v4l2_info(&dev->v4l2_dev, "%s registered as %s\n", + type == CODA_INST_ENCODER ? "encoder" : "decoder", + video_device_node_name(vfd)); + return ret; } static void coda_copy_firmware(struct coda_dev *dev, const u8 * const buf, @@ -2618,9 +2691,6 @@ static void coda_fw_callback(const struct firmware *fw, void *context) } } - v4l2_info(&dev->v4l2_dev, "codec registered as /dev/video[%d-%d]\n", - dev->vfd[0].num, dev->vfd[i - 1].num); - pm_runtime_put_sync(&pdev->dev); return; @@ -2790,8 +2860,8 @@ static int coda_probe(struct platform_device *pdev) return irq; } - ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, coda_irq_handler, - IRQF_ONESHOT, dev_name(&pdev->dev), dev); + ret = devm_request_irq(&pdev->dev, irq, coda_irq_handler, 0, + dev_name(&pdev->dev), dev); if (ret < 0) { dev_err(&pdev->dev, "failed to request irq: %d\n", ret); return ret; diff --git a/drivers/media/platform/coda/coda-h264.c b/drivers/media/platform/coda/coda-h264.c index a2fa29da1d31..8bd0aa8af114 100644 --- a/drivers/media/platform/coda/coda-h264.c +++ b/drivers/media/platform/coda/coda-h264.c @@ -10,7 +10,8 @@ #include <linux/kernel.h> #include <linux/string.h> #include <linux/videodev2.h> -#include <coda.h> + +#include "coda.h" static const u8 coda_filler_size[8] = { 0, 7, 14, 13, 12, 11, 10, 9 }; diff --git a/drivers/media/platform/coda/coda-mpeg2.c b/drivers/media/platform/coda/coda-mpeg2.c new file mode 100644 index 000000000000..73e50dabce19 --- /dev/null +++ b/drivers/media/platform/coda/coda-mpeg2.c @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Coda multi-standard codec IP - MPEG-2 helper functions + * + * Copyright (C) 2019 Pengutronix, Philipp Zabel + */ + +#include <linux/kernel.h> +#include <linux/videodev2.h> +#include "coda.h" + +int coda_mpeg2_profile(int profile_idc) +{ + switch (profile_idc) { + case 5: + return V4L2_MPEG_VIDEO_MPEG2_PROFILE_SIMPLE; + case 4: + return V4L2_MPEG_VIDEO_MPEG2_PROFILE_MAIN; + case 3: + return V4L2_MPEG_VIDEO_MPEG2_PROFILE_SNR_SCALABLE; + case 2: + return V4L2_MPEG_VIDEO_MPEG2_PROFILE_SPATIALLY_SCALABLE; + case 1: + return V4L2_MPEG_VIDEO_MPEG2_PROFILE_HIGH; + default: + return -EINVAL; + } +} + +int coda_mpeg2_level(int level_idc) +{ + switch (level_idc) { + case 10: + return V4L2_MPEG_VIDEO_MPEG2_LEVEL_LOW; + case 8: + return V4L2_MPEG_VIDEO_MPEG2_LEVEL_MAIN; + case 6: + return V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH_1440; + case 4: + return V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH; + default: + return -EINVAL; + } +} diff --git a/drivers/media/platform/coda/coda-mpeg4.c b/drivers/media/platform/coda/coda-mpeg4.c new file mode 100644 index 000000000000..c3aca763c320 --- /dev/null +++ b/drivers/media/platform/coda/coda-mpeg4.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Coda multi-standard codec IP - MPEG-4 helper functions + * + * Copyright (C) 2019 Pengutronix, Philipp Zabel + */ + +#include <linux/kernel.h> +#include <linux/videodev2.h> + +#include "coda.h" + +int coda_mpeg4_profile(int profile_idc) +{ + switch (profile_idc) { + case 0: + return V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE; + case 15: + return V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE; + case 2: + return V4L2_MPEG_VIDEO_MPEG4_PROFILE_CORE; + case 1: + return V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE_SCALABLE; + case 11: + return V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY; + default: + return -EINVAL; + } +} + +int coda_mpeg4_level(int level_idc) +{ + switch (level_idc) { + case 0: + return V4L2_MPEG_VIDEO_MPEG4_LEVEL_0; + case 1: + return V4L2_MPEG_VIDEO_MPEG4_LEVEL_1; + case 2: + return V4L2_MPEG_VIDEO_MPEG4_LEVEL_2; + case 3: + return V4L2_MPEG_VIDEO_MPEG4_LEVEL_3; + case 4: + return V4L2_MPEG_VIDEO_MPEG4_LEVEL_4; + case 5: + return V4L2_MPEG_VIDEO_MPEG4_LEVEL_5; + default: + return -EINVAL; + } +} diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h index cfcfff7838cd..0c2cace53ce8 100644 --- a/drivers/media/platform/coda/coda.h +++ b/drivers/media/platform/coda/coda.h @@ -118,6 +118,8 @@ struct coda_params { s8 h264_chroma_qp_index_offset; u8 h264_profile_idc; u8 h264_level_idc; + u8 mpeg2_profile_idc; + u8 mpeg2_level_idc; u8 mpeg4_intra_qp; u8 mpeg4_inter_qp; u8 gop_size; @@ -213,6 +215,10 @@ struct coda_ctx { struct v4l2_ctrl_handler ctrls; struct v4l2_ctrl *h264_profile_ctrl; struct v4l2_ctrl *h264_level_ctrl; + struct v4l2_ctrl *mpeg2_profile_ctrl; + struct v4l2_ctrl *mpeg2_level_ctrl; + struct v4l2_ctrl *mpeg4_profile_ctrl; + struct v4l2_ctrl *mpeg4_level_ctrl; struct v4l2_fh fh; int gopcounter; int runcounter; @@ -324,6 +330,14 @@ int coda_sps_parse_profile(struct coda_ctx *ctx, struct vb2_buffer *vb); int coda_h264_sps_fixup(struct coda_ctx *ctx, int width, int height, char *buf, int *size, int max_size); +int coda_mpeg2_profile(int profile_idc); +int coda_mpeg2_level(int level_idc); +int coda_mpeg4_profile(int profile_idc); +int coda_mpeg4_level(int level_idc); + +void coda_update_profile_level_ctrls(struct coda_ctx *ctx, u8 profile_idc, + u8 level_idc); + bool coda_jpeg_check_buffer(struct coda_ctx *ctx, struct vb2_buffer *vb); int coda_jpeg_write_tables(struct coda_ctx *ctx); void coda_set_jpeg_compression_quality(struct coda_ctx *ctx, int quality); diff --git a/drivers/media/platform/coda/coda_regs.h b/drivers/media/platform/coda/coda_regs.h index abf8e195f6c0..4d503f472397 100644 --- a/drivers/media/platform/coda/coda_regs.h +++ b/drivers/media/platform/coda/coda_regs.h @@ -177,7 +177,7 @@ #define CODA_RET_DEC_SEQ_FRATE_DR 0x1e8 #define CODA_RET_DEC_SEQ_JPG_PARA 0x1e4 #define CODA_RET_DEC_SEQ_JPG_THUMB_IND 0x1e8 -#define CODA9_RET_DEC_SEQ_HEADER_REPORT 0x1ec +#define CODA7_RET_DEC_SEQ_HEADER_REPORT 0x1ec /* Decoder Picture Run */ #define CODA_CMD_DEC_PIC_ROT_MODE 0x180 diff --git a/drivers/media/platform/coda/trace.h b/drivers/media/platform/coda/trace.h index a672bfc4c6ba..6cf58237fff2 100644 --- a/drivers/media/platform/coda/trace.h +++ b/drivers/media/platform/coda/trace.h @@ -157,7 +157,7 @@ DEFINE_EVENT(coda_buf_meta_class, coda_dec_rot_done, #endif /* __CODA_TRACE_H__ */ #undef TRACE_INCLUDE_PATH -#define TRACE_INCLUDE_PATH . +#define TRACE_INCLUDE_PATH ../../drivers/media/platform/coda #undef TRACE_INCLUDE_FILE #define TRACE_INCLUDE_FILE trace diff --git a/drivers/media/platform/davinci/vpss.c b/drivers/media/platform/davinci/vpss.c index 3f079ac1b080..d38d2bbb6f0f 100644 --- a/drivers/media/platform/davinci/vpss.c +++ b/drivers/media/platform/davinci/vpss.c @@ -498,9 +498,9 @@ static struct platform_driver vpss_driver = { static void vpss_exit(void) { + platform_driver_unregister(&vpss_driver); iounmap(oper_cfg.vpss_regs_base2); release_mem_region(VPSS_CLK_CTRL, 4); - platform_driver_unregister(&vpss_driver); } static int __init vpss_init(void) @@ -509,6 +509,11 @@ static int __init vpss_init(void) return -EBUSY; oper_cfg.vpss_regs_base2 = ioremap(VPSS_CLK_CTRL, 4); + if (unlikely(!oper_cfg.vpss_regs_base2)) { + release_mem_region(VPSS_CLK_CTRL, 4); + return -ENOMEM; + } + writel(VPSS_CLK_CTRL_VENCCLKEN | VPSS_CLK_CTRL_DACCLKEN, oper_cfg.vpss_regs_base2); diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index ea46d7387221..854869f0024e 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -327,7 +327,7 @@ void gsc_check_src_scale_info(struct gsc_variant *var, } } -int gsc_enum_fmt_mplane(struct v4l2_fmtdesc *f) +int gsc_enum_fmt(struct v4l2_fmtdesc *f) { const struct gsc_fmt *fmt; diff --git a/drivers/media/platform/exynos-gsc/gsc-core.h b/drivers/media/platform/exynos-gsc/gsc-core.h index c81f0a17d286..8ea49ca004fd 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.h +++ b/drivers/media/platform/exynos-gsc/gsc-core.h @@ -387,7 +387,7 @@ void gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state); u32 get_plane_size(struct gsc_frame *fr, unsigned int plane); const struct gsc_fmt *get_format(int index); const struct gsc_fmt *find_fmt(u32 *pixelformat, u32 *mbus_code, u32 index); -int gsc_enum_fmt_mplane(struct v4l2_fmtdesc *f); +int gsc_enum_fmt(struct v4l2_fmtdesc *f); int gsc_try_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f); void gsc_set_frame_size(struct gsc_frame *frame, int width, int height); int gsc_g_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f); diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index 677d7cc80785..35a1d0d6dd66 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -294,15 +294,13 @@ static int gsc_m2m_querycap(struct file *file, void *fh, strscpy(cap->card, GSC_MODULE_NAME " gscaler", sizeof(cap->card)); snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", dev_name(&gsc->pdev->dev)); - cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } -static int gsc_m2m_enum_fmt_mplane(struct file *file, void *priv, - struct v4l2_fmtdesc *f) +static int gsc_m2m_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) { - return gsc_enum_fmt_mplane(f); + return gsc_enum_fmt(f); } static int gsc_m2m_g_fmt_mplane(struct file *file, void *fh, @@ -558,8 +556,8 @@ static int gsc_m2m_s_selection(struct file *file, void *fh, static const struct v4l2_ioctl_ops gsc_m2m_ioctl_ops = { .vidioc_querycap = gsc_m2m_querycap, - .vidioc_enum_fmt_vid_cap_mplane = gsc_m2m_enum_fmt_mplane, - .vidioc_enum_fmt_vid_out_mplane = gsc_m2m_enum_fmt_mplane, + .vidioc_enum_fmt_vid_cap = gsc_m2m_enum_fmt, + .vidioc_enum_fmt_vid_out = gsc_m2m_enum_fmt, .vidioc_g_fmt_vid_cap_mplane = gsc_m2m_g_fmt_mplane, .vidioc_g_fmt_vid_out_mplane = gsc_m2m_g_fmt_mplane, .vidioc_try_fmt_vid_cap_mplane = gsc_m2m_try_fmt_mplane, @@ -759,6 +757,8 @@ int gsc_register_m2m_device(struct gsc_dev *gsc) gsc->vdev.lock = &gsc->lock; gsc->vdev.vfl_dir = VFL_DIR_M2M; gsc->vdev.v4l2_dev = &gsc->v4l2_dev; + gsc->vdev.device_caps = V4L2_CAP_STREAMING | + V4L2_CAP_VIDEO_M2M_MPLANE; snprintf(gsc->vdev.name, sizeof(gsc->vdev.name), "%s.%d:m2m", GSC_MODULE_NAME, gsc->id); diff --git a/drivers/media/platform/exynos4-is/common.c b/drivers/media/platform/exynos4-is/common.c index 76f557548dfc..d47a77c8d4d6 100644 --- a/drivers/media/platform/exynos4-is/common.c +++ b/drivers/media/platform/exynos4-is/common.c @@ -37,15 +37,12 @@ struct v4l2_subdev *fimc_find_remote_sensor(struct media_entity *entity) } EXPORT_SYMBOL(fimc_find_remote_sensor); -void __fimc_vidioc_querycap(struct device *dev, struct v4l2_capability *cap, - unsigned int caps) +void __fimc_vidioc_querycap(struct device *dev, struct v4l2_capability *cap) { strscpy(cap->driver, dev->driver->name, sizeof(cap->driver)); strscpy(cap->card, dev->driver->name, sizeof(cap->card)); snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", dev_name(dev)); - cap->device_caps = caps; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; } EXPORT_SYMBOL(__fimc_vidioc_querycap); diff --git a/drivers/media/platform/exynos4-is/common.h b/drivers/media/platform/exynos4-is/common.h index 75b9c71d9419..58da94e7910c 100644 --- a/drivers/media/platform/exynos4-is/common.h +++ b/drivers/media/platform/exynos4-is/common.h @@ -12,5 +12,4 @@ #include <media/v4l2-subdev.h> struct v4l2_subdev *fimc_find_remote_sensor(struct media_entity *entity); -void __fimc_vidioc_querycap(struct device *dev, struct v4l2_capability *cap, - unsigned int caps); +void __fimc_vidioc_querycap(struct device *dev, struct v4l2_capability *cap); diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c index de4af0357a3c..84b91e248c5a 100644 --- a/drivers/media/platform/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/exynos4-is/fimc-capture.c @@ -728,13 +728,12 @@ static int fimc_cap_querycap(struct file *file, void *priv, { struct fimc_dev *fimc = video_drvdata(file); - __fimc_vidioc_querycap(&fimc->pdev->dev, cap, V4L2_CAP_STREAMING | - V4L2_CAP_VIDEO_CAPTURE_MPLANE); + __fimc_vidioc_querycap(&fimc->pdev->dev, cap); return 0; } -static int fimc_cap_enum_fmt_mplane(struct file *file, void *priv, - struct v4l2_fmtdesc *f) +static int fimc_cap_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) { struct fimc_fmt *fmt; @@ -1361,7 +1360,7 @@ static int fimc_cap_s_selection(struct file *file, void *fh, static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = { .vidioc_querycap = fimc_cap_querycap, - .vidioc_enum_fmt_vid_cap_mplane = fimc_cap_enum_fmt_mplane, + .vidioc_enum_fmt_vid_cap = fimc_cap_enum_fmt, .vidioc_try_fmt_vid_cap_mplane = fimc_cap_try_fmt_mplane, .vidioc_s_fmt_vid_cap_mplane = fimc_cap_s_fmt_mplane, .vidioc_g_fmt_vid_cap_mplane = fimc_cap_g_fmt_mplane, @@ -1765,6 +1764,7 @@ static int fimc_register_capture_device(struct fimc_dev *fimc, vfd->release = video_device_release_empty; vfd->queue = q; vfd->lock = &fimc->lock; + vfd->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE_MPLANE; video_set_drvdata(vfd, fimc); vid_cap = &fimc->vid_cap; diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.c b/drivers/media/platform/exynos4-is/fimc-isp-video.c index bb35a2017f21..2226a13ac89b 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp-video.c +++ b/drivers/media/platform/exynos4-is/fimc-isp-video.c @@ -349,12 +349,12 @@ static int isp_video_querycap(struct file *file, void *priv, { struct fimc_isp *isp = video_drvdata(file); - __fimc_vidioc_querycap(&isp->pdev->dev, cap, V4L2_CAP_STREAMING); + __fimc_vidioc_querycap(&isp->pdev->dev, cap); return 0; } -static int isp_video_enum_fmt_mplane(struct file *file, void *priv, - struct v4l2_fmtdesc *f) +static int isp_video_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) { const struct fimc_fmt *fmt; @@ -551,7 +551,7 @@ static int isp_video_reqbufs(struct file *file, void *priv, static const struct v4l2_ioctl_ops isp_video_ioctl_ops = { .vidioc_querycap = isp_video_querycap, - .vidioc_enum_fmt_vid_cap_mplane = isp_video_enum_fmt_mplane, + .vidioc_enum_fmt_vid_cap = isp_video_enum_fmt, .vidioc_try_fmt_vid_cap_mplane = isp_video_try_fmt_mplane, .vidioc_s_fmt_vid_cap_mplane = isp_video_s_fmt_mplane, .vidioc_g_fmt_vid_cap_mplane = isp_video_g_fmt_mplane, @@ -614,6 +614,7 @@ int fimc_isp_video_device_register(struct fimc_isp *isp, vdev->minor = -1; vdev->release = video_device_release_empty; vdev->lock = &isp->video_lock; + vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE_MPLANE; iv->pad.flags = MEDIA_PAD_FL_SINK; ret = media_entity_pads_init(&vdev->entity, 1, &iv->pad); diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c index 96f0a8a0dcae..e71342756d88 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/exynos4-is/fimc-lite.c @@ -658,14 +658,11 @@ static int fimc_lite_querycap(struct file *file, void *priv, strscpy(cap->card, FIMC_LITE_DRV_NAME, sizeof(cap->card)); snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", dev_name(&fimc->pdev->dev)); - - cap->device_caps = V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } -static int fimc_lite_enum_fmt_mplane(struct file *file, void *priv, - struct v4l2_fmtdesc *f) +static int fimc_lite_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) { const struct fimc_fmt *fmt; @@ -954,7 +951,7 @@ static int fimc_lite_s_selection(struct file *file, void *fh, static const struct v4l2_ioctl_ops fimc_lite_ioctl_ops = { .vidioc_querycap = fimc_lite_querycap, - .vidioc_enum_fmt_vid_cap_mplane = fimc_lite_enum_fmt_mplane, + .vidioc_enum_fmt_vid_cap = fimc_lite_enum_fmt, .vidioc_try_fmt_vid_cap_mplane = fimc_lite_try_fmt_mplane, .vidioc_s_fmt_vid_cap_mplane = fimc_lite_s_fmt_mplane, .vidioc_g_fmt_vid_cap_mplane = fimc_lite_g_fmt_mplane, @@ -1282,6 +1279,7 @@ static int fimc_lite_subdev_registered(struct v4l2_subdev *sd) vfd->minor = -1; vfd->release = video_device_release_empty; vfd->queue = q; + vfd->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING; fimc->reqbufs_count = 0; INIT_LIST_HEAD(&fimc->pending_buf_q); diff --git a/drivers/media/platform/exynos4-is/fimc-m2m.c b/drivers/media/platform/exynos4-is/fimc-m2m.c index b950c152fa28..62e876fc3555 100644 --- a/drivers/media/platform/exynos4-is/fimc-m2m.c +++ b/drivers/media/platform/exynos4-is/fimc-m2m.c @@ -232,14 +232,13 @@ static int fimc_m2m_querycap(struct file *file, void *fh, struct v4l2_capability *cap) { struct fimc_dev *fimc = video_drvdata(file); - unsigned int caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE; - __fimc_vidioc_querycap(&fimc->pdev->dev, cap, caps); + __fimc_vidioc_querycap(&fimc->pdev->dev, cap); return 0; } -static int fimc_m2m_enum_fmt_mplane(struct file *file, void *priv, - struct v4l2_fmtdesc *f) +static int fimc_m2m_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) { struct fimc_fmt *fmt; @@ -529,8 +528,8 @@ static int fimc_m2m_s_selection(struct file *file, void *fh, static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = { .vidioc_querycap = fimc_m2m_querycap, - .vidioc_enum_fmt_vid_cap_mplane = fimc_m2m_enum_fmt_mplane, - .vidioc_enum_fmt_vid_out_mplane = fimc_m2m_enum_fmt_mplane, + .vidioc_enum_fmt_vid_cap = fimc_m2m_enum_fmt, + .vidioc_enum_fmt_vid_out = fimc_m2m_enum_fmt, .vidioc_g_fmt_vid_cap_mplane = fimc_m2m_g_fmt_mplane, .vidioc_g_fmt_vid_out_mplane = fimc_m2m_g_fmt_mplane, .vidioc_try_fmt_vid_cap_mplane = fimc_m2m_try_fmt_mplane, @@ -732,6 +731,7 @@ int fimc_register_m2m_device(struct fimc_dev *fimc, vfd->release = video_device_release_empty; vfd->lock = &fimc->lock; vfd->vfl_dir = VFL_DIR_M2M; + vfd->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE; set_bit(V4L2_FL_QUIRK_INVERTED_CROP, &vfd->flags); snprintf(vfd->name, sizeof(vfd->name), "fimc.%d.m2m", fimc->id); diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index 1b83a6ec745f..d53427a8db11 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -445,6 +445,7 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd, pd->fimc_bus_type = FIMC_BUS_TYPE_ISP_WRITEBACK; else pd->fimc_bus_type = pd->sensor_bus_type; + of_node_put(np); if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor))) { of_node_put(rem); @@ -470,7 +471,8 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd, static int fimc_md_register_sensor_entities(struct fimc_md *fmd) { struct device_node *parent = fmd->pdev->dev.of_node; - struct device_node *node, *ports; + struct device_node *ports = NULL; + struct device_node *node; int index = 0; int ret; @@ -519,12 +521,14 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd) } index++; } + of_node_put(ports); rpm_put: pm_runtime_put(fmd->pmf); return 0; cleanup: + of_node_put(ports); v4l2_async_notifier_cleanup(&fmd->subdev_notifier); pm_runtime_put(fmd->pmf); return ret; diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index f1b301810260..040fe9501415 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -200,7 +200,6 @@ struct mcam_vb_buffer { struct list_head queue; struct mcam_dma_desc *dma_desc; /* Descriptor virtual address */ dma_addr_t dma_desc_pa; /* Descriptor physical address */ - int dma_desc_nent; /* Number of mapped descriptors */ }; static inline struct mcam_vb_buffer *vb_to_mvb(struct vb2_v4l2_buffer *vb) @@ -608,9 +607,11 @@ static void mcam_dma_contig_done(struct mcam_camera *cam, int frame) static void mcam_sg_next_buffer(struct mcam_camera *cam) { struct mcam_vb_buffer *buf; + struct sg_table *sg_table; buf = list_first_entry(&cam->buffers, struct mcam_vb_buffer, queue); list_del_init(&buf->queue); + sg_table = vb2_dma_sg_plane_desc(&buf->vb_buf.vb2_buf, 0); /* * Very Bad Not Good Things happen if you don't clear * C1_DESC_ENA before making any descriptor changes. @@ -618,7 +619,7 @@ static void mcam_sg_next_buffer(struct mcam_camera *cam) mcam_reg_clear_bit(cam, REG_CTRL1, C1_DESC_ENA); mcam_reg_write(cam, REG_DMA_DESC_Y, buf->dma_desc_pa); mcam_reg_write(cam, REG_DESC_LEN_Y, - buf->dma_desc_nent*sizeof(struct mcam_dma_desc)); + sg_table->nents * sizeof(struct mcam_dma_desc)); mcam_reg_write(cam, REG_DESC_LEN_U, 0); mcam_reg_write(cam, REG_DESC_LEN_V, 0); mcam_reg_set_bit(cam, REG_CTRL1, C1_DESC_ENA); @@ -791,12 +792,6 @@ static void mcam_ctlr_image(struct mcam_camera *cam) * Make sure it knows we want to use hsync/vsync. */ mcam_reg_write_mask(cam, REG_CTRL0, C0_SIF_HVSYNC, C0_SIFM_MASK); - /* - * This field controls the generation of EOF(DVP only) - */ - if (cam->bus_type != V4L2_MBUS_CSI2_DPHY) - mcam_reg_set_bit(cam, REG_CTRL0, - C0_EOF_VSYNC | C0_VEDGE_CTRL); } diff --git a/drivers/media/platform/meson/ao-cec-g12a.c b/drivers/media/platform/meson/ao-cec-g12a.c index 3620a1e310f5..fb52e5dd044a 100644 --- a/drivers/media/platform/meson/ao-cec-g12a.c +++ b/drivers/media/platform/meson/ao-cec-g12a.c @@ -365,28 +365,22 @@ static int meson_ao_cec_g12a_read(void *context, unsigned int addr, { struct meson_ao_cec_g12a_device *ao_cec = context; u32 reg = FIELD_PREP(CECB_RW_ADDR, addr); - unsigned long flags; int ret = 0; - spin_lock_irqsave(&ao_cec->cec_reg_lock, flags); - ret = regmap_write(ao_cec->regmap, CECB_RW_REG, reg); if (ret) - goto read_out; + return ret; ret = regmap_read_poll_timeout(ao_cec->regmap, CECB_RW_REG, reg, !(reg & CECB_RW_BUS_BUSY), 5, 1000); if (ret) - goto read_out; + return ret; ret = regmap_read(ao_cec->regmap, CECB_RW_REG, ®); *data = FIELD_GET(CECB_RW_RD_DATA, reg); -read_out: - spin_unlock_irqrestore(&ao_cec->cec_reg_lock, flags); - return ret; } @@ -394,19 +388,11 @@ static int meson_ao_cec_g12a_write(void *context, unsigned int addr, unsigned int data) { struct meson_ao_cec_g12a_device *ao_cec = context; - unsigned long flags; u32 reg = FIELD_PREP(CECB_RW_ADDR, addr) | FIELD_PREP(CECB_RW_WR_DATA, data) | CECB_RW_WRITE_EN; - int ret = 0; - spin_lock_irqsave(&ao_cec->cec_reg_lock, flags); - - ret = regmap_write(ao_cec->regmap, CECB_RW_REG, reg); - - spin_unlock_irqrestore(&ao_cec->cec_reg_lock, flags); - - return ret; + return regmap_write(ao_cec->regmap, CECB_RW_REG, reg); } static const struct regmap_config meson_ao_cec_g12a_cec_regmap_conf = { @@ -415,7 +401,6 @@ static const struct regmap_config meson_ao_cec_g12a_cec_regmap_conf = { .reg_read = meson_ao_cec_g12a_read, .reg_write = meson_ao_cec_g12a_write, .max_register = 0xffff, - .fast_io = true, }; static inline void diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c index 656444e7ca2b..723a233e2b5f 100644 --- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c +++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c @@ -528,8 +528,8 @@ end: static const struct v4l2_ioctl_ops mtk_jpeg_ioctl_ops = { .vidioc_querycap = mtk_jpeg_querycap, - .vidioc_enum_fmt_vid_cap_mplane = mtk_jpeg_enum_fmt_vid_cap, - .vidioc_enum_fmt_vid_out_mplane = mtk_jpeg_enum_fmt_vid_out, + .vidioc_enum_fmt_vid_cap = mtk_jpeg_enum_fmt_vid_cap, + .vidioc_enum_fmt_vid_out = mtk_jpeg_enum_fmt_vid_out, .vidioc_try_fmt_vid_cap_mplane = mtk_jpeg_try_fmt_vid_cap_mplane, .vidioc_try_fmt_vid_out_mplane = mtk_jpeg_try_fmt_vid_out_mplane, .vidioc_g_fmt_vid_cap_mplane = mtk_jpeg_g_fmt_vid_mplane, diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c index b28e3dd4885c..7c9e2d69e21a 100644 --- a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c +++ b/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c @@ -612,7 +612,7 @@ static int mtk_mdp_m2m_querycap(struct file *file, void *fh, return 0; } -static int mtk_mdp_enum_fmt_mplane(struct v4l2_fmtdesc *f, u32 type) +static int mtk_mdp_enum_fmt(struct v4l2_fmtdesc *f, u32 type) { const struct mtk_mdp_fmt *fmt; @@ -625,16 +625,16 @@ static int mtk_mdp_enum_fmt_mplane(struct v4l2_fmtdesc *f, u32 type) return 0; } -static int mtk_mdp_m2m_enum_fmt_mplane_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) +static int mtk_mdp_m2m_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) { - return mtk_mdp_enum_fmt_mplane(f, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + return mtk_mdp_enum_fmt(f, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); } -static int mtk_mdp_m2m_enum_fmt_mplane_vid_out(struct file *file, void *priv, - struct v4l2_fmtdesc *f) +static int mtk_mdp_m2m_enum_fmt_vid_out(struct file *file, void *priv, + struct v4l2_fmtdesc *f) { - return mtk_mdp_enum_fmt_mplane(f, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + return mtk_mdp_enum_fmt(f, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); } static int mtk_mdp_m2m_g_fmt_mplane(struct file *file, void *fh, @@ -927,8 +927,8 @@ static int mtk_mdp_m2m_s_selection(struct file *file, void *fh, static const struct v4l2_ioctl_ops mtk_mdp_m2m_ioctl_ops = { .vidioc_querycap = mtk_mdp_m2m_querycap, - .vidioc_enum_fmt_vid_cap_mplane = mtk_mdp_m2m_enum_fmt_mplane_vid_cap, - .vidioc_enum_fmt_vid_out_mplane = mtk_mdp_m2m_enum_fmt_mplane_vid_out, + .vidioc_enum_fmt_vid_cap = mtk_mdp_m2m_enum_fmt_vid_cap, + .vidioc_enum_fmt_vid_out = mtk_mdp_m2m_enum_fmt_vid_out, .vidioc_g_fmt_vid_cap_mplane = mtk_mdp_m2m_g_fmt_mplane, .vidioc_g_fmt_vid_out_mplane = mtk_mdp_m2m_g_fmt_mplane, .vidioc_try_fmt_vid_cap_mplane = mtk_mdp_m2m_try_fmt_mplane, diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c index 7ae588e62ed8..90d1a67db7e5 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: PC Chen <pc.chen@mediatek.com> @@ -24,7 +24,7 @@ #define DFT_CFG_WIDTH MTK_VDEC_MIN_W #define DFT_CFG_HEIGHT MTK_VDEC_MIN_H -static struct mtk_video_fmt mtk_video_formats[] = { +static const struct mtk_video_fmt mtk_video_formats[] = { { .fourcc = V4L2_PIX_FMT_H264, .type = MTK_FMT_DEC, @@ -68,9 +68,9 @@ static const struct mtk_codec_framesizes mtk_vdec_framesizes[] = { #define NUM_SUPPORTED_FRAMESIZE ARRAY_SIZE(mtk_vdec_framesizes) #define NUM_FORMATS ARRAY_SIZE(mtk_video_formats) -static struct mtk_video_fmt *mtk_vdec_find_format(struct v4l2_format *f) +static const struct mtk_video_fmt *mtk_vdec_find_format(struct v4l2_format *f) { - struct mtk_video_fmt *fmt; + const struct mtk_video_fmt *fmt; unsigned int k; for (k = 0; k < NUM_FORMATS; k++) { @@ -122,8 +122,9 @@ static struct vb2_buffer *get_display_buffer(struct mtk_vcodec_ctx *ctx) if (dstbuf->used) { vb2_set_plane_payload(&dstbuf->vb.vb2_buf, 0, ctx->picinfo.fb_sz[0]); - vb2_set_plane_payload(&dstbuf->vb.vb2_buf, 1, - ctx->picinfo.fb_sz[1]); + if (ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes == 2) + vb2_set_plane_payload(&dstbuf->vb.vb2_buf, 1, + ctx->picinfo.fb_sz[1]); mtk_v4l2_debug(2, "[%d]status=%x queue id=%d to done_list %d", @@ -271,7 +272,7 @@ static void mtk_vdec_flush_decoder(struct mtk_vcodec_ctx *ctx) static void mtk_vdec_update_fmt(struct mtk_vcodec_ctx *ctx, unsigned int pixelformat) { - struct mtk_video_fmt *fmt; + const struct mtk_video_fmt *fmt; struct mtk_q_data *dst_q_data; unsigned int k; @@ -394,7 +395,8 @@ static void mtk_vdec_worker(struct work_struct *work) vdec_if_decode(ctx, NULL, NULL, &res_chg); clean_display_buffer(ctx); vb2_set_plane_payload(&dst_buf_info->vb.vb2_buf, 0, 0); - vb2_set_plane_payload(&dst_buf_info->vb.vb2_buf, 1, 0); + if (ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes == 2) + vb2_set_plane_payload(&dst_buf_info->vb.vb2_buf, 1, 0); dst_buf->flags |= V4L2_BUF_FLAG_LAST; v4l2_m2m_buf_done(&dst_buf_info->vb, VB2_BUF_STATE_DONE); clean_free_buffer(ctx); @@ -644,7 +646,8 @@ static int vidioc_vdec_subscribe_evt(struct v4l2_fh *fh, } } -static int vidioc_try_fmt(struct v4l2_format *f, struct mtk_video_fmt *fmt) +static int vidioc_try_fmt(struct v4l2_format *f, + const struct mtk_video_fmt *fmt) { struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; int i; @@ -717,7 +720,7 @@ static int vidioc_try_fmt(struct v4l2_format *f, struct mtk_video_fmt *fmt) static int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *priv, struct v4l2_format *f) { - struct mtk_video_fmt *fmt; + const struct mtk_video_fmt *fmt; fmt = mtk_vdec_find_format(f); if (!fmt) { @@ -732,7 +735,7 @@ static int vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv, struct v4l2_format *f) { struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; - struct mtk_video_fmt *fmt; + const struct mtk_video_fmt *fmt; fmt = mtk_vdec_find_format(f); if (!fmt) { @@ -826,7 +829,7 @@ static int vidioc_vdec_s_fmt(struct file *file, void *priv, struct v4l2_pix_format_mplane *pix_mp; struct mtk_q_data *q_data; int ret = 0; - struct mtk_video_fmt *fmt; + const struct mtk_video_fmt *fmt; mtk_v4l2_debug(3, "[%d]", ctx->id); @@ -925,7 +928,7 @@ static int vidioc_enum_framesizes(struct file *file, void *priv, static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool output_queue) { - struct mtk_video_fmt *fmt; + const struct mtk_video_fmt *fmt; int i, j = 0; for (i = 0; i < NUM_FORMATS; i++) { @@ -949,14 +952,14 @@ static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool output_queue) return 0; } -static int vidioc_vdec_enum_fmt_vid_cap_mplane(struct file *file, void *pirv, - struct v4l2_fmtdesc *f) +static int vidioc_vdec_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) { return vidioc_enum_fmt(f, false); } -static int vidioc_vdec_enum_fmt_vid_out_mplane(struct file *file, void *priv, - struct v4l2_fmtdesc *f) +static int vidioc_vdec_enum_fmt_vid_out(struct file *file, void *priv, + struct v4l2_fmtdesc *f) { return vidioc_enum_fmt(f, true); } @@ -1324,7 +1327,8 @@ static void vb2ops_vdec_stop_streaming(struct vb2_queue *q) while ((dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx))) { vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0); - vb2_set_plane_payload(&dst_buf->vb2_buf, 1, 0); + if (ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes == 2) + vb2_set_plane_payload(&dst_buf->vb2_buf, 1, 0); v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); } @@ -1453,8 +1457,8 @@ const struct v4l2_ioctl_ops mtk_vdec_ioctl_ops = { .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, - .vidioc_enum_fmt_vid_cap_mplane = vidioc_vdec_enum_fmt_vid_cap_mplane, - .vidioc_enum_fmt_vid_out_mplane = vidioc_vdec_enum_fmt_vid_out_mplane, + .vidioc_enum_fmt_vid_cap = vidioc_vdec_enum_fmt_vid_cap, + .vidioc_enum_fmt_vid_out = vidioc_vdec_enum_fmt_vid_out, .vidioc_enum_framesizes = vidioc_enum_framesizes, .vidioc_querycap = vidioc_vdec_querycap, diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.h index 3861d4433be9..e0c5338bde3d 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.h +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2016 MediaTek Inc. * Author: PC Chen <pc.chen@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c index 372d37824377..00d090df11bb 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: PC Chen <pc.chen@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c index 273f78f129da..5a6ec8fb52da 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: Tiffany Lin <tiffany.lin@mediatek.com> @@ -34,8 +34,8 @@ int mtk_vcodec_init_dec_pm(struct mtk_vcodec_dev *mtkdev) } pdev = of_find_device_by_node(node); + of_node_put(node); if (WARN_ON(!pdev)) { - of_node_put(node); return -1; } pm->larbvdec = &pdev->dev; diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.h index 74555cc5a893..872d8bf8cfaf 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.h +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2016 MediaTek Inc. * Author: Tiffany Lin <tiffany.lin@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h index 1044176d8e6f..c1422739dab4 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2016 MediaTek Inc. * Author: PC Chen <pc.chen@mediatek.com> @@ -129,7 +129,7 @@ struct mtk_q_data { enum v4l2_field field; unsigned int bytesperline[MTK_VCODEC_MAX_PLANES]; unsigned int sizeimage[MTK_VCODEC_MAX_PLANES]; - struct mtk_video_fmt *fmt; + const struct mtk_video_fmt *fmt; }; /** diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c index 0cf5744b4c28..480cc8fe281a 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: PC Chen <pc.chen@mediatek.com> @@ -29,7 +29,7 @@ static void mtk_venc_worker(struct work_struct *work); -static struct mtk_video_fmt mtk_video_formats[] = { +static const struct mtk_video_fmt mtk_video_formats[] = { { .fourcc = V4L2_PIX_FMT_NV12M, .type = MTK_FMT_FRAME, @@ -158,7 +158,7 @@ static const struct v4l2_ctrl_ops mtk_vcodec_enc_ctrl_ops = { static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool output_queue) { - struct mtk_video_fmt *fmt; + const struct mtk_video_fmt *fmt; int i, j = 0; for (i = 0; i < NUM_FORMATS; ++i) { @@ -199,14 +199,14 @@ static int vidioc_enum_framesizes(struct file *file, void *fh, return -EINVAL; } -static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv, - struct v4l2_fmtdesc *f) +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) { return vidioc_enum_fmt(f, false); } -static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *prov, - struct v4l2_fmtdesc *f) +static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, + struct v4l2_fmtdesc *f) { return vidioc_enum_fmt(f, true); } @@ -266,9 +266,9 @@ static struct mtk_q_data *mtk_venc_get_q_data(struct mtk_vcodec_ctx *ctx, return &ctx->q_data[MTK_Q_DATA_DST]; } -static struct mtk_video_fmt *mtk_venc_find_format(struct v4l2_format *f) +static const struct mtk_video_fmt *mtk_venc_find_format(struct v4l2_format *f) { - struct mtk_video_fmt *fmt; + const struct mtk_video_fmt *fmt; unsigned int k; for (k = 0; k < NUM_FORMATS; k++) { @@ -283,7 +283,8 @@ static struct mtk_video_fmt *mtk_venc_find_format(struct v4l2_format *f) /* V4L2 specification suggests the driver corrects the format struct if any of * the dimensions is unsupported */ -static int vidioc_try_fmt(struct v4l2_format *f, struct mtk_video_fmt *fmt) +static int vidioc_try_fmt(struct v4l2_format *f, + const struct mtk_video_fmt *fmt) { struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; int i; @@ -419,7 +420,7 @@ static int vidioc_venc_s_fmt_cap(struct file *file, void *priv, struct vb2_queue *vq; struct mtk_q_data *q_data; int i, ret; - struct mtk_video_fmt *fmt; + const struct mtk_video_fmt *fmt; vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); if (!vq) { @@ -481,7 +482,7 @@ static int vidioc_venc_s_fmt_out(struct file *file, void *priv, struct vb2_queue *vq; struct mtk_q_data *q_data; int ret, i; - struct mtk_video_fmt *fmt; + const struct mtk_video_fmt *fmt; struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); @@ -580,7 +581,7 @@ static int vidioc_venc_g_fmt(struct file *file, void *priv, static int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *priv, struct v4l2_format *f) { - struct mtk_video_fmt *fmt; + const struct mtk_video_fmt *fmt; struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); fmt = mtk_venc_find_format(f); @@ -599,7 +600,7 @@ static int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *priv, static int vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv, struct v4l2_format *f) { - struct mtk_video_fmt *fmt; + const struct mtk_video_fmt *fmt; fmt = mtk_venc_find_format(f); if (!fmt) { @@ -717,8 +718,8 @@ const struct v4l2_ioctl_ops mtk_venc_ioctl_ops = { .vidioc_dqbuf = vidioc_venc_dqbuf, .vidioc_querycap = vidioc_venc_querycap, - .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane, - .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out, .vidioc_enum_framesizes = vidioc_enum_framesizes, .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap_mplane, diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h index 8248cb628882..a9c9f86b9c83 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2016 MediaTek Inc. * Author: PC Chen <pc.chen@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c index b15e9d2ef6a9..1d82aa2b6017 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: PC Chen <pc.chen@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c index 4740ae5e9a8e..3e2bfded79a6 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: Tiffany Lin <tiffany.lin@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.h index 63165fc1b84a..b7ecdfd74823 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.h +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2016 MediaTek Inc. * Author: Tiffany Lin <tiffany.lin@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c index f8aae7cc5f57..a3c7a380c930 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: Tiffany Lin <tiffany.lin@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.h index ba632528fa72..638cd1f3526a 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.h +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2016 MediaTek Inc. * Author: Tiffany Lin <tiffany.lin@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c index 13f7061bfb50..d48f542db1a9 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: PC Chen <pc.chen@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h index 677adb990e28..b999d7b84ed1 100644 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h +++ b/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2016 MediaTek Inc. * Author: PC Chen <pc.chen@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c b/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c index 455dbe4887c1..c035f744b1f1 100644 --- a/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c +++ b/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: PC Chen <pc.chen@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c index 91139cef6283..24f976f0d477 100644 --- a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c +++ b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: Jungchang Tsao <jungchang.tsao@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c index c1904ad5e69b..9e6b630d7f5b 100644 --- a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c +++ b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: Daniel Hsiao <daniel.hsiao@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/vdec_drv_base.h b/drivers/media/platform/mtk-vcodec/vdec_drv_base.h index b6cb922fc400..2019aec71ddb 100644 --- a/drivers/media/platform/mtk-vcodec/vdec_drv_base.h +++ b/drivers/media/platform/mtk-vcodec/vdec_drv_base.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2016 MediaTek Inc. * Author: PC Chen <pc.chen@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/vdec_drv_if.c b/drivers/media/platform/mtk-vcodec/vdec_drv_if.c index 5c98a76a77b7..bd42d9028c42 100644 --- a/drivers/media/platform/mtk-vcodec/vdec_drv_if.c +++ b/drivers/media/platform/mtk-vcodec/vdec_drv_if.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: PC Chen <pc.chen@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/vdec_drv_if.h b/drivers/media/platform/mtk-vcodec/vdec_drv_if.h index 409623574145..c5bd8b0dbe13 100644 --- a/drivers/media/platform/mtk-vcodec/vdec_drv_if.h +++ b/drivers/media/platform/mtk-vcodec/vdec_drv_if.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2016 MediaTek Inc. * Author: PC Chen <pc.chen@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/vdec_ipi_msg.h b/drivers/media/platform/mtk-vcodec/vdec_ipi_msg.h index b05dcdeb7734..47a1c1c0fd04 100644 --- a/drivers/media/platform/mtk-vcodec/vdec_ipi_msg.h +++ b/drivers/media/platform/mtk-vcodec/vdec_ipi_msg.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2016 MediaTek Inc. * Author: PC Chen <pc.chen@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.c b/drivers/media/platform/mtk-vcodec/vdec_vpu_if.c index 035ba917ed0e..3f38cc4509ef 100644 --- a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.c +++ b/drivers/media/platform/mtk-vcodec/vdec_vpu_if.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: PC Chen <pc.chen@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.h b/drivers/media/platform/mtk-vcodec/vdec_vpu_if.h index 6701778ea5d9..b76f717e4fd7 100644 --- a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.h +++ b/drivers/media/platform/mtk-vcodec/vdec_vpu_if.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2016 MediaTek Inc. * Author: PC Chen <pc.chen@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c b/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c index 3125eaf2a326..21f2eaea207b 100644 --- a/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c +++ b/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: Jungchang Tsao <jungchang.tsao@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c b/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c index ba19cdc4e4f1..81f87f6ec435 100644 --- a/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c +++ b/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: Daniel Hsiao <daniel.hsiao@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/venc_drv_base.h b/drivers/media/platform/mtk-vcodec/venc_drv_base.h index 81620683b94f..09968a68db42 100644 --- a/drivers/media/platform/mtk-vcodec/venc_drv_base.h +++ b/drivers/media/platform/mtk-vcodec/venc_drv_base.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2016 MediaTek Inc. * Author: Daniel Hsiao <daniel.hsiao@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/venc_drv_if.c b/drivers/media/platform/mtk-vcodec/venc_drv_if.c index 608c08b2ab8f..600c43c17e48 100644 --- a/drivers/media/platform/mtk-vcodec/venc_drv_if.c +++ b/drivers/media/platform/mtk-vcodec/venc_drv_if.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: Daniel Hsiao <daniel.hsiao@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/venc_drv_if.h b/drivers/media/platform/mtk-vcodec/venc_drv_if.h index bbba1cec7be4..cc5bb36c2735 100644 --- a/drivers/media/platform/mtk-vcodec/venc_drv_if.h +++ b/drivers/media/platform/mtk-vcodec/venc_drv_if.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2016 MediaTek Inc. * Author: Daniel Hsiao <daniel.hsiao@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h b/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h index be34780760f4..28ee04ca6241 100644 --- a/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h +++ b/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2016 MediaTek Inc. * Author: Jungchang Tsao <jungchang.tsao@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/venc_vpu_if.c b/drivers/media/platform/mtk-vcodec/venc_vpu_if.c index 7daf8694c62e..3e931b0ed096 100644 --- a/drivers/media/platform/mtk-vcodec/venc_vpu_if.c +++ b/drivers/media/platform/mtk-vcodec/venc_vpu_if.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 MediaTek Inc. * Author: PoChun Lin <pochun.lin@mediatek.com> diff --git a/drivers/media/platform/mtk-vcodec/venc_vpu_if.h b/drivers/media/platform/mtk-vcodec/venc_vpu_if.h index a6b6d0eafb50..ba301a138a5a 100644 --- a/drivers/media/platform/mtk-vcodec/venc_vpu_if.h +++ b/drivers/media/platform/mtk-vcodec/venc_vpu_if.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2016 MediaTek Inc. * Author: PoChun Lin <pochun.lin@mediatek.com> diff --git a/drivers/media/platform/mtk-vpu/mtk_vpu.c b/drivers/media/platform/mtk-vpu/mtk_vpu.c index da655d166d52..cc2ff40d060d 100644 --- a/drivers/media/platform/mtk-vpu/mtk_vpu.c +++ b/drivers/media/platform/mtk-vpu/mtk_vpu.c @@ -460,9 +460,9 @@ struct platform_device *vpu_get_plat_device(struct platform_device *pdev) } vpu_pdev = of_find_device_by_node(vpu_node); + of_node_put(vpu_node); if (WARN_ON(!vpu_pdev)) { dev_err(dev, "vpu pdev failed\n"); - of_node_put(vpu_node); return NULL; } diff --git a/drivers/media/platform/omap/Kconfig b/drivers/media/platform/omap/Kconfig index 08a606a5adff..1a99dff21ca0 100644 --- a/drivers/media/platform/omap/Kconfig +++ b/drivers/media/platform/omap/Kconfig @@ -14,6 +14,5 @@ config VIDEO_OMAP2_VOUT select VIDEOBUF_DMA_CONTIG select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3 select FRAME_VECTOR - default n help V4L2 Display driver support for OMAP2/3 based boards. diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index bd57174d81a7..008933beebe0 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -2006,6 +2006,8 @@ static int isp_remove(struct platform_device *pdev) media_entity_enum_cleanup(&isp->crashed); v4l2_async_notifier_cleanup(&isp->notifier); + kfree(isp); + return 0; } @@ -2196,7 +2198,7 @@ static int isp_probe(struct platform_device *pdev) int ret; int i, m; - isp = devm_kzalloc(&pdev->dev, sizeof(*isp), GFP_KERNEL); + isp = kzalloc(sizeof(*isp), GFP_KERNEL); if (!isp) { dev_err(&pdev->dev, "could not allocate memory\n"); return -ENOMEM; @@ -2205,17 +2207,19 @@ static int isp_probe(struct platform_device *pdev) ret = fwnode_property_read_u32(of_fwnode_handle(pdev->dev.of_node), "ti,phy-type", &isp->phy_type); if (ret) - return ret; + goto error_release_isp; isp->syscon = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "syscon"); - if (IS_ERR(isp->syscon)) - return PTR_ERR(isp->syscon); + if (IS_ERR(isp->syscon)) { + ret = PTR_ERR(isp->syscon); + goto error_release_isp; + } ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 1, &isp->syscon_offset); if (ret) - return ret; + goto error_release_isp; isp->autoidle = autoidle; @@ -2372,6 +2376,8 @@ error_isp: error: v4l2_async_notifier_cleanup(&isp->notifier); mutex_destroy(&isp->isp_mutex); +error_release_isp: + kfree(isp); return ret; } @@ -2383,7 +2389,7 @@ static const struct dev_pm_ops omap3isp_pm_ops = { .complete = isp_pm_complete, }; -static struct platform_device_id omap3isp_id_table[] = { +static const struct platform_device_id omap3isp_id_table[] = { { "omap3isp", 0 }, { }, }; diff --git a/drivers/media/platform/omap3isp/isph3a_aewb.c b/drivers/media/platform/omap3isp/isph3a_aewb.c index 3c82dea4d375..2cefc30e7b18 100644 --- a/drivers/media/platform/omap3isp/isph3a_aewb.c +++ b/drivers/media/platform/omap3isp/isph3a_aewb.c @@ -291,9 +291,10 @@ int omap3isp_h3a_aewb_init(struct isp_device *isp) { struct ispstat *aewb = &isp->isp_aewb; struct omap3isp_h3a_aewb_config *aewb_cfg; - struct omap3isp_h3a_aewb_config *aewb_recover_cfg; + struct omap3isp_h3a_aewb_config *aewb_recover_cfg = NULL; + int ret; - aewb_cfg = devm_kzalloc(isp->dev, sizeof(*aewb_cfg), GFP_KERNEL); + aewb_cfg = kzalloc(sizeof(*aewb_cfg), GFP_KERNEL); if (!aewb_cfg) return -ENOMEM; @@ -303,12 +304,12 @@ int omap3isp_h3a_aewb_init(struct isp_device *isp) aewb->isp = isp; /* Set recover state configuration */ - aewb_recover_cfg = devm_kzalloc(isp->dev, sizeof(*aewb_recover_cfg), - GFP_KERNEL); + aewb_recover_cfg = kzalloc(sizeof(*aewb_recover_cfg), GFP_KERNEL); if (!aewb_recover_cfg) { dev_err(aewb->isp->dev, "AEWB: cannot allocate memory for recover configuration.\n"); - return -ENOMEM; + ret = -ENOMEM; + goto err; } aewb_recover_cfg->saturation_limit = OMAP3ISP_AEWB_MAX_SATURATION_LIM; @@ -325,13 +326,22 @@ int omap3isp_h3a_aewb_init(struct isp_device *isp) if (h3a_aewb_validate_params(aewb, aewb_recover_cfg)) { dev_err(aewb->isp->dev, "AEWB: recover configuration is invalid.\n"); - return -EINVAL; + ret = -EINVAL; + goto err; } aewb_recover_cfg->buf_size = h3a_aewb_get_buf_size(aewb_recover_cfg); aewb->recover_priv = aewb_recover_cfg; - return omap3isp_stat_init(aewb, "AEWB", &h3a_aewb_subdev_ops); + ret = omap3isp_stat_init(aewb, "AEWB", &h3a_aewb_subdev_ops); + +err: + if (ret) { + kfree(aewb_cfg); + kfree(aewb_recover_cfg); + } + + return ret; } /* diff --git a/drivers/media/platform/omap3isp/isph3a_af.c b/drivers/media/platform/omap3isp/isph3a_af.c index 4da25c84f0c6..843ec1dc5c9d 100644 --- a/drivers/media/platform/omap3isp/isph3a_af.c +++ b/drivers/media/platform/omap3isp/isph3a_af.c @@ -354,9 +354,10 @@ int omap3isp_h3a_af_init(struct isp_device *isp) { struct ispstat *af = &isp->isp_af; struct omap3isp_h3a_af_config *af_cfg; - struct omap3isp_h3a_af_config *af_recover_cfg; + struct omap3isp_h3a_af_config *af_recover_cfg = NULL; + int ret; - af_cfg = devm_kzalloc(isp->dev, sizeof(*af_cfg), GFP_KERNEL); + af_cfg = kzalloc(sizeof(*af_cfg), GFP_KERNEL); if (af_cfg == NULL) return -ENOMEM; @@ -366,12 +367,12 @@ int omap3isp_h3a_af_init(struct isp_device *isp) af->isp = isp; /* Set recover state configuration */ - af_recover_cfg = devm_kzalloc(isp->dev, sizeof(*af_recover_cfg), - GFP_KERNEL); + af_recover_cfg = kzalloc(sizeof(*af_recover_cfg), GFP_KERNEL); if (!af_recover_cfg) { dev_err(af->isp->dev, "AF: cannot allocate memory for recover configuration.\n"); - return -ENOMEM; + ret = -ENOMEM; + goto err; } af_recover_cfg->paxel.h_start = OMAP3ISP_AF_PAXEL_HZSTART_MIN; @@ -383,13 +384,22 @@ int omap3isp_h3a_af_init(struct isp_device *isp) if (h3a_af_validate_params(af, af_recover_cfg)) { dev_err(af->isp->dev, "AF: recover configuration is invalid.\n"); - return -EINVAL; + ret = -EINVAL; + goto err; } af_recover_cfg->buf_size = h3a_af_get_buf_size(af_recover_cfg); af->recover_priv = af_recover_cfg; - return omap3isp_stat_init(af, "AF", &h3a_af_subdev_ops); + ret = omap3isp_stat_init(af, "AF", &h3a_af_subdev_ops); + +err: + if (ret) { + kfree(af_cfg); + kfree(af_recover_cfg); + } + + return ret; } void omap3isp_h3a_af_cleanup(struct isp_device *isp) diff --git a/drivers/media/platform/omap3isp/isphist.c b/drivers/media/platform/omap3isp/isphist.c index d4be3d0e06f9..3b9ed8086387 100644 --- a/drivers/media/platform/omap3isp/isphist.c +++ b/drivers/media/platform/omap3isp/isphist.c @@ -478,9 +478,9 @@ int omap3isp_hist_init(struct isp_device *isp) { struct ispstat *hist = &isp->isp_hist; struct omap3isp_hist_config *hist_cfg; - int ret = -1; + int ret; - hist_cfg = devm_kzalloc(isp->dev, sizeof(*hist_cfg), GFP_KERNEL); + hist_cfg = kzalloc(sizeof(*hist_cfg), GFP_KERNEL); if (hist_cfg == NULL) return -ENOMEM; @@ -502,7 +502,7 @@ int omap3isp_hist_init(struct isp_device *isp) if (IS_ERR(hist->dma_ch)) { ret = PTR_ERR(hist->dma_ch); if (ret == -EPROBE_DEFER) - return ret; + goto err; hist->dma_ch = NULL; dev_warn(isp->dev, @@ -518,9 +518,12 @@ int omap3isp_hist_init(struct isp_device *isp) hist->event_type = V4L2_EVENT_OMAP3ISP_HIST; ret = omap3isp_stat_init(hist, "histogram", &hist_subdev_ops); + +err: if (ret) { - if (hist->dma_ch) + if (!IS_ERR_OR_NULL(hist->dma_ch)) dma_release_channel(hist->dma_ch); + kfree(hist_cfg); } return ret; diff --git a/drivers/media/platform/omap3isp/ispstat.c b/drivers/media/platform/omap3isp/ispstat.c index 47353fee26c3..46a42c5dc1cc 100644 --- a/drivers/media/platform/omap3isp/ispstat.c +++ b/drivers/media/platform/omap3isp/ispstat.c @@ -1040,7 +1040,7 @@ static int isp_stat_init_entities(struct ispstat *stat, const char *name, v4l2_subdev_init(subdev, sd_ops); snprintf(subdev->name, V4L2_SUBDEV_NAME_SIZE, "OMAP3 ISP %s", name); - subdev->grp_id = 1 << 16; /* group ID for isp subdevs */ + subdev->grp_id = BIT(16); /* group ID for isp subdevs */ subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; v4l2_set_subdevdata(subdev, stat); @@ -1078,4 +1078,6 @@ void omap3isp_stat_cleanup(struct ispstat *stat) mutex_destroy(&stat->ioctl_lock); isp_stat_bufs_free(stat); kfree(stat->buf); + kfree(stat->priv); + kfree(stat->recover_priv); } diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c index 078d64114b24..175bbed9a235 100644 --- a/drivers/media/platform/omap3isp/ispvideo.c +++ b/drivers/media/platform/omap3isp/ispvideo.c @@ -1495,6 +1495,5 @@ int omap3isp_video_register(struct isp_video *video, struct v4l2_device *vdev) void omap3isp_video_unregister(struct isp_video *video) { - if (video_is_registered(&video->video)) - video_unregister_device(&video->video); + video_unregister_device(&video->video); } diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c index 6addc5ea8494..1c9bfaabc54c 100644 --- a/drivers/media/platform/pxa_camera.c +++ b/drivers/media/platform/pxa_camera.c @@ -1388,7 +1388,7 @@ static int pxa_buffer_init(struct pxa_camera_dev *pcdev, break; default: return -EINVAL; - }; + } buf->nb_planes = nb_channels; ret = sg_split(sgt->sgl, sgt->nents, 0, nb_channels, diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index 58aebe7114cd..1d50dfbbb762 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -703,7 +703,7 @@ static int video_s_input(struct file *file, void *fh, unsigned int input) static const struct v4l2_ioctl_ops msm_vid_ioctl_ops = { .vidioc_querycap = video_querycap, - .vidioc_enum_fmt_vid_cap_mplane = video_enum_fmt, + .vidioc_enum_fmt_vid_cap = video_enum_fmt, .vidioc_g_fmt_vid_cap_mplane = video_g_fmt, .vidioc_s_fmt_vid_cap_mplane = video_s_fmt, .vidioc_try_fmt_vid_cap_mplane = video_try_fmt, diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index db8e40b55d72..0acc7576cc58 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -446,7 +446,7 @@ static const struct venus_resources msm8996_res = { .reg_tbl_size = ARRAY_SIZE(msm8996_reg_preset), .clks = {"core", "iface", "bus", "mbus" }, .clks_num = 4, - .max_load = 3110400, /* 4096x2160@90 */ + .max_load = 2563200, .hfi_version = HFI_VERSION_3XX, .vmem_id = VIDC_RESOURCE_NONE, .vmem_size = 0, @@ -469,7 +469,7 @@ static const struct venus_resources sdm845_res = { .freq_tbl_size = ARRAY_SIZE(sdm845_freq_table), .clks = {"core", "iface", "bus" }, .clks_num = 3, - .max_load = 2563200, + .max_load = 3110400, /* 4096x2160@90 */ .hfi_version = HFI_VERSION_4XX, .vmem_id = VIDC_RESOURCE_NONE, .vmem_size = 0, diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c index 1eba23409ff3..d3d1748a7ef6 100644 --- a/drivers/media/platform/qcom/venus/firmware.c +++ b/drivers/media/platform/qcom/venus/firmware.c @@ -78,11 +78,11 @@ static int venus_load_fw(struct venus_core *core, const char *fwname, ret = of_address_to_resource(node, 0, &r); if (ret) - return ret; + goto err_put_node; ret = request_firmware(&mdt, fwname, dev); if (ret < 0) - return ret; + goto err_put_node; fw_size = qcom_mdt_get_size(mdt); if (fw_size < 0) { @@ -116,6 +116,8 @@ static int venus_load_fw(struct venus_core *core, const char *fwname, memunmap(mem_va); err_release_fw: release_firmware(mdt); +err_put_node: + of_node_put(node); return ret; } diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 7d0017613113..71b06dfc6dc4 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -458,6 +458,13 @@ static bool is_dynamic_bufmode(struct venus_inst *inst) struct venus_core *core = inst->core; struct venus_caps *caps; + /* + * v4 doesn't send BUFFER_ALLOC_MODE_SUPPORTED property and supports + * dynamic buffer mode by default for HFI_BUFFER_OUTPUT/OUTPUT2. + */ + if (IS_V4(core)) + return true; + caps = venus_caps_by_codec(core, inst->hfi_codec, inst->session_type); if (!caps) return false; diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.c b/drivers/media/platform/qcom/venus/hfi_cmds.c index 8efd55a2ad70..4f645076abfb 100644 --- a/drivers/media/platform/qcom/venus/hfi_cmds.c +++ b/drivers/media/platform/qcom/venus/hfi_cmds.c @@ -1205,6 +1205,8 @@ pkt_session_set_property_4xx(struct hfi_session_set_property_pkt *pkt, break; } case HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE: + case HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER: + case HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE: /* not implemented on Venus 4xx */ return -ENOTSUPP; default: diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 6205ad8b3201..e1f998656c07 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -482,8 +482,8 @@ unlock: static const struct v4l2_ioctl_ops vdec_ioctl_ops = { .vidioc_querycap = vdec_querycap, - .vidioc_enum_fmt_vid_cap_mplane = vdec_enum_fmt, - .vidioc_enum_fmt_vid_out_mplane = vdec_enum_fmt, + .vidioc_enum_fmt_vid_cap = vdec_enum_fmt, + .vidioc_enum_fmt_vid_out = vdec_enum_fmt, .vidioc_s_fmt_vid_cap_mplane = vdec_s_fmt, .vidioc_s_fmt_vid_out_mplane = vdec_s_fmt, .vidioc_g_fmt_vid_cap_mplane = vdec_g_fmt, diff --git a/drivers/media/platform/qcom/venus/vdec_ctrls.c b/drivers/media/platform/qcom/venus/vdec_ctrls.c index 68e0f7d0b8fc..300350bfe8bd 100644 --- a/drivers/media/platform/qcom/venus/vdec_ctrls.c +++ b/drivers/media/platform/qcom/venus/vdec_ctrls.c @@ -66,7 +66,7 @@ static int vdec_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl) break; default: return -EINVAL; - }; + } return 0; } diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 7a4815d52c12..a5f3d2c46bea 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -607,8 +607,8 @@ static int venc_enum_frameintervals(struct file *file, void *fh, static const struct v4l2_ioctl_ops venc_ioctl_ops = { .vidioc_querycap = venc_querycap, - .vidioc_enum_fmt_vid_cap_mplane = venc_enum_fmt, - .vidioc_enum_fmt_vid_out_mplane = venc_enum_fmt, + .vidioc_enum_fmt_vid_cap = venc_enum_fmt, + .vidioc_enum_fmt_vid_out = venc_enum_fmt, .vidioc_s_fmt_vid_cap_mplane = venc_s_fmt, .vidioc_s_fmt_vid_out_mplane = venc_s_fmt, .vidioc_g_fmt_vid_cap_mplane = venc_g_fmt, diff --git a/drivers/media/platform/qcom/venus/venc_ctrls.c b/drivers/media/platform/qcom/venus/venc_ctrls.c index 8832285d8c15..877c0b3299e9 100644 --- a/drivers/media/platform/qcom/venus/venc_ctrls.c +++ b/drivers/media/platform/qcom/venus/venc_ctrls.c @@ -108,6 +108,9 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_H264_PROFILE: ctr->profile.h264 = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE: + ctr->profile.hevc = ctrl->val; + break; case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: ctr->profile.vpx = ctrl->val; break; @@ -117,6 +120,9 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_H264_LEVEL: ctr->level.h264 = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL: + ctr->level.hevc = ctrl->val; + break; case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: ctr->h264_i_qp = ctrl->val; break; @@ -208,7 +214,7 @@ int venc_ctrl_init(struct venus_inst *inst) { int ret; - ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 28); + ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 30); if (ret) return ret; @@ -237,6 +243,19 @@ int venc_ctrl_init(struct venus_inst *inst) 0, V4L2_MPEG_VIDEO_MPEG4_LEVEL_0); v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_PROFILE, + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10, + ~((1 << V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN) | + (1 << V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE) | + (1 << V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10)), + V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN); + + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_HEVC_LEVEL, + V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2, + 0, V4L2_MPEG_VIDEO_HEVC_LEVEL_1); + + v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, V4L2_CID_MPEG_VIDEO_H264_PROFILE, V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH, ~((1 << V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) | @@ -265,7 +284,7 @@ int venc_ctrl_init(struct venus_inst *inst) v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE, - V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES, + V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES, 0, V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE); v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, diff --git a/drivers/media/platform/rcar-vin/rcar-csi2.c b/drivers/media/platform/rcar-vin/rcar-csi2.c index 8f097e514900..c14af1b929df 100644 --- a/drivers/media/platform/rcar-vin/rcar-csi2.c +++ b/drivers/media/platform/rcar-vin/rcar-csi2.c @@ -1019,10 +1019,8 @@ static int rcsi2_probe_resources(struct rcar_csi2 *priv, return ret; priv->rstc = devm_reset_control_get(&pdev->dev, NULL); - if (IS_ERR(priv->rstc)) - return PTR_ERR(priv->rstc); - return 0; + return PTR_ERR_OR_ZERO(priv->rstc); } static const struct rcar_csi2_info rcar_csi2_info_r8a7795 = { diff --git a/drivers/media/platform/rcar_fdp1.c b/drivers/media/platform/rcar_fdp1.c index 6a90bc4c476e..6f9a4c69f620 100644 --- a/drivers/media/platform/rcar_fdp1.c +++ b/drivers/media/platform/rcar_fdp1.c @@ -1730,8 +1730,8 @@ static const char * const fdp1_ctrl_deint_menu[] = { static const struct v4l2_ioctl_ops fdp1_ioctl_ops = { .vidioc_querycap = fdp1_vidioc_querycap, - .vidioc_enum_fmt_vid_cap_mplane = fdp1_enum_fmt_vid_cap, - .vidioc_enum_fmt_vid_out_mplane = fdp1_enum_fmt_vid_out, + .vidioc_enum_fmt_vid_cap = fdp1_enum_fmt_vid_cap, + .vidioc_enum_fmt_vid_out = fdp1_enum_fmt_vid_out, .vidioc_g_fmt_vid_cap_mplane = fdp1_g_fmt, .vidioc_g_fmt_vid_out_mplane = fdp1_g_fmt, .vidioc_try_fmt_vid_cap_mplane = fdp1_try_fmt, diff --git a/drivers/media/platform/rcar_jpu.c b/drivers/media/platform/rcar_jpu.c index 1dfd2eb65920..1c3f507acfc9 100644 --- a/drivers/media/platform/rcar_jpu.c +++ b/drivers/media/platform/rcar_jpu.c @@ -671,8 +671,6 @@ static int jpu_querycap(struct file *file, void *priv, strscpy(cap->driver, DRV_NAME, sizeof(cap->driver)); snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", dev_name(ctx->jpu->dev)); - cap->device_caps |= V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE; - cap->capabilities = V4L2_CAP_DEVICE_CAPS | cap->device_caps; memset(cap->reserved, 0, sizeof(cap->reserved)); return 0; @@ -948,8 +946,8 @@ static int jpu_streamon(struct file *file, void *priv, enum v4l2_buf_type type) static const struct v4l2_ioctl_ops jpu_ioctl_ops = { .vidioc_querycap = jpu_querycap, - .vidioc_enum_fmt_vid_cap_mplane = jpu_enum_fmt_cap, - .vidioc_enum_fmt_vid_out_mplane = jpu_enum_fmt_out, + .vidioc_enum_fmt_vid_cap = jpu_enum_fmt_cap, + .vidioc_enum_fmt_vid_out = jpu_enum_fmt_out, .vidioc_g_fmt_vid_cap_mplane = jpu_g_fmt, .vidioc_g_fmt_vid_out_mplane = jpu_g_fmt, .vidioc_try_fmt_vid_cap_mplane = jpu_try_fmt, @@ -1662,6 +1660,8 @@ static int jpu_probe(struct platform_device *pdev) jpu->vfd_encoder.lock = &jpu->mutex; jpu->vfd_encoder.v4l2_dev = &jpu->v4l2_dev; jpu->vfd_encoder.vfl_dir = VFL_DIR_M2M; + jpu->vfd_encoder.device_caps = V4L2_CAP_STREAMING | + V4L2_CAP_VIDEO_M2M_MPLANE; ret = video_register_device(&jpu->vfd_encoder, VFL_TYPE_GRABBER, -1); if (ret) { @@ -1679,6 +1679,8 @@ static int jpu_probe(struct platform_device *pdev) jpu->vfd_decoder.lock = &jpu->mutex; jpu->vfd_decoder.v4l2_dev = &jpu->v4l2_dev; jpu->vfd_decoder.vfl_dir = VFL_DIR_M2M; + jpu->vfd_decoder.device_caps = V4L2_CAP_STREAMING | + V4L2_CAP_VIDEO_M2M_MPLANE; ret = video_register_device(&jpu->vfd_decoder, VFL_TYPE_GRABBER, -1); if (ret) { diff --git a/drivers/media/platform/renesas-ceu.c b/drivers/media/platform/renesas-ceu.c index 150196f7cf96..57d0c0f9fa4b 100644 --- a/drivers/media/platform/renesas-ceu.c +++ b/drivers/media/platform/renesas-ceu.c @@ -1339,7 +1339,7 @@ static int ceu_enum_frameintervals(struct file *file, void *fh, static const struct v4l2_ioctl_ops ceu_ioctl_ops = { .vidioc_querycap = ceu_querycap, - .vidioc_enum_fmt_vid_cap_mplane = ceu_enum_fmt_vid_cap, + .vidioc_enum_fmt_vid_cap = ceu_enum_fmt_vid_cap, .vidioc_try_fmt_vid_cap_mplane = ceu_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap_mplane = ceu_s_fmt_vid_cap, .vidioc_g_fmt_vid_cap_mplane = ceu_g_fmt_vid_cap, diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 4e936b95018a..c5dc1880a4c6 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -1344,6 +1344,7 @@ static int s5p_mfc_probe(struct platform_device *pdev) vfd->lock = &dev->mfc_mutex; vfd->v4l2_dev = &dev->v4l2_dev; vfd->vfl_dir = VFL_DIR_M2M; + vfd->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; set_bit(V4L2_FL_QUIRK_INVERTED_CROP, &vfd->flags); snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_DEC_NAME); dev->vfd_dec = vfd; @@ -1362,6 +1363,7 @@ static int s5p_mfc_probe(struct platform_device *pdev) vfd->lock = &dev->mfc_mutex; vfd->v4l2_dev = &dev->v4l2_dev; vfd->vfl_dir = VFL_DIR_M2M; + vfd->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_ENC_NAME); dev->vfd_enc = vfd; video_set_drvdata(vfd, dev); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c index d12fc4f397b6..4017c8b471f4 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c @@ -271,13 +271,6 @@ static int vidioc_querycap(struct file *file, void *priv, strscpy(cap->card, dev->vfd_dec->name, sizeof(cap->card)); snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", dev_name(&dev->plat_dev->dev)); - /* - * This is only a mem-to-mem video device. The capture and output - * device capability flags are left only for backward compatibility - * and are scheduled for removal. - */ - cap->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -309,14 +302,14 @@ static int vidioc_enum_fmt(struct file *file, struct v4l2_fmtdesc *f, return 0; } -static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv, - struct v4l2_fmtdesc *f) +static int vidioc_enum_fmt_vid_cap(struct file *file, void *pirv, + struct v4l2_fmtdesc *f) { return vidioc_enum_fmt(file, f, false); } -static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *priv, - struct v4l2_fmtdesc *f) +static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, + struct v4l2_fmtdesc *f) { return vidioc_enum_fmt(file, f, true); } @@ -883,8 +876,8 @@ static int vidioc_subscribe_event(struct v4l2_fh *fh, /* v4l2_ioctl_ops */ static const struct v4l2_ioctl_ops s5p_mfc_dec_ioctl_ops = { .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane, - .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out, .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt, .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt, .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt, diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index 74090a68f807..97e76480e942 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -130,7 +130,7 @@ static struct mfc_control controls[] = { .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE, .type = V4L2_CTRL_TYPE_MENU, .minimum = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE, - .maximum = V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES, + .maximum = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES, .default_value = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE, .menu_skip_mask = 0, }, @@ -1313,13 +1313,6 @@ static int vidioc_querycap(struct file *file, void *priv, strscpy(cap->card, dev->vfd_enc->name, sizeof(cap->card)); snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", dev_name(&dev->plat_dev->dev)); - /* - * This is only a mem-to-mem video device. The capture and output - * device capability flags are left only for backward compatibility - * and are scheduled for removal. - */ - cap->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -1350,14 +1343,14 @@ static int vidioc_enum_fmt(struct file *file, struct v4l2_fmtdesc *f, return -EINVAL; } -static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv, - struct v4l2_fmtdesc *f) +static int vidioc_enum_fmt_vid_cap(struct file *file, void *pirv, + struct v4l2_fmtdesc *f) { return vidioc_enum_fmt(file, f, false); } -static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *prov, - struct v4l2_fmtdesc *f) +static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, + struct v4l2_fmtdesc *f) { return vidioc_enum_fmt(file, f, true); } @@ -2339,8 +2332,8 @@ static int vidioc_subscribe_event(struct v4l2_fh *fh, static const struct v4l2_ioctl_ops s5p_mfc_enc_ioctl_ops = { .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane, - .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out, .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt, .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt, .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt, diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c index 6144e95f6425..e83ede3efca7 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c @@ -695,9 +695,9 @@ static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx) /* multi-slice control */ /* multi-slice MB number or bit size */ mfc_write(dev, p->slice_mode, S5P_FIMV_ENC_MSLICE_CTRL); - if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) { + if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB) { mfc_write(dev, p->slice_mb, S5P_FIMV_ENC_MSLICE_MB); - } else if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES) { + } else if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES) { mfc_write(dev, p->slice_bit, S5P_FIMV_ENC_MSLICE_BIT); } else { mfc_write(dev, 0, S5P_FIMV_ENC_MSLICE_MB); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c index 281699ab7fe1..d75511190e47 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c @@ -736,10 +736,10 @@ static int s5p_mfc_set_slice_mode(struct s5p_mfc_ctx *ctx) /* multi-slice control */ /* multi-slice MB number or bit size */ writel(ctx->slice_mode, mfc_regs->e_mslice_mode); - if (ctx->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) { + if (ctx->slice_mode == V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB) { writel(ctx->slice_size.mb, mfc_regs->e_mslice_size_mb); } else if (ctx->slice_mode == - V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES) { + V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES) { writel(ctx->slice_size.bits, mfc_regs->e_mslice_size_bits); } else { writel(0x0, mfc_regs->e_mslice_size_mb); @@ -779,11 +779,11 @@ static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx) /* multi-slice MB number or bit size */ ctx->slice_mode = p->slice_mode; reg = 0; - if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) { + if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB) { reg |= (0x1 << 3); writel(reg, mfc_regs->e_enc_options); ctx->slice_size.mb = p->slice_mb; - } else if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES) { + } else if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES) { reg |= (0x1 << 3); writel(reg, mfc_regs->e_enc_options); ctx->slice_size.bits = p->slice_bit; diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.c index 075d4695ee4d..a79250a7f812 100644 --- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.c +++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.c @@ -143,7 +143,7 @@ int c8sectpfe_frontend_attach(struct dvb_frontend **fe, "%s: stv0367ter_attach failed for NIM card %s\n" , __func__, dvb_card_str(tsin->dvb_card)); return -ENODEV; - }; + } /* * init the demod so that i2c gate_ctrl @@ -203,7 +203,7 @@ int c8sectpfe_frontend_attach(struct dvb_frontend **fe, "%s: stv6110x_attach failed for NIM card %s\n" , __func__, dvb_card_str(tsin->dvb_card)); return -ENODEV; - }; + } stv090x_config.tuner_init = fe2->tuner_init; stv090x_config.tuner_set_mode = fe2->tuner_set_mode; diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index b9dad0accd1b..d855e9c09c08 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -1702,7 +1702,7 @@ static int dcmi_probe(struct platform_device *pdev) if (irq <= 0) { if (irq != -EPROBE_DEFER) dev_err(&pdev->dev, "Could not get irq\n"); - return irq; + return irq ? irq : -ENXIO; } dcmi->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c index 4c79eb64a7a7..6e0e894154f4 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c @@ -924,6 +924,7 @@ static int sun6i_csi_remove(struct platform_device *pdev) static const struct of_device_id sun6i_csi_of_match[] = { { .compatible = "allwinner,sun6i-a31-csi", }, + { .compatible = "allwinner,sun8i-a83t-csi", }, { .compatible = "allwinner,sun8i-h3-csi", }, { .compatible = "allwinner,sun8i-v3s-csi", }, { .compatible = "allwinner,sun50i-a64-csi", }, diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index 1e40eafec284..3f90f9413da1 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -1495,8 +1495,6 @@ static int vpe_querycap(struct file *file, void *priv, strscpy(cap->card, VPE_MODULE_NAME, sizeof(cap->card)); snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", VPE_MODULE_NAME); - cap->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -1973,12 +1971,12 @@ static const struct v4l2_ctrl_ops vpe_ctrl_ops = { static const struct v4l2_ioctl_ops vpe_ioctl_ops = { .vidioc_querycap = vpe_querycap, - .vidioc_enum_fmt_vid_cap_mplane = vpe_enum_fmt, + .vidioc_enum_fmt_vid_cap = vpe_enum_fmt, .vidioc_g_fmt_vid_cap_mplane = vpe_g_fmt, .vidioc_try_fmt_vid_cap_mplane = vpe_try_fmt, .vidioc_s_fmt_vid_cap_mplane = vpe_s_fmt, - .vidioc_enum_fmt_vid_out_mplane = vpe_enum_fmt, + .vidioc_enum_fmt_vid_out = vpe_enum_fmt, .vidioc_g_fmt_vid_out_mplane = vpe_g_fmt, .vidioc_try_fmt_vid_out_mplane = vpe_try_fmt, .vidioc_s_fmt_vid_out_mplane = vpe_s_fmt, @@ -2411,6 +2409,7 @@ static const struct video_device vpe_videodev = { .minor = -1, .release = video_device_release_empty, .vfl_dir = VFL_DIR_M2M, + .device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING, }; static const struct v4l2_m2m_ops m2m_ops = { diff --git a/drivers/media/platform/vicodec/Kconfig b/drivers/media/platform/vicodec/Kconfig index 36bb0e934252..89456665cb16 100644 --- a/drivers/media/platform/vicodec/Kconfig +++ b/drivers/media/platform/vicodec/Kconfig @@ -4,7 +4,6 @@ config VIDEO_VICODEC depends on VIDEO_DEV && VIDEO_V4L2 select VIDEOBUF2_VMALLOC select V4L2_MEM2MEM_DEV - default n help Driver for a Virtual Codec diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c index bd01a9206aa6..7e7c1e80f29f 100644 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -84,6 +84,7 @@ struct vicodec_q_data { unsigned int visible_width; unsigned int visible_height; unsigned int sizeimage; + unsigned int vb2_sizeimage; unsigned int sequence; const struct v4l2_fwht_pixfmt_info *info; }; @@ -116,12 +117,14 @@ struct vicodec_ctx { struct vicodec_dev *dev; bool is_enc; bool is_stateless; + bool is_draining; + bool next_is_last; + bool has_stopped; spinlock_t *lock; struct v4l2_ctrl_handler hdl; struct vb2_v4l2_buffer *last_src_buf; - struct vb2_v4l2_buffer *last_dst_buf; /* Source and destination queue data */ struct vicodec_q_data q_data[2]; @@ -138,6 +141,10 @@ struct vicodec_ctx { bool source_changed; }; +static const struct v4l2_event vicodec_eos_event = { + .type = V4L2_EVENT_EOS +}; + static inline struct vicodec_ctx *file2ctx(struct file *file) { return container_of(file->private_data, struct vicodec_ctx, fh); @@ -329,6 +336,10 @@ static int device_process(struct vicodec_ctx *ctx, copy_cap_to_ref(p_dst, ctx->state.info, &ctx->state); vb2_set_plane_payload(&dst_vb->vb2_buf, 0, q_dst->sizeimage); + if (ntohl(ctx->state.header.flags) & FWHT_FL_I_FRAME) + dst_vb->flags |= V4L2_BUF_FLAG_KEYFRAME; + else + dst_vb->flags |= V4L2_BUF_FLAG_PFRAME; } return ret; } @@ -397,9 +408,6 @@ static enum vb2_buffer_state get_next_header(struct vicodec_ctx *ctx, /* device_run() - prepares and starts the device */ static void device_run(void *priv) { - static const struct v4l2_event eos_event = { - .type = V4L2_EVENT_EOS - }; struct vicodec_ctx *ctx = priv; struct vicodec_dev *dev = ctx->dev; struct vb2_v4l2_buffer *src_buf, *dst_buf; @@ -407,7 +415,6 @@ static void device_run(void *priv) u32 state; struct media_request *src_req; - src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); src_req = src_buf->vb2_buf.req_obj.req; @@ -421,14 +428,14 @@ static void device_run(void *priv) else dst_buf->sequence = q_dst->sequence++; dst_buf->flags &= ~V4L2_BUF_FLAG_LAST; - v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, !ctx->is_enc); - - ctx->last_dst_buf = dst_buf; + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false); spin_lock(ctx->lock); if (!ctx->comp_has_next_frame && src_buf == ctx->last_src_buf) { dst_buf->flags |= V4L2_BUF_FLAG_LAST; - v4l2_event_queue_fh(&ctx->fh, &eos_event); + v4l2_event_queue_fh(&ctx->fh, &vicodec_eos_event); + ctx->is_draining = false; + ctx->has_stopped = true; } if (ctx->is_enc || ctx->is_stateless) { src_buf->sequence = q_src->sequence++; @@ -442,14 +449,14 @@ static void device_run(void *priv) ctx->comp_has_next_frame = false; } v4l2_m2m_buf_done(dst_buf, state); - if (ctx->is_stateless && src_req) - v4l2_ctrl_request_complete(src_req, &ctx->hdl); ctx->comp_size = 0; ctx->header_size = 0; ctx->comp_magic_cnt = 0; ctx->comp_has_frame = false; spin_unlock(ctx->lock); + if (ctx->is_stateless && src_req) + v4l2_ctrl_request_complete(src_req, &ctx->hdl); if (ctx->is_enc) v4l2_m2m_job_finish(dev->stateful_enc.m2m_dev, ctx->fh.m2m_ctx); @@ -579,6 +586,8 @@ static int job_ready(void *priv) unsigned int max_to_copy; unsigned int comp_frame_size; + if (ctx->has_stopped) + return 0; if (ctx->source_changed) return 0; if (ctx->is_stateless || ctx->is_enc || ctx->comp_has_frame) @@ -598,6 +607,8 @@ restart: if (ctx->header_size < sizeof(struct fwht_cframe_hdr)) { state = get_next_header(ctx, &p, p_src + sz - p); if (ctx->header_size < sizeof(struct fwht_cframe_hdr)) { + if (ctx->is_draining && src_buf == ctx->last_src_buf) + return 1; job_remove_src_buf(ctx, state); goto restart; } @@ -625,6 +636,8 @@ restart: p += copy; ctx->comp_size += copy; if (ctx->comp_size < max_to_copy) { + if (ctx->is_draining && src_buf == ctx->last_src_buf) + return 1; job_remove_src_buf(ctx, state); goto restart; } @@ -666,7 +679,6 @@ restart: v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); update_capture_data_from_header(ctx); - ctx->first_source_change_sent = true; v4l2_event_queue_fh(&ctx->fh, &rs_event); set_last_buffer(dst_buf, src_buf, ctx); ctx->source_changed = true; @@ -713,7 +725,8 @@ static int enum_fmt(struct v4l2_fmtdesc *f, struct vicodec_ctx *ctx, const struct v4l2_fwht_pixfmt_info *info = get_q_data(ctx, f->type)->info; - if (!info || ctx->is_enc) + if (ctx->is_enc || + !vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q)) info = v4l2_fwht_get_pixfmt(f->index); else info = v4l2_fwht_find_nth_fmt(info->width_div, @@ -764,9 +777,6 @@ static int vidioc_g_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) q_data = get_q_data(ctx, f->type); info = q_data->info; - if (!info) - info = v4l2_fwht_get_pixfmt(0); - switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: case V4L2_BUF_TYPE_VIDEO_OUTPUT: @@ -1032,16 +1042,10 @@ static int vidioc_s_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f) default: return -EINVAL; } - if (q_data->visible_width > q_data->coded_width) - q_data->visible_width = q_data->coded_width; - if (q_data->visible_height > q_data->coded_height) - q_data->visible_height = q_data->coded_height; - dprintk(ctx->dev, - "Setting format for type %d, coded wxh: %dx%d, visible wxh: %dx%d, fourcc: %08x\n", + "Setting format for type %d, coded wxh: %dx%d, fourcc: 0x%08x\n", f->type, q_data->coded_width, q_data->coded_height, - q_data->visible_width, q_data->visible_height, q_data->info->id); return 0; @@ -1063,18 +1067,58 @@ static int vidioc_s_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f) { struct vicodec_ctx *ctx = file2ctx(file); - struct v4l2_pix_format_mplane *pix_mp; + struct vicodec_q_data *q_data; + struct vicodec_q_data *q_data_cap; struct v4l2_pix_format *pix; + struct v4l2_pix_format_mplane *pix_mp; + u32 coded_w = 0, coded_h = 0; + unsigned int size = 0; int ret; + q_data = get_q_data(ctx, f->type); + q_data_cap = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + ret = vidioc_try_fmt_vid_out(file, priv, f); if (ret) return ret; + if (ctx->is_enc) { + struct vb2_queue *vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + struct vb2_queue *vq_cap = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE); + const struct v4l2_fwht_pixfmt_info *info = ctx->is_stateless ? + &pixfmt_stateless_fwht : &pixfmt_fwht; + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + coded_w = f->fmt.pix.width; + coded_h = f->fmt.pix.height; + } else { + coded_w = f->fmt.pix_mp.width; + coded_h = f->fmt.pix_mp.height; + } + if (vb2_is_busy(vq) && (coded_w != q_data->coded_width || + coded_h != q_data->coded_height)) + return -EBUSY; + size = coded_w * coded_h * + info->sizeimage_mult / info->sizeimage_div; + if (!ctx->is_stateless) + size += sizeof(struct fwht_cframe_hdr); + + if (vb2_is_busy(vq_cap) && size > q_data_cap->sizeimage) + return -EBUSY; + } + ret = vidioc_s_fmt(file2ctx(file), f); if (!ret) { + if (ctx->is_enc) { + q_data->visible_width = coded_w; + q_data->visible_height = coded_h; + q_data_cap->coded_width = coded_w; + q_data_cap->coded_height = coded_h; + q_data_cap->sizeimage = size; + } + switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: case V4L2_BUF_TYPE_VIDEO_OUTPUT: pix = &f->fmt.pix; ctx->state.colorspace = pix->colorspace; @@ -1082,7 +1126,6 @@ static int vidioc_s_fmt_vid_out(struct file *file, void *priv, ctx->state.ycbcr_enc = pix->ycbcr_enc; ctx->state.quantization = pix->quantization; break; - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: pix_mp = &f->fmt.pix_mp; ctx->state.colorspace = pix_mp->colorspace; @@ -1173,31 +1216,39 @@ static int vidioc_s_selection(struct file *file, void *priv, return 0; } -static void vicodec_mark_last_buf(struct vicodec_ctx *ctx) +static int vicodec_mark_last_buf(struct vicodec_ctx *ctx) { - static const struct v4l2_event eos_event = { - .type = V4L2_EVENT_EOS - }; + struct vb2_v4l2_buffer *next_dst_buf; + int ret = 0; spin_lock(ctx->lock); - ctx->last_src_buf = v4l2_m2m_last_src_buf(ctx->fh.m2m_ctx); - if (!ctx->last_src_buf && ctx->last_dst_buf) { - ctx->last_dst_buf->flags |= V4L2_BUF_FLAG_LAST; - v4l2_event_queue_fh(&ctx->fh, &eos_event); + if (ctx->is_draining) { + ret = -EBUSY; + goto unlock; } - spin_unlock(ctx->lock); -} + if (ctx->has_stopped) + goto unlock; -static int vicodec_try_encoder_cmd(struct file *file, void *fh, - struct v4l2_encoder_cmd *ec) -{ - if (ec->cmd != V4L2_ENC_CMD_STOP) - return -EINVAL; + ctx->last_src_buf = v4l2_m2m_last_src_buf(ctx->fh.m2m_ctx); + ctx->is_draining = true; + if (ctx->last_src_buf) + goto unlock; + + next_dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + if (!next_dst_buf) { + ctx->next_is_last = true; + goto unlock; + } - if (ec->flags & V4L2_ENC_CMD_STOP_AT_GOP_END) - return -EINVAL; + next_dst_buf->flags |= V4L2_BUF_FLAG_LAST; + vb2_buffer_done(&next_dst_buf->vb2_buf, VB2_BUF_STATE_DONE); + ctx->is_draining = false; + ctx->has_stopped = true; + v4l2_event_queue_fh(&ctx->fh, &vicodec_eos_event); - return 0; +unlock: + spin_unlock(ctx->lock); + return ret; } static int vicodec_encoder_cmd(struct file *file, void *fh, @@ -1206,27 +1257,26 @@ static int vicodec_encoder_cmd(struct file *file, void *fh, struct vicodec_ctx *ctx = file2ctx(file); int ret; - ret = vicodec_try_encoder_cmd(file, fh, ec); + ret = v4l2_m2m_ioctl_try_encoder_cmd(file, fh, ec); if (ret < 0) return ret; - vicodec_mark_last_buf(ctx); - return 0; -} - -static int vicodec_try_decoder_cmd(struct file *file, void *fh, - struct v4l2_decoder_cmd *dc) -{ - if (dc->cmd != V4L2_DEC_CMD_STOP) - return -EINVAL; - - if (dc->flags & V4L2_DEC_CMD_STOP_TO_BLACK) - return -EINVAL; - - if (!(dc->flags & V4L2_DEC_CMD_STOP_IMMEDIATELY) && (dc->stop.pts != 0)) - return -EINVAL; + if (!vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q) || + !vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q)) + return 0; - return 0; + if (ec->cmd == V4L2_ENC_CMD_STOP) + return vicodec_mark_last_buf(ctx); + ret = 0; + spin_lock(ctx->lock); + if (ctx->is_draining) { + ret = -EBUSY; + } else if (ctx->has_stopped) { + ctx->has_stopped = false; + vb2_clear_last_buffer_dequeued(&ctx->fh.m2m_ctx->cap_q_ctx.q); + } + spin_unlock(ctx->lock); + return ret; } static int vicodec_decoder_cmd(struct file *file, void *fh, @@ -1235,12 +1285,26 @@ static int vicodec_decoder_cmd(struct file *file, void *fh, struct vicodec_ctx *ctx = file2ctx(file); int ret; - ret = vicodec_try_decoder_cmd(file, fh, dc); + ret = v4l2_m2m_ioctl_try_decoder_cmd(file, fh, dc); if (ret < 0) return ret; - vicodec_mark_last_buf(ctx); - return 0; + if (!vb2_is_streaming(&ctx->fh.m2m_ctx->cap_q_ctx.q) || + !vb2_is_streaming(&ctx->fh.m2m_ctx->out_q_ctx.q)) + return 0; + + if (dc->cmd == V4L2_DEC_CMD_STOP) + return vicodec_mark_last_buf(ctx); + ret = 0; + spin_lock(ctx->lock); + if (ctx->is_draining) { + ret = -EBUSY; + } else if (ctx->has_stopped) { + ctx->has_stopped = false; + vb2_clear_last_buffer_dequeued(&ctx->fh.m2m_ctx->cap_q_ctx.q); + } + spin_unlock(ctx->lock); + return ret; } static int vicodec_enum_framesizes(struct file *file, void *fh, @@ -1283,6 +1347,8 @@ static int vicodec_subscribe_event(struct v4l2_fh *fh, return -EINVAL; /* fall through */ case V4L2_EVENT_EOS: + if (ctx->is_stateless) + return -EINVAL; return v4l2_event_subscribe(fh, sub, 0, NULL); default: return v4l2_ctrl_subscribe_event(fh, sub); @@ -1297,7 +1363,6 @@ static const struct v4l2_ioctl_ops vicodec_ioctl_ops = { .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt_vid_cap, .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt_vid_cap, @@ -1307,7 +1372,6 @@ static const struct v4l2_ioctl_ops vicodec_ioctl_ops = { .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out, .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out, - .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out, .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt_vid_out, .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt_vid_out, .vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt_vid_out, @@ -1326,9 +1390,9 @@ static const struct v4l2_ioctl_ops vicodec_ioctl_ops = { .vidioc_g_selection = vidioc_g_selection, .vidioc_s_selection = vidioc_s_selection, - .vidioc_try_encoder_cmd = vicodec_try_encoder_cmd, + .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd, .vidioc_encoder_cmd = vicodec_encoder_cmd, - .vidioc_try_decoder_cmd = vicodec_try_decoder_cmd, + .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_try_decoder_cmd, .vidioc_decoder_cmd = vicodec_decoder_cmd, .vidioc_enum_framesizes = vicodec_enum_framesizes, @@ -1354,6 +1418,7 @@ static int vicodec_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, *nplanes = 1; sizes[0] = size; + q_data->vb2_sizeimage = size; return 0; } @@ -1384,11 +1449,11 @@ static int vicodec_buf_prepare(struct vb2_buffer *vb) } } - if (vb2_plane_size(vb, 0) < q_data->sizeimage) { + if (vb2_plane_size(vb, 0) < q_data->vb2_sizeimage) { dprintk(ctx->dev, "%s data will not fit into plane (%lu < %lu)\n", __func__, vb2_plane_size(vb, 0), - (long)q_data->sizeimage); + (long)q_data->vb2_sizeimage); return -EINVAL; } @@ -1412,6 +1477,25 @@ static void vicodec_buf_queue(struct vb2_buffer *vb) .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, }; + if (vb2_is_streaming(vq_cap)) { + if (!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type) && + ctx->next_is_last) { + unsigned int i; + + for (i = 0; i < vb->num_planes; i++) + vb->planes[i].bytesused = 0; + vbuf->flags = V4L2_BUF_FLAG_LAST; + vbuf->field = V4L2_FIELD_NONE; + vbuf->sequence = get_q_data(ctx, vb->vb2_queue->type)->sequence++; + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + ctx->is_draining = false; + ctx->has_stopped = true; + ctx->next_is_last = false; + v4l2_event_queue_fh(&ctx->fh, &vicodec_eos_event); + return; + } + } + /* buf_queue handles only the first source change event */ if (ctx->first_source_change_sent) { v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); @@ -1519,16 +1603,11 @@ static int vicodec_start_streaming(struct vb2_queue *q, unsigned int total_planes_size; u8 *new_comp_frame = NULL; - if (!info) - return -EINVAL; - chroma_div = info->width_div * info->height_div; q_data->sequence = 0; if (V4L2_TYPE_IS_OUTPUT(q->type)) ctx->last_src_buf = NULL; - else - ctx->last_dst_buf = NULL; state->gop_cnt = 0; @@ -1604,6 +1683,32 @@ static void vicodec_stop_streaming(struct vb2_queue *q) vicodec_return_bufs(q, VB2_BUF_STATE_ERROR); + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + if (ctx->is_draining) { + struct vb2_v4l2_buffer *next_dst_buf; + + spin_lock(ctx->lock); + ctx->last_src_buf = NULL; + next_dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + if (!next_dst_buf) { + ctx->next_is_last = true; + } else { + next_dst_buf->flags |= V4L2_BUF_FLAG_LAST; + vb2_buffer_done(&next_dst_buf->vb2_buf, VB2_BUF_STATE_DONE); + ctx->is_draining = false; + ctx->has_stopped = true; + v4l2_event_queue_fh(&ctx->fh, &vicodec_eos_event); + } + spin_unlock(ctx->lock); + } + } else { + ctx->is_draining = false; + ctx->has_stopped = false; + ctx->next_is_last = false; + } + if (!ctx->is_enc && V4L2_TYPE_IS_OUTPUT(q->type)) + ctx->first_source_change_sent = false; + if ((!V4L2_TYPE_IS_OUTPUT(q->type) && !ctx->is_enc) || (V4L2_TYPE_IS_OUTPUT(q->type) && ctx->is_enc)) { if (!ctx->is_stateless) @@ -1771,11 +1876,13 @@ static const struct v4l2_ctrl_config vicodec_ctrl_stateless_state = { */ static int vicodec_open(struct file *file) { + const struct v4l2_fwht_pixfmt_info *info = v4l2_fwht_get_pixfmt(0); struct video_device *vfd = video_devdata(file); struct vicodec_dev *dev = video_drvdata(file); struct vicodec_ctx *ctx = NULL; struct v4l2_ctrl_handler *hdl; - unsigned int size; + unsigned int raw_size; + unsigned int comp_size; int rc = 0; if (mutex_lock_interruptible(vfd->lock)) @@ -1795,13 +1902,16 @@ static int vicodec_open(struct file *file) file->private_data = &ctx->fh; ctx->dev = dev; hdl = &ctx->hdl; - v4l2_ctrl_handler_init(hdl, 4); + v4l2_ctrl_handler_init(hdl, 5); v4l2_ctrl_new_std(hdl, &vicodec_ctrl_ops, V4L2_CID_MPEG_VIDEO_GOP_SIZE, 1, 16, 1, 10); v4l2_ctrl_new_std(hdl, &vicodec_ctrl_ops, V4L2_CID_FWHT_I_FRAME_QP, 1, 31, 1, 20); v4l2_ctrl_new_std(hdl, &vicodec_ctrl_ops, V4L2_CID_FWHT_P_FRAME_QP, 1, 31, 1, 20); + if (ctx->is_enc) + v4l2_ctrl_new_std(hdl, &vicodec_ctrl_ops, + V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, 1, 1, 1, 1); if (ctx->is_stateless) v4l2_ctrl_new_custom(hdl, &vicodec_ctrl_stateless_state, NULL); if (hdl->error) { @@ -1814,7 +1924,7 @@ static int vicodec_open(struct file *file) v4l2_ctrl_handler_setup(hdl); if (ctx->is_enc) - ctx->q_data[V4L2_M2M_SRC].info = v4l2_fwht_get_pixfmt(0); + ctx->q_data[V4L2_M2M_SRC].info = info; else if (ctx->is_stateless) ctx->q_data[V4L2_M2M_SRC].info = &pixfmt_stateless_fwht; else @@ -1823,22 +1933,24 @@ static int vicodec_open(struct file *file) ctx->q_data[V4L2_M2M_SRC].coded_height = 720; ctx->q_data[V4L2_M2M_SRC].visible_width = 1280; ctx->q_data[V4L2_M2M_SRC].visible_height = 720; - size = 1280 * 720 * ctx->q_data[V4L2_M2M_SRC].info->sizeimage_mult / - ctx->q_data[V4L2_M2M_SRC].info->sizeimage_div; - if (ctx->is_enc || ctx->is_stateless) - ctx->q_data[V4L2_M2M_SRC].sizeimage = size; + raw_size = 1280 * 720 * info->sizeimage_mult / info->sizeimage_div; + comp_size = 1280 * 720 * pixfmt_fwht.sizeimage_mult / + pixfmt_fwht.sizeimage_div; + if (ctx->is_enc) + ctx->q_data[V4L2_M2M_SRC].sizeimage = raw_size; + else if (ctx->is_stateless) + ctx->q_data[V4L2_M2M_SRC].sizeimage = comp_size; else ctx->q_data[V4L2_M2M_SRC].sizeimage = - size + sizeof(struct fwht_cframe_hdr); + comp_size + sizeof(struct fwht_cframe_hdr); + ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC]; if (ctx->is_enc) { - ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC]; ctx->q_data[V4L2_M2M_DST].info = &pixfmt_fwht; - ctx->q_data[V4L2_M2M_DST].sizeimage = 1280 * 720 * - ctx->q_data[V4L2_M2M_DST].info->sizeimage_mult / - ctx->q_data[V4L2_M2M_DST].info->sizeimage_div + - sizeof(struct fwht_cframe_hdr); + ctx->q_data[V4L2_M2M_DST].sizeimage = + comp_size + sizeof(struct fwht_cframe_hdr); } else { - ctx->q_data[V4L2_M2M_DST].info = NULL; + ctx->q_data[V4L2_M2M_DST].info = info; + ctx->q_data[V4L2_M2M_DST].sizeimage = raw_size; } ctx->state.colorspace = V4L2_COLORSPACE_REC709; @@ -2013,18 +2125,31 @@ static int register_instance(struct vicodec_dev *dev, return 0; } +static void vicodec_v4l2_dev_release(struct v4l2_device *v4l2_dev) +{ + struct vicodec_dev *dev = container_of(v4l2_dev, struct vicodec_dev, v4l2_dev); + + v4l2_device_unregister(&dev->v4l2_dev); + v4l2_m2m_release(dev->stateful_enc.m2m_dev); + v4l2_m2m_release(dev->stateful_dec.m2m_dev); + v4l2_m2m_release(dev->stateless_dec.m2m_dev); + kfree(dev); +} + static int vicodec_probe(struct platform_device *pdev) { struct vicodec_dev *dev; int ret; - dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); if (ret) - return ret; + goto free_dev; + + dev->v4l2_dev.release = vicodec_v4l2_dev_release; #ifdef CONFIG_MEDIA_CONTROLLER dev->mdev.dev = &pdev->dev; @@ -2102,6 +2227,8 @@ unreg_sf_enc: v4l2_m2m_release(dev->stateful_enc.m2m_dev); unreg_dev: v4l2_device_unregister(&dev->v4l2_dev); +free_dev: + kfree(dev); return ret; } @@ -2120,12 +2247,10 @@ static int vicodec_remove(struct platform_device *pdev) media_device_cleanup(&dev->mdev); #endif - v4l2_m2m_release(dev->stateful_enc.m2m_dev); - v4l2_m2m_release(dev->stateful_dec.m2m_dev); video_unregister_device(&dev->stateful_enc.vfd); video_unregister_device(&dev->stateful_dec.vfd); video_unregister_device(&dev->stateless_dec.vfd); - v4l2_device_unregister(&dev->v4l2_dev); + v4l2_device_put(&dev->v4l2_dev); return 0; } diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index 243c82b5d537..acd3bd48c7e2 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -1359,7 +1359,7 @@ static int vim2m_probe(struct platform_device *pdev) MEDIA_ENT_F_PROC_VIDEO_SCALER); if (ret) { v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem media controller\n"); - goto error_m2m; + goto error_dev; } ret = media_device_register(&dev->mdev); @@ -1373,11 +1373,11 @@ static int vim2m_probe(struct platform_device *pdev) #ifdef CONFIG_MEDIA_CONTROLLER error_m2m_mc: v4l2_m2m_unregister_media_controller(dev->m2m_dev); -error_m2m: - v4l2_m2m_release(dev->m2m_dev); #endif error_dev: video_unregister_device(&dev->vfd); + /* vim2m_device_release called by video_unregister_device to release various objects */ + return ret; error_v4l2: v4l2_device_unregister(&dev->v4l2_dev); error_free: diff --git a/drivers/media/platform/vimc/Kconfig b/drivers/media/platform/vimc/Kconfig index beba6acce593..bd221d3e1a4a 100644 --- a/drivers/media/platform/vimc/Kconfig +++ b/drivers/media/platform/vimc/Kconfig @@ -4,7 +4,6 @@ config VIDEO_VIMC depends on VIDEO_DEV && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API select VIDEOBUF2_VMALLOC select VIDEO_V4L2_TPG - default n help Skeleton driver for Virtual Media Controller diff --git a/drivers/media/platform/vimc/vimc-core.c b/drivers/media/platform/vimc/vimc-core.c index 03707bdcbfa8..571c55aa0e16 100644 --- a/drivers/media/platform/vimc/vimc-core.c +++ b/drivers/media/platform/vimc/vimc-core.c @@ -234,10 +234,7 @@ static void vimc_comp_unbind(struct device *master) static int vimc_comp_compare(struct device *comp, void *data) { - const struct platform_device *pdev = to_platform_device(comp); - const char *name = data; - - return !strcmp(pdev->dev.platform_data, name); + return comp == data; } static struct component_match *vimc_add_subdevs(struct vimc_device *vimc) @@ -267,7 +264,7 @@ static struct component_match *vimc_add_subdevs(struct vimc_device *vimc) } component_match_add(&vimc->pdev.dev, &match, vimc_comp_compare, - (void *)vimc->pipe_cfg->ents[i].name); + &vimc->subdevs[i]->dev); } return match; diff --git a/drivers/media/platform/vimc/vimc-debayer.c b/drivers/media/platform/vimc/vimc-debayer.c index 3ae8c12f5fa3..310c0d0eefa2 100644 --- a/drivers/media/platform/vimc/vimc-debayer.c +++ b/drivers/media/platform/vimc/vimc-debayer.c @@ -260,7 +260,7 @@ static int vimc_deb_set_fmt(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { /* Do not change the format while stream is on */ - if (vdeb->src_frame) + if (vdeb->ved.stream) return -EBUSY; sink_fmt = &vdeb->sink_fmt; @@ -327,9 +327,6 @@ static int vimc_deb_s_stream(struct v4l2_subdev *sd, int enable) const struct v4l2_format_info *pix_info; unsigned int frame_size; - if (vdeb->src_frame) - return 0; - /* We only support translating bayer to RGB24 */ if (src_pixelformat != V4L2_PIX_FMT_RGB24) { dev_err(vdeb->dev, diff --git a/drivers/media/platform/vimc/vimc-scaler.c b/drivers/media/platform/vimc/vimc-scaler.c index 5f31c1e351a3..c7123a45c55b 100644 --- a/drivers/media/platform/vimc/vimc-scaler.c +++ b/drivers/media/platform/vimc/vimc-scaler.c @@ -148,7 +148,7 @@ static int vimc_sca_set_fmt(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { /* Do not change the format while stream is on */ - if (vsca->src_frame) + if (vsca->ved.stream) return -EBUSY; sink_fmt = &vsca->sink_fmt; @@ -203,9 +203,6 @@ static int vimc_sca_s_stream(struct v4l2_subdev *sd, int enable) const struct v4l2_format_info *pix_info; unsigned int frame_size; - if (vsca->src_frame) - return 0; - if (!vimc_sca_is_pixfmt_supported(pixelformat)) { dev_err(vsca->dev, "pixfmt (0x%08x) is not supported\n", pixelformat); @@ -327,7 +324,7 @@ static void *vimc_sca_process_frame(struct vimc_ent_device *ved, ved); /* If the stream in this node is not active, just return */ - if (!vsca->src_frame) + if (!ved->stream) return ERR_PTR(-EINVAL); vimc_sca_fill_src_frame(vsca, sink_frame); diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c index 46a25f705456..51359472eef2 100644 --- a/drivers/media/platform/vimc/vimc-sensor.c +++ b/drivers/media/platform/vimc/vimc-sensor.c @@ -131,7 +131,7 @@ static int vimc_sen_set_fmt(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { /* Do not change the format while stream is on */ - if (vsen->frame) + if (vsen->ved.stream) return -EBUSY; mf = &vsen->mbus_format; @@ -187,10 +187,6 @@ static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable) const struct v4l2_format_info *pix_info; unsigned int frame_size; - if (vsen->kthread_sen) - /* tpg is already executing */ - return 0; - /* Calculate the frame size */ pix_info = v4l2_format_info(pixelformat); frame_size = vsen->mbus_format.width * pix_info->bpp[0] * @@ -211,7 +207,6 @@ static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable) vfree(vsen->frame); vsen->frame = NULL; - return 0; } return 0; diff --git a/drivers/media/platform/vivid/Kconfig b/drivers/media/platform/vivid/Kconfig index b172bcc11758..e2ff06edfa93 100644 --- a/drivers/media/platform/vivid/Kconfig +++ b/drivers/media/platform/vivid/Kconfig @@ -11,7 +11,6 @@ config VIDEO_VIVID select VIDEOBUF2_VMALLOC select VIDEOBUF2_DMA_CONTIG select VIDEO_V4L2_TPG - default n help Enables a virtual video driver. This driver emulates a webcam, TV, S-Video and HDMI capture hardware, including VBI support for diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c index 7047df6f0e0e..beb2e566a43c 100644 --- a/drivers/media/platform/vivid/vivid-core.c +++ b/drivers/media/platform/vivid/vivid-core.c @@ -500,20 +500,18 @@ static const struct v4l2_file_operations vivid_radio_fops = { static const struct v4l2_ioctl_ops vivid_ioctl_ops = { .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid, + .vidioc_enum_fmt_vid_cap = vivid_enum_fmt_vid, .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_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_mplane, .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt_vid_cap_mplane, .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap_mplane, .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt_vid_cap_mplane, - .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid, + .vidioc_enum_fmt_vid_out = vivid_enum_fmt_vid, .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out, .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out, .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out, - .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_mplane, .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt_vid_out_mplane, .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt_vid_out_mplane, .vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt_vid_out_mplane, diff --git a/drivers/media/platform/vivid/vivid-osd.c b/drivers/media/platform/vivid/vivid-osd.c index 1a89593b0c86..f2e789bdf4a6 100644 --- a/drivers/media/platform/vivid/vivid-osd.c +++ b/drivers/media/platform/vivid/vivid-osd.c @@ -155,7 +155,7 @@ static int _vivid_fb_check_var(struct fb_var_screeninfo *var, struct vivid_dev * var->nonstd = 0; var->vmode &= ~FB_VMODE_MASK; - var->vmode = FB_VMODE_NONINTERLACED; + var->vmode |= FB_VMODE_NONINTERLACED; /* Dummy values */ var->hsync_len = 24; diff --git a/drivers/media/platform/vivid/vivid-vid-common.c b/drivers/media/platform/vivid/vivid-vid-common.c index 74b83bcc6119..9307ce1cdd16 100644 --- a/drivers/media/platform/vivid/vivid-vid-common.c +++ b/drivers/media/platform/vivid/vivid-vid-common.c @@ -797,26 +797,6 @@ int vivid_enum_fmt_vid(struct file *file, void *priv, return 0; } -int vidioc_enum_fmt_vid_mplane(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct vivid_dev *dev = video_drvdata(file); - - if (!dev->multiplanar) - return -ENOTTY; - return vivid_enum_fmt_vid(file, priv, f); -} - -int vidioc_enum_fmt_vid(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct vivid_dev *dev = video_drvdata(file); - - if (dev->multiplanar) - return -ENOTTY; - return vivid_enum_fmt_vid(file, priv, f); -} - int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id) { struct vivid_dev *dev = video_drvdata(file); diff --git a/drivers/media/platform/vivid/vivid-vid-common.h b/drivers/media/platform/vivid/vivid-vid-common.h index 29b6c0b40a1b..d908d9725283 100644 --- a/drivers/media/platform/vivid/vivid-vid-common.h +++ b/drivers/media/platform/vivid/vivid-vid-common.h @@ -28,8 +28,6 @@ void vivid_send_source_change(struct vivid_dev *dev, unsigned type); int vivid_vid_adjust_sel(unsigned flags, struct v4l2_rect *r); int vivid_enum_fmt_vid(struct file *file, void *priv, struct v4l2_fmtdesc *f); -int vidioc_enum_fmt_vid_mplane(struct file *file, void *priv, struct v4l2_fmtdesc *f); -int vidioc_enum_fmt_vid(struct file *file, void *priv, struct v4l2_fmtdesc *f); int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id); int vidioc_g_dv_timings(struct file *file, void *_fh, struct v4l2_dv_timings *timings); int vidioc_enum_dv_timings(struct file *file, void *_fh, struct v4l2_enum_dv_timings *timings); diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 4b41687b2bde..eb79d99787bd 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -233,7 +233,6 @@ source "drivers/media/radio/wl128x/Kconfig" menuconfig V4L_RADIO_ISA_DRIVERS bool "ISA radio devices" depends on ISA || COMPILE_TEST - default n help Say Y here to enable support for these ISA drivers. diff --git a/drivers/media/radio/dsbr100.c b/drivers/media/radio/dsbr100.c index 9f7e68498321..9a45cda05779 100644 --- a/drivers/media/radio/dsbr100.c +++ b/drivers/media/radio/dsbr100.c @@ -168,8 +168,6 @@ static int vidioc_querycap(struct file *file, void *priv, strscpy(v->driver, "dsbr100", sizeof(v->driver)); strscpy(v->card, "D-Link R-100 USB FM Radio", sizeof(v->card)); usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info)); - v->device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER; - v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -378,6 +376,7 @@ static int usb_dsbr100_probe(struct usb_interface *intf, radio->videodev.release = video_device_release_empty; radio->videodev.lock = &radio->v4l2_lock; radio->videodev.ctrl_handler = &radio->hdl; + radio->videodev.device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER; radio->usbdev = interface_to_usbdev(intf); radio->curfreq = FREQ_MIN * FREQ_MUL; diff --git a/drivers/media/radio/radio-cadet.c b/drivers/media/radio/radio-cadet.c index 12160894839c..a5db9b4dc3de 100644 --- a/drivers/media/radio/radio-cadet.c +++ b/drivers/media/radio/radio-cadet.c @@ -357,9 +357,6 @@ static int vidioc_querycap(struct file *file, void *priv, strscpy(v->driver, "ADS Cadet", sizeof(v->driver)); strscpy(v->card, "ADS Cadet", sizeof(v->card)); strscpy(v->bus_info, "ISA:radio-cadet", sizeof(v->bus_info)); - v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO | - V4L2_CAP_READWRITE | V4L2_CAP_RDS_CAPTURE; - v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -646,6 +643,8 @@ static int __init cadet_init(void) dev->vdev.ioctl_ops = &cadet_ioctl_ops; dev->vdev.release = video_device_release_empty; dev->vdev.lock = &dev->lock; + dev->vdev.device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO | + V4L2_CAP_READWRITE | V4L2_CAP_RDS_CAPTURE; video_set_drvdata(&dev->vdev, dev); res = video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr); diff --git a/drivers/media/radio/radio-isa.c b/drivers/media/radio/radio-isa.c index 9f9c08393756..ad2ac16ff12d 100644 --- a/drivers/media/radio/radio-isa.c +++ b/drivers/media/radio/radio-isa.c @@ -37,9 +37,6 @@ static int radio_isa_querycap(struct file *file, void *priv, strscpy(v->driver, isa->drv->driver.driver.name, sizeof(v->driver)); strscpy(v->card, isa->drv->card, sizeof(v->card)); snprintf(v->bus_info, sizeof(v->bus_info), "ISA:%s", isa->v4l2_dev.name); - - v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO; - v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -240,6 +237,7 @@ static int radio_isa_common_probe(struct radio_isa_card *isa, isa->vdev.fops = &radio_isa_fops; isa->vdev.ioctl_ops = &radio_isa_ioctl_ops; isa->vdev.release = video_device_release_empty; + isa->vdev.device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO; video_set_drvdata(&isa->vdev, isa); isa->freq = FREQ_LOW; isa->stereo = drv->has_stereo; diff --git a/drivers/media/radio/radio-keene.c b/drivers/media/radio/radio-keene.c index 4d41857946de..a35648316aa8 100644 --- a/drivers/media/radio/radio-keene.c +++ b/drivers/media/radio/radio-keene.c @@ -168,8 +168,6 @@ static int vidioc_querycap(struct file *file, void *priv, strscpy(v->driver, "radio-keene", sizeof(v->driver)); strscpy(v->card, "Keene FM Transmitter", sizeof(v->card)); usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info)); - v->device_caps = V4L2_CAP_RADIO | V4L2_CAP_MODULATOR; - v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -361,6 +359,7 @@ static int usb_keene_probe(struct usb_interface *intf, radio->vdev.lock = &radio->lock; radio->vdev.release = video_device_release_empty; radio->vdev.vfl_dir = VFL_DIR_TX; + radio->vdev.device_caps = V4L2_CAP_RADIO | V4L2_CAP_MODULATOR; radio->usbdev = interface_to_usbdev(intf); radio->intf = intf; diff --git a/drivers/media/radio/radio-ma901.c b/drivers/media/radio/radio-ma901.c index cbcf0ed69223..657c3dda6648 100644 --- a/drivers/media/radio/radio-ma901.c +++ b/drivers/media/radio/radio-ma901.c @@ -191,8 +191,6 @@ static int vidioc_querycap(struct file *file, void *priv, strscpy(v->driver, "radio-ma901", sizeof(v->driver)); strscpy(v->card, "Masterkit MA901 USB FM Radio", sizeof(v->card)); usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info)); - v->device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER; - v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -398,6 +396,7 @@ static int usb_ma901radio_probe(struct usb_interface *intf, radio->vdev.ioctl_ops = &usb_ma901radio_ioctl_ops; radio->vdev.release = video_device_release_empty; radio->vdev.lock = &radio->lock; + radio->vdev.device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER; radio->usbdev = interface_to_usbdev(intf); radio->intf = intf; diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c index 95d12cbff5c9..99788834c646 100644 --- a/drivers/media/radio/radio-miropcm20.c +++ b/drivers/media/radio/radio-miropcm20.c @@ -204,8 +204,6 @@ static int vidioc_querycap(struct file *file, void *priv, strscpy(v->driver, "Miro PCM20", sizeof(v->driver)); strscpy(v->card, "Miro PCM20", sizeof(v->card)); snprintf(v->bus_info, sizeof(v->bus_info), "ISA:%s", dev->v4l2_dev.name); - v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE; - v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -481,6 +479,8 @@ static int __init pcm20_init(void) dev->vdev.ioctl_ops = &pcm20_ioctl_ops; dev->vdev.release = video_device_release_empty; dev->vdev.lock = &dev->lock; + dev->vdev.device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO | + V4L2_CAP_RDS_CAPTURE; video_set_drvdata(&dev->vdev, dev); snd_aci_cmd(dev->aci, ACI_SET_TUNERMONO, dev->audmode == V4L2_TUNER_MODE_MONO, -1); diff --git a/drivers/media/radio/radio-mr800.c b/drivers/media/radio/radio-mr800.c index f53f9064e1e9..cb0437b4c331 100644 --- a/drivers/media/radio/radio-mr800.c +++ b/drivers/media/radio/radio-mr800.c @@ -260,9 +260,6 @@ static int vidioc_querycap(struct file *file, void *priv, strscpy(v->driver, "radio-mr800", sizeof(v->driver)); strscpy(v->card, "AverMedia MR 800 USB FM Radio", sizeof(v->card)); usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info)); - v->device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER | - V4L2_CAP_HW_FREQ_SEEK; - v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -545,6 +542,8 @@ static int usb_amradio_probe(struct usb_interface *intf, radio->vdev.ioctl_ops = &usb_amradio_ioctl_ops; radio->vdev.release = video_device_release_empty; radio->vdev.lock = &radio->lock; + radio->vdev.device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER | + V4L2_CAP_HW_FREQ_SEEK; radio->usbdev = interface_to_usbdev(intf); radio->intf = intf; diff --git a/drivers/media/radio/radio-raremono.c b/drivers/media/radio/radio-raremono.c index 5e782b3c2fa9..606f588e1edf 100644 --- a/drivers/media/radio/radio-raremono.c +++ b/drivers/media/radio/radio-raremono.c @@ -184,8 +184,6 @@ static int vidioc_querycap(struct file *file, void *priv, strscpy(v->driver, "radio-raremono", sizeof(v->driver)); strscpy(v->card, "Thanko's Raremono", sizeof(v->card)); usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info)); - v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO; - v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -345,6 +343,7 @@ static int usb_raremono_probe(struct usb_interface *intf, radio->vdev.ioctl_ops = &usb_raremono_ioctl_ops; radio->vdev.lock = &radio->lock; radio->vdev.release = video_device_release_empty; + radio->vdev.device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO; usb_set_intfdata(intf, &radio->v4l2_dev); diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c index 434c03338d7f..54a40d60e4fd 100644 --- a/drivers/media/radio/radio-sf16fmi.c +++ b/drivers/media/radio/radio-sf16fmi.c @@ -133,8 +133,6 @@ static int vidioc_querycap(struct file *file, void *priv, strscpy(v->driver, "radio-sf16fmi", sizeof(v->driver)); strscpy(v->card, "SF16-FMI/FMP/FMD radio", sizeof(v->card)); strscpy(v->bus_info, "ISA:radio-sf16fmi", sizeof(v->bus_info)); - v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO; - v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -345,6 +343,7 @@ static int __init fmi_init(void) fmi->vdev.fops = &fmi_fops; fmi->vdev.ioctl_ops = &fmi_ioctl_ops; fmi->vdev.release = video_device_release_empty; + fmi->vdev.device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO; video_set_drvdata(&fmi->vdev, fmi); mutex_init(&fmi->lock); diff --git a/drivers/media/radio/radio-si476x.c b/drivers/media/radio/radio-si476x.c index 645242314a09..b203296de977 100644 --- a/drivers/media/radio/radio-si476x.c +++ b/drivers/media/radio/radio-si476x.c @@ -336,19 +336,6 @@ static int si476x_radio_querycap(struct file *file, void *priv, strscpy(capability->card, DRIVER_CARD, sizeof(capability->card)); snprintf(capability->bus_info, sizeof(capability->bus_info), "platform:%s", radio->v4l2dev.name); - - capability->device_caps = V4L2_CAP_TUNER - | V4L2_CAP_RADIO - | V4L2_CAP_HW_FREQ_SEEK; - - si476x_core_lock(radio->core); - if (!si476x_core_is_a_secondary_tuner(radio->core)) - capability->device_caps |= V4L2_CAP_RDS_CAPTURE - | V4L2_CAP_READWRITE; - si476x_core_unlock(radio->core); - - capability->capabilities = capability->device_caps - | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -1459,6 +1446,14 @@ static int si476x_radio_probe(struct platform_device *pdev) radio->videodev.v4l2_dev = &radio->v4l2dev; radio->videodev.ioctl_ops = &si4761_ioctl_ops; + radio->videodev.device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO | + V4L2_CAP_HW_FREQ_SEEK; + + si476x_core_lock(radio->core); + if (!si476x_core_is_a_secondary_tuner(radio->core)) + radio->videodev.device_caps |= V4L2_CAP_RDS_CAPTURE | + V4L2_CAP_READWRITE; + si476x_core_unlock(radio->core); video_set_drvdata(&radio->videodev, radio); platform_set_drvdata(pdev, radio); diff --git a/drivers/media/radio/radio-tea5764.c b/drivers/media/radio/radio-tea5764.c index b740646adc53..877a24e5c577 100644 --- a/drivers/media/radio/radio-tea5764.c +++ b/drivers/media/radio/radio-tea5764.c @@ -282,8 +282,6 @@ static int vidioc_querycap(struct file *file, void *priv, strscpy(v->card, dev->name, sizeof(v->card)); snprintf(v->bus_info, sizeof(v->bus_info), "I2C:%s", dev_name(&dev->dev)); - v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO; - v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -465,6 +463,7 @@ static int tea5764_i2c_probe(struct i2c_client *client, video_set_drvdata(&radio->vdev, radio); radio->vdev.lock = &radio->mutex; radio->vdev.v4l2_dev = v4l2_dev; + radio->vdev.device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO; /* initialize and power off the chip */ tea5764_i2c_read(radio); diff --git a/drivers/media/radio/radio-tea5777.c b/drivers/media/radio/radio-tea5777.c index 49d4beba341e..fb9de7bbcd19 100644 --- a/drivers/media/radio/radio-tea5777.c +++ b/drivers/media/radio/radio-tea5777.c @@ -260,9 +260,6 @@ static int vidioc_querycap(struct file *file, void *priv, strscpy(v->card, tea->card, sizeof(v->card)); strlcat(v->card, " TEA5777", sizeof(v->card)); strscpy(v->bus_info, tea->bus_info, sizeof(v->bus_info)); - v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO; - v->device_caps |= V4L2_CAP_HW_FREQ_SEEK; - v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -553,6 +550,8 @@ int radio_tea5777_init(struct radio_tea5777 *tea, struct module *owner) strscpy(tea->vd.name, tea->v4l2_dev->name, sizeof(tea->vd.name)); tea->vd.lock = &tea->mutex; tea->vd.v4l2_dev = tea->v4l2_dev; + tea->vd.device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO | + V4L2_CAP_HW_FREQ_SEEK; tea->fops = tea575x_fops; tea->fops.owner = owner; tea->vd.fops = &tea->fops; diff --git a/drivers/media/radio/radio-timb.c b/drivers/media/radio/radio-timb.c index 7d196f8ad3b5..948ee3eec914 100644 --- a/drivers/media/radio/radio-timb.c +++ b/drivers/media/radio/radio-timb.c @@ -34,8 +34,6 @@ static int timbradio_vidioc_querycap(struct file *file, void *priv, strscpy(v->driver, DRIVER_NAME, sizeof(v->driver)); strscpy(v->card, "Timberdale Radio", sizeof(v->card)); snprintf(v->bus_info, sizeof(v->bus_info), "platform:"DRIVER_NAME); - v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO; - v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -114,6 +112,7 @@ static int timbradio_probe(struct platform_device *pdev) tr->video_dev.release = video_device_release_empty; tr->video_dev.minor = -1; tr->video_dev.lock = &tr->lock; + tr->video_dev.device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO; strscpy(tr->v4l2_dev.name, DRIVER_NAME, sizeof(tr->v4l2_dev.name)); err = v4l2_device_register(NULL, &tr->v4l2_dev); diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c index 330de50f8920..104ac41c6f96 100644 --- a/drivers/media/radio/radio-wl1273.c +++ b/drivers/media/radio/radio-wl1273.c @@ -1284,14 +1284,6 @@ static int wl1273_fm_vidioc_querycap(struct file *file, void *priv, sizeof(capability->card)); strscpy(capability->bus_info, radio->bus_type, sizeof(capability->bus_info)); - - capability->device_caps = V4L2_CAP_HW_FREQ_SEEK | - V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_AUDIO | - V4L2_CAP_RDS_CAPTURE | V4L2_CAP_MODULATOR | - V4L2_CAP_RDS_OUTPUT; - capability->capabilities = capability->device_caps | - V4L2_CAP_DEVICE_CAPS; - return 0; } @@ -1980,6 +1972,10 @@ static const struct video_device wl1273_viddev_template = { .name = WL1273_FM_DRIVER_NAME, .release = wl1273_vdev_release, .vfl_dir = VFL_DIR_TX, + .device_caps = V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_TUNER | + V4L2_CAP_RADIO | V4L2_CAP_AUDIO | + V4L2_CAP_RDS_CAPTURE | V4L2_CAP_MODULATOR | + V4L2_CAP_RDS_OUTPUT, }; static int wl1273_fm_radio_remove(struct platform_device *pdev) diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c index a3152d646c3a..7d53422b3b56 100644 --- a/drivers/media/radio/si470x/radio-si470x-i2c.c +++ b/drivers/media/radio/si470x/radio-si470x-i2c.c @@ -223,10 +223,6 @@ static int si470x_vidioc_querycap(struct file *file, void *priv, { strscpy(capability->driver, DRIVER_NAME, sizeof(capability->driver)); strscpy(capability->card, DRIVER_CARD, sizeof(capability->card)); - capability->device_caps = V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_READWRITE | - V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE; - capability->capabilities = capability->device_caps | V4L2_CAP_DEVICE_CAPS; - return 0; } @@ -382,6 +378,9 @@ static int si470x_i2c_probe(struct i2c_client *client, radio->videodev.lock = &radio->lock; radio->videodev.v4l2_dev = &radio->v4l2_dev; radio->videodev.release = video_device_release_empty; + radio->videodev.device_caps = + V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_READWRITE | V4L2_CAP_TUNER | + V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE; video_set_drvdata(&radio->videodev, radio); radio->gpio_reset = devm_gpiod_get_optional(&client->dev, "reset", diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c index 58e622d57373..49073747b1e7 100644 --- a/drivers/media/radio/si470x/radio-si470x-usb.c +++ b/drivers/media/radio/si470x/radio-si470x-usb.c @@ -514,9 +514,6 @@ static int si470x_vidioc_querycap(struct file *file, void *priv, strscpy(capability->card, DRIVER_CARD, sizeof(capability->card)); usb_make_path(radio->usbdev, capability->bus_info, sizeof(capability->bus_info)); - capability->device_caps = V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_READWRITE | - V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE; - capability->capabilities = capability->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -670,6 +667,9 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, radio->videodev.lock = &radio->lock; radio->videodev.v4l2_dev = &radio->v4l2_dev; radio->videodev.release = video_device_release_empty; + radio->videodev.device_caps = + V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_READWRITE | V4L2_CAP_TUNER | + V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE; video_set_drvdata(&radio->videodev, radio); /* get device and chip versions */ diff --git a/drivers/media/radio/si4713/radio-platform-si4713.c b/drivers/media/radio/si4713/radio-platform-si4713.c index 70d51d3607ff..a7dfe5f55c18 100644 --- a/drivers/media/radio/si4713/radio-platform-si4713.c +++ b/drivers/media/radio/si4713/radio-platform-si4713.c @@ -63,9 +63,6 @@ static int radio_si4713_querycap(struct file *file, void *priv, sizeof(capability->card)); strscpy(capability->bus_info, "platform:radio-si4713", sizeof(capability->bus_info)); - capability->device_caps = V4L2_CAP_MODULATOR | V4L2_CAP_RDS_OUTPUT; - capability->capabilities = capability->device_caps | V4L2_CAP_DEVICE_CAPS; - return 0; } @@ -175,6 +172,7 @@ static int radio_si4713_pdriver_probe(struct platform_device *pdev) rsdev->radio_dev.ctrl_handler = sd->ctrl_handler; /* Serialize all access to the si4713 */ rsdev->radio_dev.lock = &rsdev->lock; + rsdev->radio_dev.device_caps = V4L2_CAP_MODULATOR | V4L2_CAP_RDS_OUTPUT; video_set_drvdata(&rsdev->radio_dev, rsdev); if (video_register_device(&rsdev->radio_dev, VFL_TYPE_RADIO, radio_nr)) { dev_err(&pdev->dev, "Could not register video device.\n"); diff --git a/drivers/media/radio/si4713/radio-usb-si4713.c b/drivers/media/radio/si4713/radio-usb-si4713.c index 23065ecce979..33274189c83c 100644 --- a/drivers/media/radio/si4713/radio-usb-si4713.c +++ b/drivers/media/radio/si4713/radio-usb-si4713.c @@ -70,9 +70,6 @@ static int vidioc_querycap(struct file *file, void *priv, strscpy(v->driver, "radio-usb-si4713", sizeof(v->driver)); strscpy(v->card, "Si4713 FM Transmitter", sizeof(v->card)); usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info)); - v->device_caps = V4L2_CAP_MODULATOR | V4L2_CAP_RDS_OUTPUT; - v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; - return 0; } @@ -475,6 +472,7 @@ static int usb_si4713_probe(struct usb_interface *intf, radio->vdev.lock = &radio->lock; radio->vdev.release = video_device_release_empty; radio->vdev.vfl_dir = VFL_DIR_TX; + radio->vdev.device_caps = V4L2_CAP_MODULATOR | V4L2_CAP_RDS_OUTPUT; video_set_drvdata(&radio->vdev, radio); diff --git a/drivers/media/radio/tea575x.c b/drivers/media/radio/tea575x.c index 64613dd145a1..b0303cf00387 100644 --- a/drivers/media/radio/tea575x.c +++ b/drivers/media/radio/tea575x.c @@ -226,10 +226,6 @@ static int vidioc_querycap(struct file *file, void *priv, strscpy(v->card, tea->card, sizeof(v->card)); strlcat(v->card, tea->tea5759 ? " TEA5759" : " TEA5757", sizeof(v->card)); strscpy(v->bus_info, tea->bus_info, sizeof(v->bus_info)); - v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO; - if (!tea->cannot_read_data) - v->device_caps |= V4L2_CAP_HW_FREQ_SEEK; - v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -529,6 +525,9 @@ int snd_tea575x_init(struct snd_tea575x *tea, struct module *owner) strscpy(tea->vd.name, tea->v4l2_dev->name, sizeof(tea->vd.name)); tea->vd.lock = &tea->mutex; tea->vd.v4l2_dev = tea->v4l2_dev; + tea->vd.device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO; + if (!tea->cannot_read_data) + tea->vd.device_caps |= V4L2_CAP_HW_FREQ_SEEK; tea->fops = tea575x_fops; tea->fops.owner = owner; tea->vd.fops = &tea->fops; diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.c b/drivers/media/radio/wl128x/fmdrv_v4l2.c index c80a6df47f5e..1c146d14dbbd 100644 --- a/drivers/media/radio/wl128x/fmdrv_v4l2.c +++ b/drivers/media/radio/wl128x/fmdrv_v4l2.c @@ -185,13 +185,6 @@ static int fm_v4l2_vidioc_querycap(struct file *file, void *priv, strscpy(capability->card, FM_DRV_CARD_SHORT_NAME, sizeof(capability->card)); sprintf(capability->bus_info, "UART"); - capability->device_caps = V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_TUNER | - V4L2_CAP_RADIO | V4L2_CAP_MODULATOR | - V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | - V4L2_CAP_RDS_CAPTURE; - capability->capabilities = capability->device_caps | - V4L2_CAP_DEVICE_CAPS; - return 0; } @@ -515,6 +508,9 @@ static const struct video_device fm_viddev_template = { * but that would affect applications using this driver. */ .vfl_dir = VFL_DIR_M2M, + .device_caps = V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_TUNER | V4L2_CAP_RADIO | + V4L2_CAP_MODULATOR | V4L2_CAP_AUDIO | + V4L2_CAP_READWRITE | V4L2_CAP_RDS_CAPTURE, }; int fm_v4l2_init_video_device(struct fmdev *fmdev, int radio_nr) @@ -541,6 +537,7 @@ int fm_v4l2_init_video_device(struct fmdev *fmdev, int radio_nr) /* Register with V4L2 subsystem as RADIO device */ if (video_register_device(&gradio_dev, VFL_TYPE_RADIO, radio_nr)) { + v4l2_device_unregister(&fmdev->v4l2_dev); fmerr("Could not register video device\n"); return -ENOMEM; } @@ -554,6 +551,8 @@ int fm_v4l2_init_video_device(struct fmdev *fmdev, int radio_nr) if (ret < 0) { fmerr("(fmdev): Can't init ctrl handler\n"); v4l2_ctrl_handler_free(&fmdev->ctrl_handler); + video_unregister_device(fmdev->radio_dev); + v4l2_device_unregister(&fmdev->v4l2_dev); return -EBUSY; } diff --git a/drivers/media/rc/ir-spi.c b/drivers/media/rc/ir-spi.c index 66334e8d63ba..c58f2d38a458 100644 --- a/drivers/media/rc/ir-spi.c +++ b/drivers/media/rc/ir-spi.c @@ -161,6 +161,7 @@ static const struct of_device_id ir_spi_of_match[] = { { .compatible = "ir-spi-led" }, {}, }; +MODULE_DEVICE_TABLE(of, ir_spi_of_match); static struct spi_driver ir_spi_driver = { .probe = ir_spi_probe, diff --git a/drivers/media/rc/keymaps/rc-hauppauge.c b/drivers/media/rc/keymaps/rc-hauppauge.c index 582aa9012443..c117e9fc2697 100644 --- a/drivers/media/rc/keymaps/rc-hauppauge.c +++ b/drivers/media/rc/keymaps/rc-hauppauge.c @@ -233,6 +233,7 @@ static struct rc_map_table rc5_hauppauge_new[] = { * This one also uses RC-5 protocol * Keycodes start with address = 0x00 */ + { 0x000f, KEY_TV }, { 0x001f, KEY_TV }, { 0x0020, KEY_CHANNELUP }, { 0x000c, KEY_RADIO }, diff --git a/drivers/media/rc/meson-ir.c b/drivers/media/rc/meson-ir.c index 9e1a978a5fd9..02914da8cce5 100644 --- a/drivers/media/rc/meson-ir.c +++ b/drivers/media/rc/meson-ir.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0 /* * Driver for Amlogic Meson IR remote receiver * diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index be5fd129d728..13da4c5c7d17 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -1502,7 +1502,7 @@ static ssize_t store_wakeup_protocols(struct device *device, const char *buf, size_t len) { struct rc_dev *dev = to_rc_dev(device); - enum rc_proto protocol; + enum rc_proto protocol = RC_PROTO_UNKNOWN; ssize_t rc; u64 allowed; int i; @@ -1511,9 +1511,7 @@ static ssize_t store_wakeup_protocols(struct device *device, allowed = dev->allowed_wakeup_protocols; - if (sysfs_streq(buf, "none")) { - protocol = RC_PROTO_UNKNOWN; - } else { + if (!sysfs_streq(buf, "none")) { for (i = 0; i < ARRAY_SIZE(protocols); i++) { if ((allowed & (1ULL << i)) && sysfs_streq(buf, protocols[i].name)) { diff --git a/drivers/media/spi/Kconfig b/drivers/media/spi/Kconfig index ba464efdab03..08386abb9bbc 100644 --- a/drivers/media/spi/Kconfig +++ b/drivers/media/spi/Kconfig @@ -2,7 +2,7 @@ if VIDEO_V4L2 menu "SPI helper chips" - visible if !MEDIA_SUBDRV_AUTOSELECT || COMPILE_TEST + visible if !MEDIA_SUBDRV_AUTOSELECT || COMPILE_TEST || EXPERT config VIDEO_GS1662 tristate "Gennum Serializers video" diff --git a/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig index 72805e5abc68..a7108e575e9b 100644 --- a/drivers/media/tuners/Kconfig +++ b/drivers/media/tuners/Kconfig @@ -16,7 +16,7 @@ config MEDIA_TUNER select MEDIA_TUNER_MC44S803 if MEDIA_SUBDRV_AUTOSELECT menu "Customize TV tuners" - visible if !MEDIA_SUBDRV_AUTOSELECT || COMPILE_TEST + visible if !MEDIA_SUBDRV_AUTOSELECT || COMPILE_TEST || EXPERT depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_RADIO_SUPPORT || MEDIA_SDR_SUPPORT config MEDIA_TUNER_SIMPLE diff --git a/drivers/media/usb/airspy/airspy.c b/drivers/media/usb/airspy/airspy.c index 3329de6671ce..b35231ffe503 100644 --- a/drivers/media/usb/airspy/airspy.c +++ b/drivers/media/usb/airspy/airspy.c @@ -613,10 +613,6 @@ static int airspy_querycap(struct file *file, void *fh, strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); strscpy(cap->card, s->vdev.name, sizeof(cap->card)); usb_make_path(s->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE | V4L2_CAP_TUNER; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - return 0; } @@ -1057,6 +1053,8 @@ static int airspy_probe(struct usb_interface *intf, s->v4l2_dev.ctrl_handler = &s->hdl; s->vdev.v4l2_dev = &s->v4l2_dev; s->vdev.lock = &s->v4l2_lock; + s->vdev.device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE | V4L2_CAP_TUNER; ret = video_register_device(&s->vdev, VFL_TYPE_SDR, -1); if (ret) { diff --git a/drivers/media/usb/au0828/au0828-core.c b/drivers/media/usb/au0828/au0828-core.c index f746f6e2f686..a8a72d5fbd12 100644 --- a/drivers/media/usb/au0828/au0828-core.c +++ b/drivers/media/usb/au0828/au0828-core.c @@ -719,6 +719,12 @@ static int au0828_usb_probe(struct usb_interface *interface, /* Setup */ au0828_card_setup(dev); + /* + * Store the pointer to the au0828_dev so it can be accessed in + * au0828_usb_disconnect + */ + usb_set_intfdata(interface, dev); + /* Analog TV */ retval = au0828_analog_register(dev, interface); if (retval) { @@ -737,12 +743,6 @@ static int au0828_usb_probe(struct usb_interface *interface, /* Remote controller */ au0828_rc_register(dev); - /* - * Store the pointer to the au0828_dev so it can be accessed in - * au0828_usb_disconnect - */ - usb_set_intfdata(interface, dev); - pr_info("Registered device AU0828 [%s]\n", dev->board.name == NULL ? "Unset" : dev->board.name); diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index a414a25e48a8..5e00019bce8a 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -1182,7 +1182,6 @@ static int au0828_set_format(struct au0828_dev *dev, unsigned int cmd, static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct video_device *vdev = video_devdata(file); struct au0828_dev *dev = video_drvdata(file); dprintk(1, "%s called std_set %d dev_state %ld\n", __func__, @@ -1193,16 +1192,10 @@ static int vidioc_querycap(struct file *file, void *priv, usb_make_path(dev->usbdev, cap->bus_info, sizeof(cap->bus_info)); /* set the device capabilities */ - cap->device_caps = V4L2_CAP_AUDIO | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING | - V4L2_CAP_TUNER; - if (vdev->vfl_type == VFL_TYPE_GRABBER) - cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE; - else - cap->device_caps |= V4L2_CAP_VBI_CAPTURE; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS | - V4L2_CAP_VBI_CAPTURE | V4L2_CAP_VIDEO_CAPTURE; + cap->capabilities = + V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | + V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_DEVICE_CAPS; return 0; } @@ -1991,6 +1984,9 @@ int au0828_analog_register(struct au0828_dev *dev, dev->vdev.lock = &dev->lock; dev->vdev.queue = &dev->vb_vidq; dev->vdev.queue->lock = &dev->vb_queue_lock; + dev->vdev.device_caps = + V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | + V4L2_CAP_TUNER | V4L2_CAP_VIDEO_CAPTURE; strscpy(dev->vdev.name, "au0828a video", sizeof(dev->vdev.name)); /* Setup the VBI device */ @@ -1999,6 +1995,9 @@ int au0828_analog_register(struct au0828_dev *dev, dev->vbi_dev.lock = &dev->lock; dev->vbi_dev.queue = &dev->vb_vbiq; dev->vbi_dev.queue->lock = &dev->vb_vbi_queue_lock; + dev->vbi_dev.device_caps = + V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | + V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE; strscpy(dev->vbi_dev.name, "au0828a vbi", sizeof(dev->vbi_dev.name)); /* Init entities at the Media Controller */ diff --git a/drivers/media/usb/cpia2/cpia2_usb.c b/drivers/media/usb/cpia2/cpia2_usb.c index b2268981c963..17468f7d78ed 100644 --- a/drivers/media/usb/cpia2/cpia2_usb.c +++ b/drivers/media/usb/cpia2/cpia2_usb.c @@ -893,7 +893,6 @@ static void cpia2_usb_disconnect(struct usb_interface *intf) cpia2_unregister_camera(cam); v4l2_device_disconnect(&cam->v4l2_dev); mutex_unlock(&cam->v4l2_lock); - v4l2_device_put(&cam->v4l2_dev); if(cam->buffers) { DBG("Wakeup waiting processes\n"); @@ -902,6 +901,8 @@ static void cpia2_usb_disconnect(struct usb_interface *intf) wake_up_interruptible(&cam->wq_stream); } + v4l2_device_put(&cam->v4l2_dev); + LOG("CPiA2 camera disconnected.\n"); } diff --git a/drivers/media/usb/cpia2/cpia2_v4l.c b/drivers/media/usb/cpia2/cpia2_v4l.c index da6a5b2f86d1..0feae825cebb 100644 --- a/drivers/media/usb/cpia2/cpia2_v4l.c +++ b/drivers/media/usb/cpia2/cpia2_v4l.c @@ -250,13 +250,6 @@ static int cpia2_querycap(struct file *file, void *fh, struct v4l2_capability *v if (usb_make_path(cam->dev, vc->bus_info, sizeof(vc->bus_info)) <0) memset(vc->bus_info,0, sizeof(vc->bus_info)); - - vc->device_caps = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING; - vc->capabilities = vc->device_caps | - V4L2_CAP_DEVICE_CAPS; - return 0; } @@ -1152,6 +1145,8 @@ int cpia2_register_camera(struct camera_data *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); diff --git a/drivers/media/usb/cx231xx/cx231xx-dvb.c b/drivers/media/usb/cx231xx/cx231xx-dvb.c index 8fbb9523c88d..e205f7f0a56a 100644 --- a/drivers/media/usb/cx231xx/cx231xx-dvb.c +++ b/drivers/media/usb/cx231xx/cx231xx-dvb.c @@ -1147,6 +1147,7 @@ static int dvb_fini(struct cx231xx *dev) if (dev->dvb) { unregister_dvb(dev->dvb); + kfree(dev->dvb); dev->dvb = NULL; } diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index f8820478d46b..b651ac7713ea 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -1555,30 +1555,19 @@ static int vidioc_streamoff(struct file *file, void *priv, int cx231xx_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct video_device *vdev = video_devdata(file); struct cx231xx_fh *fh = priv; struct cx231xx *dev = fh->dev; strscpy(cap->driver, "cx231xx", sizeof(cap->driver)); strscpy(cap->card, cx231xx_boards[dev->model].name, sizeof(cap->card)); usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - - if (vdev->vfl_type == VFL_TYPE_RADIO) - cap->device_caps = V4L2_CAP_RADIO; - else { - cap->device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; - if (vdev->vfl_type == VFL_TYPE_VBI) - cap->device_caps |= V4L2_CAP_VBI_CAPTURE; - else - cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE; - } - if (dev->tuner_type != TUNER_ABSENT) - cap->device_caps |= V4L2_CAP_TUNER; - cap->capabilities = cap->device_caps | V4L2_CAP_READWRITE | + cap->capabilities = V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_DEVICE_CAPS; if (video_is_registered(&dev->radio_dev)) cap->capabilities |= V4L2_CAP_RADIO; + if (dev->tuner_type != TUNER_ABSENT) + cap->capabilities |= V4L2_CAP_TUNER; return 0; } @@ -2234,6 +2223,11 @@ int cx231xx_register_analog_devices(struct cx231xx *dev) dev_err(dev->dev, "failed to initialize video media entity!\n"); #endif dev->vdev.ctrl_handler = &dev->ctrl_handler; + dev->vdev.device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | + V4L2_CAP_VIDEO_CAPTURE; + if (dev->tuner_type != TUNER_ABSENT) + dev->vdev.device_caps |= V4L2_CAP_TUNER; + /* register v4l2 video video_device */ ret = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, video_nr[dev->devno]); @@ -2262,6 +2256,11 @@ int cx231xx_register_analog_devices(struct cx231xx *dev) dev_err(dev->dev, "failed to initialize vbi media entity!\n"); #endif dev->vbi_dev.ctrl_handler = &dev->ctrl_handler; + dev->vbi_dev.device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | + V4L2_CAP_VBI_CAPTURE; + if (dev->tuner_type != TUNER_ABSENT) + dev->vbi_dev.device_caps |= V4L2_CAP_TUNER; + /* register v4l2 vbi video_device */ ret = video_register_device(&dev->vbi_dev, VFL_TYPE_VBI, vbi_nr[dev->devno]); @@ -2277,6 +2276,7 @@ int cx231xx_register_analog_devices(struct cx231xx *dev) cx231xx_vdev_init(dev, &dev->radio_dev, &cx231xx_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[dev->devno]); if (ret < 0) { diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index de52309eaaab..3afd18733614 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -107,8 +107,6 @@ static int af9035_ctrl_msg(struct dvb_usb_device *d, struct usb_req *req) memcpy(req->rbuf, &state->buf[ACK_HDR_LEN], req->rlen); exit: mutex_unlock(&d->usb_mutex); - if (ret < 0) - dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c index 91729a39a306..7e817ea506c6 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c @@ -24,14 +24,19 @@ static int dvb_usb_v2_generic_io(struct dvb_usb_device *d, ret = usb_bulk_msg(d->udev, usb_sndbulkpipe(d->udev, d->props->generic_bulk_ctrl_endpoint), wbuf, wlen, &actual_length, 2000); - if (ret < 0) + if (ret) { dev_err(&d->udev->dev, "%s: usb_bulk_msg() failed=%d\n", KBUILD_MODNAME, ret); - else - ret = actual_length != wlen ? -EIO : 0; + return ret; + } + if (actual_length != wlen) { + dev_err(&d->udev->dev, "%s: usb_bulk_msg() write length=%d, actual=%d\n", + KBUILD_MODNAME, wlen, actual_length); + return -EIO; + } - /* an answer is expected, and no error before */ - if (!ret && rbuf && rlen) { + /* an answer is expected */ + if (rbuf && rlen) { if (d->props->generic_bulk_ctrl_delay) usleep_range(d->props->generic_bulk_ctrl_delay, d->props->generic_bulk_ctrl_delay diff --git a/drivers/media/usb/dvb-usb-v2/dvbsky.c b/drivers/media/usb/dvb-usb-v2/dvbsky.c index c41e10bd6ef7..8610487f2d72 100644 --- a/drivers/media/usb/dvb-usb-v2/dvbsky.c +++ b/drivers/media/usb/dvb-usb-v2/dvbsky.c @@ -91,8 +91,6 @@ static int dvbsky_gpio_ctrl(struct dvb_usb_device *d, u8 gport, u8 value) obuf[1] = gport; obuf[2] = value; ret = dvbsky_usb_generic_rw(d, obuf, 3, ibuf, 1); - if (ret) - dev_err(&d->udev->dev, "failed=%d\n", ret); return ret; } @@ -130,8 +128,6 @@ static int dvbsky_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], obuf[3] = msg[0].addr; ret = dvbsky_usb_generic_rw(d, obuf, 4, ibuf, msg[0].len + 1); - if (ret) - dev_err(&d->udev->dev, "failed=%d\n", ret); if (!ret) memcpy(msg[0].buf, &ibuf[1], msg[0].len); } else { @@ -142,8 +138,6 @@ static int dvbsky_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], memcpy(&obuf[3], msg[0].buf, msg[0].len); ret = dvbsky_usb_generic_rw(d, obuf, msg[0].len + 3, ibuf, 1); - if (ret) - dev_err(&d->udev->dev, "failed=%d\n", ret); } } else { if ((msg[0].len > 60) || (msg[1].len > 60)) { @@ -161,9 +155,6 @@ static int dvbsky_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], memcpy(&obuf[4], msg[0].buf, msg[0].len); ret = dvbsky_usb_generic_rw(d, obuf, msg[0].len + 4, ibuf, msg[1].len + 1); - if (ret) - dev_err(&d->udev->dev, "failed=%d\n", ret); - if (!ret) memcpy(msg[1].buf, &ibuf[1], msg[1].len); } @@ -192,8 +183,6 @@ static int dvbsky_rc_query(struct dvb_usb_device *d) obuf[0] = 0x10; ret = dvbsky_usb_generic_rw(d, obuf, 1, ibuf, 2); - if (ret) - dev_err(&d->udev->dev, "failed=%d\n", ret); if (ret == 0) code = (ibuf[0] << 8) | ibuf[1]; if (code != 0xffff) { diff --git a/drivers/media/usb/dvb-usb/Kconfig b/drivers/media/usb/dvb-usb/Kconfig index 87dbae875177..1a3e5f965ae4 100644 --- a/drivers/media/usb/dvb-usb/Kconfig +++ b/drivers/media/usb/dvb-usb/Kconfig @@ -139,12 +139,24 @@ config DVB_USB_CXUSB select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the Conexant USB2.0 hybrid reference design. - Currently, only DVB and ATSC modes are supported, analog mode - shall be added in the future. Devices that require this module: + DVB and ATSC modes are supported, for a basic analog mode support + see the next option ("Analog support for the Conexant USB2.0 hybrid + reference design"). + Devices that require this module: Medion MD95700 hybrid USB2.0 device. DViCO FusionHDTV (Bluebird) USB2.0 devices +config DVB_USB_CXUSB_ANALOG + bool "Analog support for the Conexant USB2.0 hybrid reference design" + depends on DVB_USB_CXUSB && VIDEO_V4L2 + select VIDEO_CX25840 + select VIDEOBUF2_VMALLOC + help + Say Y here to enable basic analog mode support for the Conexant + USB2.0 hybrid reference design. + Currently this mode is supported only on a Medion MD95700 device. + config DVB_USB_M920X tristate "Uli m920x DVB-T USB2.0 support" depends on DVB_USB diff --git a/drivers/media/usb/dvb-usb/Makefile b/drivers/media/usb/dvb-usb/Makefile index 407d90ca8be0..28e4806a87cd 100644 --- a/drivers/media/usb/dvb-usb/Makefile +++ b/drivers/media/usb/dvb-usb/Makefile @@ -42,6 +42,9 @@ dvb-usb-digitv-objs := digitv.o obj-$(CONFIG_DVB_USB_DIGITV) += dvb-usb-digitv.o dvb-usb-cxusb-objs := cxusb.o +ifeq ($(CONFIG_DVB_USB_CXUSB_ANALOG),y) +dvb-usb-cxusb-objs += cxusb-analog.o +endif obj-$(CONFIG_DVB_USB_CXUSB) += dvb-usb-cxusb.o dvb-usb-ttusb2-objs := ttusb2.o diff --git a/drivers/media/usb/dvb-usb/cxusb-analog.c b/drivers/media/usb/dvb-usb/cxusb-analog.c new file mode 100644 index 000000000000..0699f718d052 --- /dev/null +++ b/drivers/media/usb/dvb-usb/cxusb-analog.c @@ -0,0 +1,1845 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// DVB USB compliant linux driver for Conexant USB reference design - +// (analog part). +// +// Copyright (C) 2011, 2017, 2018 +// Maciej S. Szmigiero (mail@maciej.szmigiero.name) +// +// In case there are new analog / DVB-T hybrid devices released in the market +// using the same general design as Medion MD95700: a CX25840 video decoder +// outputting a BT.656 stream to a USB bridge chip which then forwards it to +// the host in isochronous USB packets this code should be made generic, with +// board specific bits implemented via separate card structures. +// +// This is, however, unlikely as the Medion model was released +// years ago (in 2005). +// +// TODO: +// * audio support, +// * finish radio support (requires audio of course), +// * VBI support, +// * controls support + +#include <linux/bitops.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/ktime.h> +#include <linux/vmalloc.h> +#include <media/drv-intf/cx25840.h> +#include <media/tuner.h> +#include <media/v4l2-fh.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-subdev.h> +#include <media/videobuf2-vmalloc.h> + +#include "cxusb.h" + +static int cxusb_medion_v_queue_setup(struct vb2_queue *q, + unsigned int *num_buffers, + unsigned int *num_planes, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct dvb_usb_device *dvbdev = vb2_get_drv_priv(q); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + unsigned int size = cxdev->width * cxdev->height * 2; + + if (*num_planes > 0) { + if (*num_planes != 1) + return -EINVAL; + + if (sizes[0] < size) + return -EINVAL; + } else { + *num_planes = 1; + sizes[0] = size; + } + + return 0; +} + +static int cxusb_medion_v_buf_init(struct vb2_buffer *vb) +{ + struct dvb_usb_device *dvbdev = vb2_get_drv_priv(vb->vb2_queue); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + + cxusb_vprintk(dvbdev, OPS, "buffer init\n"); + + if (vb2_plane_size(vb, 0) < cxdev->width * cxdev->height * 2) + return -ENOMEM; + + cxusb_vprintk(dvbdev, OPS, "buffer OK\n"); + + return 0; +} + +static void cxusb_auxbuf_init(struct dvb_usb_device *dvbdev, + struct cxusb_medion_auxbuf *auxbuf, + u8 *buf, unsigned int len) +{ + cxusb_vprintk(dvbdev, AUXB, "initializing auxbuf of len %u\n", len); + + auxbuf->buf = buf; + auxbuf->len = len; + auxbuf->paylen = 0; +} + +static void cxusb_auxbuf_head_trim(struct dvb_usb_device *dvbdev, + struct cxusb_medion_auxbuf *auxbuf, + unsigned int pos) +{ + if (pos == 0) + return; + + if (WARN_ON(pos > auxbuf->paylen)) + return; + + cxusb_vprintk(dvbdev, AUXB, + "trimming auxbuf len by %u to %u\n", + pos, auxbuf->paylen - pos); + + memmove(auxbuf->buf, auxbuf->buf + pos, auxbuf->paylen - pos); + auxbuf->paylen -= pos; +} + +static unsigned int cxusb_auxbuf_paylen(struct cxusb_medion_auxbuf *auxbuf) +{ + return auxbuf->paylen; +} + +static bool cxusb_auxbuf_make_space(struct dvb_usb_device *dvbdev, + struct cxusb_medion_auxbuf *auxbuf, + unsigned int howmuch) +{ + unsigned int freespace; + + if (WARN_ON(howmuch >= auxbuf->len)) + howmuch = auxbuf->len - 1; + + freespace = auxbuf->len - cxusb_auxbuf_paylen(auxbuf); + + cxusb_vprintk(dvbdev, AUXB, "freespace is %u\n", freespace); + + if (freespace >= howmuch) + return true; + + howmuch -= freespace; + + cxusb_vprintk(dvbdev, AUXB, "will overwrite %u bytes of buffer\n", + howmuch); + + cxusb_auxbuf_head_trim(dvbdev, auxbuf, howmuch); + + return false; +} + +/* returns false if some data was overwritten */ +static bool cxusb_auxbuf_append_urb(struct dvb_usb_device *dvbdev, + struct cxusb_medion_auxbuf *auxbuf, + struct urb *urb) +{ + unsigned long len; + int i; + bool ret; + + for (i = 0, len = 0; i < urb->number_of_packets; i++) + len += urb->iso_frame_desc[i].actual_length; + + ret = cxusb_auxbuf_make_space(dvbdev, auxbuf, len); + + for (i = 0; i < urb->number_of_packets; i++) { + unsigned int to_copy; + + to_copy = urb->iso_frame_desc[i].actual_length; + + memcpy(auxbuf->buf + auxbuf->paylen, urb->transfer_buffer + + urb->iso_frame_desc[i].offset, to_copy); + + auxbuf->paylen += to_copy; + } + + return ret; +} + +static bool cxusb_auxbuf_copy(struct cxusb_medion_auxbuf *auxbuf, + unsigned int pos, unsigned char *dest, + unsigned int len) +{ + if (pos + len > auxbuf->paylen) + return false; + + memcpy(dest, auxbuf->buf + pos, len); + + return true; +} + +static bool cxusb_medion_cf_refc_fld_chg(struct dvb_usb_device *dvbdev, + struct cxusb_bt656_params *bt656, + bool firstfield, + unsigned int maxlines, + unsigned int maxlinesamples, + unsigned char buf[4]) +{ + bool firstfield_code = (buf[3] & CXUSB_BT656_FIELD_MASK) == + CXUSB_BT656_FIELD_1; + unsigned int remlines; + + if (bt656->line == 0 || firstfield == firstfield_code) + return false; + + if (bt656->fmode == LINE_SAMPLES) { + unsigned int remsamples = maxlinesamples - + bt656->linesamples; + + cxusb_vprintk(dvbdev, BT656, + "field %c after line %u field change\n", + firstfield ? '1' : '2', bt656->line); + + if (bt656->buf && remsamples > 0) { + memset(bt656->buf, 0, remsamples); + bt656->buf += remsamples; + + cxusb_vprintk(dvbdev, BT656, + "field %c line %u %u samples still remaining (of %u)\n", + firstfield ? '1' : '2', + bt656->line, remsamples, + maxlinesamples); + } + + bt656->line++; + } + + remlines = maxlines - bt656->line; + if (bt656->buf && remlines > 0) { + memset(bt656->buf, 0, remlines * maxlinesamples); + bt656->buf += remlines * maxlinesamples; + + cxusb_vprintk(dvbdev, BT656, + "field %c %u lines still remaining (of %u)\n", + firstfield ? '1' : '2', remlines, + maxlines); + } + + return true; +} + +static void cxusb_medion_cf_refc_start_sch(struct dvb_usb_device *dvbdev, + struct cxusb_bt656_params *bt656, + bool firstfield, + unsigned char buf[4]) +{ + bool firstfield_code = (buf[3] & CXUSB_BT656_FIELD_MASK) == + CXUSB_BT656_FIELD_1; + bool sav_code = (buf[3] & CXUSB_BT656_SEAV_MASK) == + CXUSB_BT656_SEAV_SAV; + bool vbi_code = (buf[3] & CXUSB_BT656_VBI_MASK) == + CXUSB_BT656_VBI_ON; + + if (!sav_code || firstfield != firstfield_code) + return; + + if (!vbi_code) { + cxusb_vprintk(dvbdev, BT656, "line start @ pos %u\n", + bt656->pos); + + bt656->linesamples = 0; + bt656->fmode = LINE_SAMPLES; + } else { + cxusb_vprintk(dvbdev, BT656, "VBI start @ pos %u\n", + bt656->pos); + + bt656->fmode = VBI_SAMPLES; + } +} + +static void cxusb_medion_cf_refc_line_smpl(struct dvb_usb_device *dvbdev, + struct cxusb_bt656_params *bt656, + bool firstfield, + unsigned int maxlinesamples, + unsigned char buf[4]) +{ + bool sav_code = (buf[3] & CXUSB_BT656_SEAV_MASK) == + CXUSB_BT656_SEAV_SAV; + unsigned int remsamples; + + if (sav_code) + cxusb_vprintk(dvbdev, BT656, + "SAV in line samples @ line %u, pos %u\n", + bt656->line, bt656->pos); + + remsamples = maxlinesamples - bt656->linesamples; + if (bt656->buf && remsamples > 0) { + memset(bt656->buf, 0, remsamples); + bt656->buf += remsamples; + + cxusb_vprintk(dvbdev, BT656, + "field %c line %u %u samples still remaining (of %u)\n", + firstfield ? '1' : '2', bt656->line, remsamples, + maxlinesamples); + } + + bt656->fmode = START_SEARCH; + bt656->line++; +} + +static void cxusb_medion_cf_refc_vbi_smpl(struct dvb_usb_device *dvbdev, + struct cxusb_bt656_params *bt656, + unsigned char buf[4]) +{ + bool sav_code = (buf[3] & CXUSB_BT656_SEAV_MASK) == + CXUSB_BT656_SEAV_SAV; + + if (sav_code) + cxusb_vprintk(dvbdev, BT656, "SAV in VBI samples @ pos %u\n", + bt656->pos); + + bt656->fmode = START_SEARCH; +} + +/* returns whether the whole 4-byte code should be skipped in the buffer */ +static bool cxusb_medion_cf_ref_code(struct dvb_usb_device *dvbdev, + struct cxusb_bt656_params *bt656, + bool firstfield, + unsigned int maxlines, + unsigned int maxlinesamples, + unsigned char buf[4]) +{ + if (bt656->fmode == START_SEARCH) { + cxusb_medion_cf_refc_start_sch(dvbdev, bt656, firstfield, buf); + } else if (bt656->fmode == LINE_SAMPLES) { + cxusb_medion_cf_refc_line_smpl(dvbdev, bt656, firstfield, + maxlinesamples, buf); + return false; + } else if (bt656->fmode == VBI_SAMPLES) { + cxusb_medion_cf_refc_vbi_smpl(dvbdev, bt656, buf); + return false; + } + + return true; +} + +static bool cxusb_medion_cs_start_sch(struct dvb_usb_device *dvbdev, + struct cxusb_medion_auxbuf *auxbuf, + struct cxusb_bt656_params *bt656, + unsigned int maxlinesamples) +{ + unsigned char buf[64]; + unsigned int idx; + unsigned int tocheck = clamp_t(size_t, maxlinesamples / 4, 3, + sizeof(buf)); + + if (!cxusb_auxbuf_copy(auxbuf, bt656->pos + 1, buf, tocheck)) + return false; + + for (idx = 0; idx <= tocheck - 3; idx++) + if (memcmp(buf + idx, CXUSB_BT656_PREAMBLE, 3) == 0) { + bt656->pos += (1 + idx); + return true; + } + + cxusb_vprintk(dvbdev, BT656, "line %u early start, pos %u\n", + bt656->line, bt656->pos); + + bt656->linesamples = 0; + bt656->fmode = LINE_SAMPLES; + + return true; +} + +static void cxusb_medion_cs_line_smpl(struct cxusb_bt656_params *bt656, + unsigned int maxlinesamples, + unsigned char val) +{ + if (bt656->buf) + *(bt656->buf++) = val; + + bt656->linesamples++; + bt656->pos++; + + if (bt656->linesamples >= maxlinesamples) { + bt656->fmode = START_SEARCH; + bt656->line++; + } +} + +static bool cxusb_medion_copy_samples(struct dvb_usb_device *dvbdev, + struct cxusb_medion_auxbuf *auxbuf, + struct cxusb_bt656_params *bt656, + unsigned int maxlinesamples, + unsigned char val) +{ + if (bt656->fmode == START_SEARCH && bt656->line > 0) + return cxusb_medion_cs_start_sch(dvbdev, auxbuf, bt656, + maxlinesamples); + else if (bt656->fmode == LINE_SAMPLES) + cxusb_medion_cs_line_smpl(bt656, maxlinesamples, val); + else /* TODO: copy VBI samples */ + bt656->pos++; + + return true; +} + +static bool cxusb_medion_copy_field(struct dvb_usb_device *dvbdev, + struct cxusb_medion_auxbuf *auxbuf, + struct cxusb_bt656_params *bt656, + bool firstfield, + unsigned int maxlines, + unsigned int maxlinesmpls) +{ + while (bt656->line < maxlines) { + unsigned char val; + + if (!cxusb_auxbuf_copy(auxbuf, bt656->pos, &val, 1)) + break; + + if (val == CXUSB_BT656_PREAMBLE[0]) { + unsigned char buf[4]; + + buf[0] = val; + if (!cxusb_auxbuf_copy(auxbuf, bt656->pos + 1, + buf + 1, 3)) + break; + + if (buf[1] == CXUSB_BT656_PREAMBLE[1] && + buf[2] == CXUSB_BT656_PREAMBLE[2]) { + /* + * is this a field change? + * if so, terminate copying the current field + */ + if (cxusb_medion_cf_refc_fld_chg(dvbdev, + bt656, + firstfield, + maxlines, + maxlinesmpls, + buf)) + return true; + + if (cxusb_medion_cf_ref_code(dvbdev, bt656, + firstfield, + maxlines, + maxlinesmpls, + buf)) + bt656->pos += 4; + + continue; + } + } + + if (!cxusb_medion_copy_samples(dvbdev, auxbuf, bt656, + maxlinesmpls, val)) + break; + } + + if (bt656->line < maxlines) { + cxusb_vprintk(dvbdev, BT656, + "end of buffer pos = %u, line = %u\n", + bt656->pos, bt656->line); + return false; + } + + return true; +} + +static bool cxusb_medion_v_process_auxbuf(struct cxusb_medion_dev *cxdev, + bool reset) +{ + struct dvb_usb_device *dvbdev = cxdev->dvbdev; + struct cxusb_bt656_params *bt656 = &cxdev->bt656; + + /* + * if this is a new frame + * fetch a buffer from list + */ + if (bt656->mode == NEW_FRAME) { + if (!list_empty(&cxdev->buflist)) { + cxdev->vbuf = + list_first_entry(&cxdev->buflist, + struct cxusb_medion_vbuffer, + list); + list_del(&cxdev->vbuf->list); + } else { + dev_warn(&dvbdev->udev->dev, "no free buffers\n"); + } + } + + if (bt656->mode == NEW_FRAME || reset) { + cxusb_vprintk(dvbdev, URB, "will copy field 1\n"); + bt656->pos = 0; + bt656->mode = FIRST_FIELD; + bt656->fmode = START_SEARCH; + bt656->line = 0; + + if (cxdev->vbuf) { + cxdev->vbuf->vb2.vb2_buf.timestamp = ktime_get_ns(); + bt656->buf = vb2_plane_vaddr(&cxdev->vbuf->vb2.vb2_buf, + 0); + } + } + + if (bt656->mode == FIRST_FIELD) { + if (!cxusb_medion_copy_field(dvbdev, &cxdev->auxbuf, bt656, + true, cxdev->height / 2, + cxdev->width * 2)) + return false; + + /* + * do not trim buffer there in case + * we need to reset the search later + */ + + cxusb_vprintk(dvbdev, URB, "will copy field 2\n"); + bt656->mode = SECOND_FIELD; + bt656->fmode = START_SEARCH; + bt656->line = 0; + } + + if (bt656->mode == SECOND_FIELD) { + if (!cxusb_medion_copy_field(dvbdev, &cxdev->auxbuf, bt656, + false, cxdev->height / 2, + cxdev->width * 2)) + return false; + + cxusb_auxbuf_head_trim(dvbdev, &cxdev->auxbuf, bt656->pos); + + bt656->mode = NEW_FRAME; + + if (cxdev->vbuf) { + vb2_set_plane_payload(&cxdev->vbuf->vb2.vb2_buf, 0, + cxdev->width * cxdev->height * 2); + + cxdev->vbuf->vb2.field = cxdev->field_order; + cxdev->vbuf->vb2.sequence = cxdev->vbuf_sequence++; + + vb2_buffer_done(&cxdev->vbuf->vb2.vb2_buf, + VB2_BUF_STATE_DONE); + + cxdev->vbuf = NULL; + cxdev->bt656.buf = NULL; + + cxusb_vprintk(dvbdev, URB, "frame done\n"); + } else { + cxusb_vprintk(dvbdev, URB, "frame skipped\n"); + cxdev->vbuf_sequence++; + } + } + + return true; +} + +static bool cxusb_medion_v_complete_handle_urb(struct cxusb_medion_dev *cxdev, + bool *auxbuf_reset) +{ + struct dvb_usb_device *dvbdev = cxdev->dvbdev; + unsigned int urbn; + struct urb *urb; + int ret; + + *auxbuf_reset = false; + + urbn = cxdev->nexturb; + if (!test_bit(urbn, &cxdev->urbcomplete)) + return false; + + clear_bit(urbn, &cxdev->urbcomplete); + + do { + cxdev->nexturb++; + cxdev->nexturb %= CXUSB_VIDEO_URBS; + urb = cxdev->streamurbs[cxdev->nexturb]; + } while (!urb); + + urb = cxdev->streamurbs[urbn]; + cxusb_vprintk(dvbdev, URB, "URB %u status = %d\n", urbn, urb->status); + + if (urb->status == 0 || urb->status == -EXDEV) { + int i; + unsigned long len; + + for (i = 0, len = 0; i < urb->number_of_packets; i++) + len += urb->iso_frame_desc[i].actual_length; + + cxusb_vprintk(dvbdev, URB, "URB %u data len = %lu\n", urbn, + len); + + if (len > 0) { + cxusb_vprintk(dvbdev, URB, "appending URB\n"); + + /* + * append new data to auxbuf while + * overwriting old data if necessary + * + * if any overwrite happens then we can no + * longer rely on consistency of the whole + * data so let's start again the current + * auxbuf frame assembling process from + * the beginning + */ + *auxbuf_reset = + !cxusb_auxbuf_append_urb(dvbdev, + &cxdev->auxbuf, + urb); + } + } + + cxusb_vprintk(dvbdev, URB, "URB %u resubmit\n", urbn); + + ret = usb_submit_urb(urb, GFP_KERNEL); + if (ret != 0) + dev_err(&dvbdev->udev->dev, + "unable to resubmit URB %u (%d), you'll have to restart streaming\n", + urbn, ret); + + /* next URB is complete already? reschedule us then to handle it */ + return test_bit(cxdev->nexturb, &cxdev->urbcomplete); +} + +static void cxusb_medion_v_complete_work(struct work_struct *work) +{ + struct cxusb_medion_dev *cxdev = container_of(work, + struct cxusb_medion_dev, + urbwork); + struct dvb_usb_device *dvbdev = cxdev->dvbdev; + bool auxbuf_reset; + bool reschedule; + + mutex_lock(cxdev->videodev->lock); + + cxusb_vprintk(dvbdev, URB, "worker called, stop_streaming = %d\n", + (int)cxdev->stop_streaming); + + if (cxdev->stop_streaming) + goto unlock; + + reschedule = cxusb_medion_v_complete_handle_urb(cxdev, &auxbuf_reset); + + if (cxusb_medion_v_process_auxbuf(cxdev, auxbuf_reset)) + /* reschedule us until auxbuf no longer can produce any frame */ + reschedule = true; + + if (reschedule) { + cxusb_vprintk(dvbdev, URB, "rescheduling worker\n"); + schedule_work(&cxdev->urbwork); + } + +unlock: + mutex_unlock(cxdev->videodev->lock); +} + +static void cxusb_medion_v_complete(struct urb *u) +{ + struct dvb_usb_device *dvbdev = u->context; + struct cxusb_medion_dev *cxdev = dvbdev->priv; + unsigned int i; + + for (i = 0; i < CXUSB_VIDEO_URBS; i++) + if (cxdev->streamurbs[i] == u) + break; + + if (i >= CXUSB_VIDEO_URBS) { + dev_err(&dvbdev->udev->dev, + "complete on unknown URB\n"); + return; + } + + cxusb_vprintk(dvbdev, URB, "URB %u complete\n", i); + + set_bit(i, &cxdev->urbcomplete); + schedule_work(&cxdev->urbwork); +} + +static void cxusb_medion_urbs_free(struct cxusb_medion_dev *cxdev) +{ + unsigned int i; + + for (i = 0; i < CXUSB_VIDEO_URBS; i++) + if (cxdev->streamurbs[i]) { + kfree(cxdev->streamurbs[i]->transfer_buffer); + usb_free_urb(cxdev->streamurbs[i]); + cxdev->streamurbs[i] = NULL; + } +} + +static void cxusb_medion_return_buffers(struct cxusb_medion_dev *cxdev, + bool requeue) +{ + struct cxusb_medion_vbuffer *vbuf, *vbuf_tmp; + + list_for_each_entry_safe(vbuf, vbuf_tmp, &cxdev->buflist, + list) { + list_del(&vbuf->list); + vb2_buffer_done(&vbuf->vb2.vb2_buf, + requeue ? VB2_BUF_STATE_QUEUED : + VB2_BUF_STATE_ERROR); + } + + if (cxdev->vbuf) { + vb2_buffer_done(&cxdev->vbuf->vb2.vb2_buf, + requeue ? VB2_BUF_STATE_QUEUED : + VB2_BUF_STATE_ERROR); + + cxdev->vbuf = NULL; + cxdev->bt656.buf = NULL; + } +} + +static int cxusb_medion_v_ss_auxbuf_alloc(struct cxusb_medion_dev *cxdev, + int *npackets) +{ + struct dvb_usb_device *dvbdev = cxdev->dvbdev; + u8 *buf; + unsigned int framelen, urblen, auxbuflen; + + framelen = (cxdev->width * 2 + 4 + 4) * + (cxdev->height + 50 /* VBI lines */); + + /* + * try to fit a whole frame into each URB, as long as doing so + * does not require very high order memory allocations + */ + BUILD_BUG_ON(CXUSB_VIDEO_URB_MAX_SIZE / CXUSB_VIDEO_PKT_SIZE > + CXUSB_VIDEO_MAX_FRAME_PKTS); + *npackets = min_t(int, (framelen + CXUSB_VIDEO_PKT_SIZE - 1) / + CXUSB_VIDEO_PKT_SIZE, + CXUSB_VIDEO_URB_MAX_SIZE / CXUSB_VIDEO_PKT_SIZE); + urblen = *npackets * CXUSB_VIDEO_PKT_SIZE; + + cxusb_vprintk(dvbdev, URB, + "each URB will have %d packets for total of %u bytes (%u x %u @ %u)\n", + *npackets, urblen, (unsigned int)cxdev->width, + (unsigned int)cxdev->height, framelen); + + auxbuflen = framelen + urblen; + + buf = vmalloc(auxbuflen); + if (!buf) + return -ENOMEM; + + cxusb_auxbuf_init(dvbdev, &cxdev->auxbuf, buf, auxbuflen); + + return 0; +} + +static u32 cxusb_medion_norm2field_order(v4l2_std_id norm) +{ + bool is625 = norm & V4L2_STD_625_50; + bool is525 = norm & V4L2_STD_525_60; + + if (!is625 && !is525) + return V4L2_FIELD_NONE; + + if (is625 && is525) + return V4L2_FIELD_NONE; + + if (is625) + return V4L2_FIELD_SEQ_TB; + else /* is525 */ + return V4L2_FIELD_SEQ_BT; +} + +static u32 cxusb_medion_field_order(struct cxusb_medion_dev *cxdev) +{ + struct dvb_usb_device *dvbdev = cxdev->dvbdev; + u32 field; + int ret; + v4l2_std_id norm; + + /* TV tuner is PAL-only so it is always TB */ + if (cxdev->input == 0) + return V4L2_FIELD_SEQ_TB; + + field = cxusb_medion_norm2field_order(cxdev->norm); + if (field != V4L2_FIELD_NONE) + return field; + + ret = v4l2_subdev_call(cxdev->cx25840, video, g_std, &norm); + if (ret != 0) { + cxusb_vprintk(dvbdev, OPS, + "cannot get current standard for input %u\n", + (unsigned int)cxdev->input); + } else { + field = cxusb_medion_norm2field_order(norm); + if (field != V4L2_FIELD_NONE) + return field; + } + + dev_warn(&dvbdev->udev->dev, + "cannot determine field order for the current standard setup and received signal, using TB\n"); + return V4L2_FIELD_SEQ_TB; +} + +static int cxusb_medion_v_start_streaming(struct vb2_queue *q, + unsigned int count) +{ + struct dvb_usb_device *dvbdev = vb2_get_drv_priv(q); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + u8 streamon_params[2] = { 0x03, 0x00 }; + int npackets, i; + int ret; + + cxusb_vprintk(dvbdev, OPS, "should start streaming\n"); + + if (cxdev->stop_streaming) { + /* stream is being stopped */ + ret = -EBUSY; + goto ret_retbufs; + } + + cxdev->field_order = cxusb_medion_field_order(cxdev); + + ret = v4l2_subdev_call(cxdev->cx25840, video, s_stream, 1); + if (ret != 0) { + dev_err(&dvbdev->udev->dev, + "unable to start stream (%d)\n", ret); + goto ret_retbufs; + } + + ret = cxusb_ctrl_msg(dvbdev, CMD_STREAMING_ON, streamon_params, 2, + NULL, 0); + if (ret != 0) { + dev_err(&dvbdev->udev->dev, + "unable to start streaming (%d)\n", ret); + goto ret_unstream_cx; + } + + ret = cxusb_medion_v_ss_auxbuf_alloc(cxdev, &npackets); + if (ret != 0) + goto ret_unstream_md; + + for (i = 0; i < CXUSB_VIDEO_URBS; i++) { + int framen; + u8 *streambuf; + struct urb *surb; + + /* + * TODO: change this to an array of single pages to avoid + * doing a large continuous allocation when (if) + * s-g isochronous USB transfers are supported + */ + streambuf = kmalloc(npackets * CXUSB_VIDEO_PKT_SIZE, + GFP_KERNEL); + if (!streambuf) { + if (i < 2) { + ret = -ENOMEM; + goto ret_freeab; + } + break; + } + + surb = usb_alloc_urb(npackets, GFP_KERNEL); + if (!surb) { + kfree(streambuf); + ret = -ENOMEM; + goto ret_freeu; + } + + cxdev->streamurbs[i] = surb; + surb->dev = dvbdev->udev; + surb->context = dvbdev; + surb->pipe = usb_rcvisocpipe(dvbdev->udev, 2); + + surb->interval = 1; + surb->transfer_flags = URB_ISO_ASAP; + + surb->transfer_buffer = streambuf; + + surb->complete = cxusb_medion_v_complete; + surb->number_of_packets = npackets; + surb->transfer_buffer_length = npackets * CXUSB_VIDEO_PKT_SIZE; + + for (framen = 0; framen < npackets; framen++) { + surb->iso_frame_desc[framen].offset = + CXUSB_VIDEO_PKT_SIZE * framen; + + surb->iso_frame_desc[framen].length = + CXUSB_VIDEO_PKT_SIZE; + } + } + + cxdev->urbcomplete = 0; + cxdev->nexturb = 0; + cxdev->vbuf_sequence = 0; + + cxdev->vbuf = NULL; + cxdev->bt656.mode = NEW_FRAME; + cxdev->bt656.buf = NULL; + + for (i = 0; i < CXUSB_VIDEO_URBS; i++) + if (cxdev->streamurbs[i]) { + ret = usb_submit_urb(cxdev->streamurbs[i], + GFP_KERNEL); + if (ret != 0) + dev_err(&dvbdev->udev->dev, + "URB %d submission failed (%d)\n", i, + ret); + } + + return 0; + +ret_freeu: + cxusb_medion_urbs_free(cxdev); + +ret_freeab: + vfree(cxdev->auxbuf.buf); + +ret_unstream_md: + cxusb_ctrl_msg(dvbdev, CMD_STREAMING_OFF, NULL, 0, NULL, 0); + +ret_unstream_cx: + v4l2_subdev_call(cxdev->cx25840, video, s_stream, 0); + +ret_retbufs: + cxusb_medion_return_buffers(cxdev, true); + + return ret; +} + +static void cxusb_medion_v_stop_streaming(struct vb2_queue *q) +{ + struct dvb_usb_device *dvbdev = vb2_get_drv_priv(q); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + int ret; + unsigned int i; + + cxusb_vprintk(dvbdev, OPS, "should stop streaming\n"); + + if (WARN_ON(cxdev->stop_streaming)) + return; + + cxdev->stop_streaming = true; + + cxusb_ctrl_msg(dvbdev, CMD_STREAMING_OFF, NULL, 0, NULL, 0); + + ret = v4l2_subdev_call(cxdev->cx25840, video, s_stream, 0); + if (ret != 0) + dev_err(&dvbdev->udev->dev, "unable to stop stream (%d)\n", + ret); + + /* let URB completion run */ + mutex_unlock(cxdev->videodev->lock); + + for (i = 0; i < CXUSB_VIDEO_URBS; i++) + if (cxdev->streamurbs[i]) + usb_kill_urb(cxdev->streamurbs[i]); + + flush_work(&cxdev->urbwork); + + mutex_lock(cxdev->videodev->lock); + + /* free transfer buffer and URB */ + vfree(cxdev->auxbuf.buf); + + cxusb_medion_urbs_free(cxdev); + + cxusb_medion_return_buffers(cxdev, false); + + cxdev->stop_streaming = false; +} + +static void cxusub_medion_v_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *v4l2buf = to_vb2_v4l2_buffer(vb); + struct cxusb_medion_vbuffer *vbuf = + container_of(v4l2buf, struct cxusb_medion_vbuffer, vb2); + struct dvb_usb_device *dvbdev = vb2_get_drv_priv(vb->vb2_queue); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + + /* cxusb_vprintk(dvbdev, OPS, "mmmm.. a fresh buffer...\n"); */ + + list_add_tail(&vbuf->list, &cxdev->buflist); +} + +static const struct vb2_ops cxdev_video_qops = { + .queue_setup = cxusb_medion_v_queue_setup, + .buf_init = cxusb_medion_v_buf_init, + .start_streaming = cxusb_medion_v_start_streaming, + .stop_streaming = cxusb_medion_v_stop_streaming, + .buf_queue = cxusub_medion_v_buf_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish +}; + +static const __u32 videocaps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | + V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; +static const __u32 radiocaps = V4L2_CAP_TUNER | V4L2_CAP_RADIO; + +static int cxusb_medion_v_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct dvb_usb_device *dvbdev = video_drvdata(file); + + strscpy(cap->driver, dvbdev->udev->dev.driver->name, + sizeof(cap->driver)); + strscpy(cap->card, "Medion 95700", sizeof(cap->card)); + usb_make_path(dvbdev->udev, cap->bus_info, sizeof(cap->bus_info)); + + cap->capabilities = videocaps | radiocaps | V4L2_CAP_DEVICE_CAPS; + + return 0; +} + +static int cxusb_medion_v_enum_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + if (f->index != 0) + return -EINVAL; + + f->pixelformat = V4L2_PIX_FMT_UYVY; + + return 0; +} + +static int cxusb_medion_g_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct dvb_usb_device *dvbdev = video_drvdata(file); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + + f->fmt.pix.width = cxdev->width; + f->fmt.pix.height = cxdev->height; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; + f->fmt.pix.field = vb2_start_streaming_called(&cxdev->videoqueue) ? + cxdev->field_order : cxusb_medion_field_order(cxdev); + f->fmt.pix.bytesperline = cxdev->width * 2; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height; + + return 0; +} + +static int cxusb_medion_try_s_fmt_vid_cap(struct file *file, + struct v4l2_format *f, + bool isset) +{ + struct dvb_usb_device *dvbdev = video_drvdata(file); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + struct v4l2_subdev_format subfmt; + u32 field; + int ret; + + if (isset && vb2_is_busy(&cxdev->videoqueue)) + return -EBUSY; + + field = vb2_start_streaming_called(&cxdev->videoqueue) ? + cxdev->field_order : cxusb_medion_field_order(cxdev); + + memset(&subfmt, 0, sizeof(subfmt)); + subfmt.which = isset ? V4L2_SUBDEV_FORMAT_ACTIVE : + V4L2_SUBDEV_FORMAT_TRY; + subfmt.format.width = f->fmt.pix.width & ~1; + subfmt.format.height = f->fmt.pix.height & ~1; + subfmt.format.code = MEDIA_BUS_FMT_FIXED; + subfmt.format.field = field; + subfmt.format.colorspace = V4L2_COLORSPACE_SMPTE170M; + + ret = v4l2_subdev_call(cxdev->cx25840, pad, set_fmt, NULL, &subfmt); + if (ret != 0) + return ret; + + f->fmt.pix.width = subfmt.format.width; + f->fmt.pix.height = subfmt.format.height; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; + f->fmt.pix.field = field; + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + + if (isset) { + cxdev->width = f->fmt.pix.width; + cxdev->height = f->fmt.pix.height; + } + + return 0; +} + +static int cxusb_medion_try_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + return cxusb_medion_try_s_fmt_vid_cap(file, f, false); +} + +static int cxusb_medion_s_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + return cxusb_medion_try_s_fmt_vid_cap(file, f, true); +} + +static const struct { + struct v4l2_input input; + u32 inputcfg; +} cxusb_medion_inputs[] = { + { .input = { .name = "TV tuner", .type = V4L2_INPUT_TYPE_TUNER, + .tuner = 0, .std = V4L2_STD_PAL }, + .inputcfg = CX25840_COMPOSITE2, }, + + { .input = { .name = "Composite", .type = V4L2_INPUT_TYPE_CAMERA, + .std = V4L2_STD_ALL }, + .inputcfg = CX25840_COMPOSITE1, }, + + { .input = { .name = "S-Video", .type = V4L2_INPUT_TYPE_CAMERA, + .std = V4L2_STD_ALL }, + .inputcfg = CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 } +}; + +#define CXUSB_INPUT_CNT ARRAY_SIZE(cxusb_medion_inputs) + +static int cxusb_medion_enum_input(struct file *file, void *fh, + struct v4l2_input *inp) +{ + struct dvb_usb_device *dvbdev = video_drvdata(file); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + u32 index = inp->index; + + if (index >= CXUSB_INPUT_CNT) + return -EINVAL; + + *inp = cxusb_medion_inputs[index].input; + inp->index = index; + inp->capabilities |= V4L2_IN_CAP_STD; + + if (index == cxdev->input) { + int ret; + u32 status = 0; + + ret = v4l2_subdev_call(cxdev->cx25840, video, g_input_status, + &status); + if (ret != 0) + dev_warn(&dvbdev->udev->dev, + "cx25840 input status query failed (%d)\n", + ret); + else + inp->status = status; + } + + return 0; +} + +static int cxusb_medion_g_input(struct file *file, void *fh, + unsigned int *i) +{ + struct dvb_usb_device *dvbdev = video_drvdata(file); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + + *i = cxdev->input; + + return 0; +} + +static int cxusb_medion_set_norm(struct cxusb_medion_dev *cxdev, + v4l2_std_id norm) +{ + struct dvb_usb_device *dvbdev = cxdev->dvbdev; + int ret; + + cxusb_vprintk(dvbdev, OPS, + "trying to set standard for input %u to %lx\n", + (unsigned int)cxdev->input, + (unsigned long)norm); + + /* no autodetection support */ + if (norm == V4L2_STD_UNKNOWN) + return -EINVAL; + + /* on composite or S-Video any std is acceptable */ + if (cxdev->input != 0) { + ret = v4l2_subdev_call(cxdev->cx25840, video, s_std, norm); + if (ret) + return ret; + + goto ret_savenorm; + } + + /* TV tuner is only able to demodulate PAL */ + if ((norm & ~V4L2_STD_PAL) != 0) + return -EINVAL; + + ret = v4l2_subdev_call(cxdev->tda9887, video, s_std, norm); + if (ret != 0) { + dev_err(&dvbdev->udev->dev, + "tda9887 norm setup failed (%d)\n", + ret); + return ret; + } + + ret = v4l2_subdev_call(cxdev->tuner, video, s_std, norm); + if (ret != 0) { + dev_err(&dvbdev->udev->dev, + "tuner norm setup failed (%d)\n", + ret); + return ret; + } + + ret = v4l2_subdev_call(cxdev->cx25840, video, s_std, norm); + if (ret != 0) { + dev_err(&dvbdev->udev->dev, + "cx25840 norm setup failed (%d)\n", + ret); + return ret; + } + +ret_savenorm: + cxdev->norm = norm; + + return 0; +} + +static int cxusb_medion_s_input(struct file *file, void *fh, + unsigned int i) +{ + struct dvb_usb_device *dvbdev = video_drvdata(file); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + int ret; + v4l2_std_id norm; + + if (i >= CXUSB_INPUT_CNT) + return -EINVAL; + + ret = v4l2_subdev_call(cxdev->cx25840, video, s_routing, + cxusb_medion_inputs[i].inputcfg, 0, 0); + if (ret != 0) + return ret; + + cxdev->input = i; + cxdev->videodev->tvnorms = cxusb_medion_inputs[i].input.std; + + norm = cxdev->norm & cxusb_medion_inputs[i].input.std; + if (norm == 0) + norm = cxusb_medion_inputs[i].input.std; + + cxusb_medion_set_norm(cxdev, norm); + + return 0; +} + +static int cxusb_medion_g_tuner(struct file *file, void *fh, + struct v4l2_tuner *tuner) +{ + struct dvb_usb_device *dvbdev = video_drvdata(file); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + struct video_device *vdev = video_devdata(file); + int ret; + + if (tuner->index != 0) + return -EINVAL; + + if (vdev->vfl_type == VFL_TYPE_GRABBER) + tuner->type = V4L2_TUNER_ANALOG_TV; + else + tuner->type = V4L2_TUNER_RADIO; + + tuner->capability = 0; + tuner->afc = 0; + + /* + * fills: + * always: capability (static), rangelow (static), rangehigh (static) + * radio mode: afc (may fail silently), rxsubchans (static), audmode + */ + ret = v4l2_subdev_call(cxdev->tda9887, tuner, g_tuner, tuner); + if (ret != 0) + return ret; + + /* + * fills: + * always: capability (static), rangelow (static), rangehigh (static) + * radio mode: rxsubchans (always stereo), audmode, + * signal (might be wrong) + */ + ret = v4l2_subdev_call(cxdev->tuner, tuner, g_tuner, tuner); + if (ret != 0) + return ret; + + tuner->signal = 0; + + /* + * fills: TV mode: capability, rxsubchans, audmode, signal + */ + ret = v4l2_subdev_call(cxdev->cx25840, tuner, g_tuner, tuner); + if (ret != 0) + return ret; + + if (vdev->vfl_type == VFL_TYPE_GRABBER) + strscpy(tuner->name, "TV tuner", sizeof(tuner->name)); + else + strscpy(tuner->name, "Radio tuner", sizeof(tuner->name)); + + memset(tuner->reserved, 0, sizeof(tuner->reserved)); + + return 0; +} + +static int cxusb_medion_s_tuner(struct file *file, void *fh, + const struct v4l2_tuner *tuner) +{ + struct dvb_usb_device *dvbdev = video_drvdata(file); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + struct video_device *vdev = video_devdata(file); + int ret; + + if (tuner->index != 0) + return -EINVAL; + + ret = v4l2_subdev_call(cxdev->tda9887, tuner, s_tuner, tuner); + if (ret != 0) + return ret; + + ret = v4l2_subdev_call(cxdev->tuner, tuner, s_tuner, tuner); + if (ret != 0) + return ret; + + /* + * make sure that cx25840 is in a correct TV / radio mode, + * since calls above may have changed it for tuner / IF demod + */ + if (vdev->vfl_type == VFL_TYPE_GRABBER) + v4l2_subdev_call(cxdev->cx25840, video, s_std, cxdev->norm); + else + v4l2_subdev_call(cxdev->cx25840, tuner, s_radio); + + return v4l2_subdev_call(cxdev->cx25840, tuner, s_tuner, tuner); +} + +static int cxusb_medion_g_frequency(struct file *file, void *fh, + struct v4l2_frequency *freq) +{ + struct dvb_usb_device *dvbdev = video_drvdata(file); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + + if (freq->tuner != 0) + return -EINVAL; + + return v4l2_subdev_call(cxdev->tuner, tuner, g_frequency, freq); +} + +static int cxusb_medion_s_frequency(struct file *file, void *fh, + const struct v4l2_frequency *freq) +{ + struct dvb_usb_device *dvbdev = video_drvdata(file); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + struct video_device *vdev = video_devdata(file); + int ret; + + if (freq->tuner != 0) + return -EINVAL; + + ret = v4l2_subdev_call(cxdev->tda9887, tuner, s_frequency, freq); + if (ret != 0) + return ret; + + ret = v4l2_subdev_call(cxdev->tuner, tuner, s_frequency, freq); + if (ret != 0) + return ret; + + /* + * make sure that cx25840 is in a correct TV / radio mode, + * since calls above may have changed it for tuner / IF demod + */ + if (vdev->vfl_type == VFL_TYPE_GRABBER) + v4l2_subdev_call(cxdev->cx25840, video, s_std, cxdev->norm); + else + v4l2_subdev_call(cxdev->cx25840, tuner, s_radio); + + return v4l2_subdev_call(cxdev->cx25840, tuner, s_frequency, freq); +} + +static int cxusb_medion_g_std(struct file *file, void *fh, + v4l2_std_id *norm) +{ + struct dvb_usb_device *dvbdev = video_drvdata(file); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + + *norm = cxdev->norm; + + if (*norm == V4L2_STD_UNKNOWN) + return -ENODATA; + + return 0; +} + +static int cxusb_medion_s_std(struct file *file, void *fh, + v4l2_std_id norm) +{ + struct dvb_usb_device *dvbdev = video_drvdata(file); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + + return cxusb_medion_set_norm(cxdev, norm); +} + +static int cxusb_medion_querystd(struct file *file, void *fh, + v4l2_std_id *norm) +{ + struct dvb_usb_device *dvbdev = video_drvdata(file); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + v4l2_std_id norm_mask; + int ret; + + /* + * make sure we don't have improper std bits set for the TV tuner + * (could happen when no signal was present yet after reset) + */ + if (cxdev->input == 0) + norm_mask = V4L2_STD_PAL; + else + norm_mask = V4L2_STD_ALL; + + ret = v4l2_subdev_call(cxdev->cx25840, video, querystd, norm); + if (ret != 0) { + cxusb_vprintk(dvbdev, OPS, + "cannot get detected standard for input %u\n", + (unsigned int)cxdev->input); + return ret; + } + + cxusb_vprintk(dvbdev, OPS, "input %u detected standard is %lx\n", + (unsigned int)cxdev->input, (unsigned long)*norm); + *norm &= norm_mask; + + return 0; +} + +static int cxusb_medion_log_status(struct file *file, void *fh) +{ + struct dvb_usb_device *dvbdev = video_drvdata(file); + struct cxusb_medion_dev *cxdev = dvbdev->priv; + + v4l2_device_call_all(&cxdev->v4l2dev, 0, core, log_status); + + return 0; +} + +static const struct v4l2_ioctl_ops cxusb_video_ioctl = { + .vidioc_querycap = cxusb_medion_v_querycap, + .vidioc_enum_fmt_vid_cap = cxusb_medion_v_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cxusb_medion_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = cxusb_medion_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cxusb_medion_try_fmt_vid_cap, + .vidioc_enum_input = cxusb_medion_enum_input, + .vidioc_g_input = cxusb_medion_g_input, + .vidioc_s_input = cxusb_medion_s_input, + .vidioc_g_tuner = cxusb_medion_g_tuner, + .vidioc_s_tuner = cxusb_medion_s_tuner, + .vidioc_g_frequency = cxusb_medion_g_frequency, + .vidioc_s_frequency = cxusb_medion_s_frequency, + .vidioc_g_std = cxusb_medion_g_std, + .vidioc_s_std = cxusb_medion_s_std, + .vidioc_querystd = cxusb_medion_querystd, + .vidioc_log_status = cxusb_medion_log_status, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff +}; + +static const struct v4l2_ioctl_ops cxusb_radio_ioctl = { + .vidioc_querycap = cxusb_medion_v_querycap, + .vidioc_g_tuner = cxusb_medion_g_tuner, + .vidioc_s_tuner = cxusb_medion_s_tuner, + .vidioc_g_frequency = cxusb_medion_g_frequency, + .vidioc_s_frequency = cxusb_medion_s_frequency, + .vidioc_log_status = cxusb_medion_log_status +}; + +/* + * in principle, this should be const, but s_io_pin_config is declared + * to take non-const, and gcc complains + */ +static struct v4l2_subdev_io_pin_config cxusub_medion_pin_config[] = { + { .pin = CX25840_PIN_DVALID_PRGM0, .function = CX25840_PAD_DEFAULT, + .strength = CX25840_PIN_DRIVE_MEDIUM }, + { .pin = CX25840_PIN_PLL_CLK_PRGM7, .function = CX25840_PAD_AUX_PLL }, + { .pin = CX25840_PIN_HRESET_PRGM2, .function = CX25840_PAD_ACTIVE, + .strength = CX25840_PIN_DRIVE_MEDIUM } +}; + +int cxusb_medion_analog_init(struct dvb_usb_device *dvbdev) +{ + struct cxusb_medion_dev *cxdev = dvbdev->priv; + u8 tuner_analog_msg_data[] = { 0x9c, 0x60, 0x85, 0x54 }; + struct i2c_msg tuner_analog_msg = { .addr = 0x61, .flags = 0, + .buf = tuner_analog_msg_data, + .len = + sizeof(tuner_analog_msg_data) }; + struct v4l2_subdev_format subfmt; + int ret; + + /* switch tuner to analog mode so IF demod will become accessible */ + ret = i2c_transfer(&dvbdev->i2c_adap, &tuner_analog_msg, 1); + if (ret != 1) + dev_warn(&dvbdev->udev->dev, + "tuner analog switch failed (%d)\n", ret); + + /* + * cx25840 might have lost power during mode switching so we need + * to set it again + */ + ret = v4l2_subdev_call(cxdev->cx25840, core, reset, 0); + if (ret != 0) + dev_warn(&dvbdev->udev->dev, + "cx25840 reset failed (%d)\n", ret); + + ret = v4l2_subdev_call(cxdev->cx25840, video, s_routing, + CX25840_COMPOSITE1, 0, 0); + if (ret != 0) + dev_warn(&dvbdev->udev->dev, + "cx25840 initial input setting failed (%d)\n", ret); + + /* composite */ + cxdev->input = 1; + cxdev->videodev->tvnorms = V4L2_STD_ALL; + cxdev->norm = V4L2_STD_PAL; + + /* TODO: setup audio samples insertion */ + + ret = v4l2_subdev_call(cxdev->cx25840, core, s_io_pin_config, + ARRAY_SIZE(cxusub_medion_pin_config), + cxusub_medion_pin_config); + if (ret != 0) + dev_warn(&dvbdev->udev->dev, + "cx25840 pin config failed (%d)\n", ret); + + /* make sure that we aren't in radio mode */ + v4l2_subdev_call(cxdev->tda9887, video, s_std, cxdev->norm); + v4l2_subdev_call(cxdev->tuner, video, s_std, cxdev->norm); + v4l2_subdev_call(cxdev->cx25840, video, s_std, cxdev->norm); + + memset(&subfmt, 0, sizeof(subfmt)); + subfmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + subfmt.format.width = cxdev->width; + subfmt.format.height = cxdev->height; + subfmt.format.code = MEDIA_BUS_FMT_FIXED; + subfmt.format.field = V4L2_FIELD_SEQ_TB; + subfmt.format.colorspace = V4L2_COLORSPACE_SMPTE170M; + + ret = v4l2_subdev_call(cxdev->cx25840, pad, set_fmt, NULL, &subfmt); + if (ret != 0) + dev_warn(&dvbdev->udev->dev, + "cx25840 format set failed (%d)\n", ret); + + if (ret == 0) { + cxdev->width = subfmt.format.width; + cxdev->height = subfmt.format.height; + } + + return 0; +} + +static int cxusb_videoradio_open(struct file *f) +{ + struct dvb_usb_device *dvbdev = video_drvdata(f); + int ret; + + /* + * no locking needed since this call only modifies analog + * state if there are no other analog handles currenly + * opened so ops done via them cannot create a conflict + */ + ret = cxusb_medion_get(dvbdev, CXUSB_OPEN_ANALOG); + if (ret != 0) + return ret; + + ret = v4l2_fh_open(f); + if (ret != 0) + goto ret_release; + + cxusb_vprintk(dvbdev, OPS, "got open\n"); + + return 0; + +ret_release: + cxusb_medion_put(dvbdev); + + return ret; +} + +static int cxusb_videoradio_release(struct file *f) +{ + struct video_device *vdev = video_devdata(f); + struct dvb_usb_device *dvbdev = video_drvdata(f); + int ret; + + cxusb_vprintk(dvbdev, OPS, "got release\n"); + + if (vdev->vfl_type == VFL_TYPE_GRABBER) + ret = vb2_fop_release(f); + else + ret = v4l2_fh_release(f); + + cxusb_medion_put(dvbdev); + + return ret; +} + +static const struct v4l2_file_operations cxusb_video_fops = { + .owner = THIS_MODULE, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, + .open = cxusb_videoradio_open, + .release = cxusb_videoradio_release +}; + +static const struct v4l2_file_operations cxusb_radio_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, + .open = cxusb_videoradio_open, + .release = cxusb_videoradio_release +}; + +static void cxusb_medion_v4l2_release(struct v4l2_device *v4l2_dev) +{ + struct cxusb_medion_dev *cxdev = + container_of(v4l2_dev, struct cxusb_medion_dev, v4l2dev); + struct dvb_usb_device *dvbdev = cxdev->dvbdev; + + cxusb_vprintk(dvbdev, OPS, "v4l2 device release\n"); + + v4l2_device_unregister(&cxdev->v4l2dev); + + mutex_destroy(&cxdev->dev_lock); + + while (completion_done(&cxdev->v4l2_release)) + schedule(); + + complete(&cxdev->v4l2_release); +} + +static void cxusb_medion_videodev_release(struct video_device *vdev) +{ + struct dvb_usb_device *dvbdev = video_get_drvdata(vdev); + + cxusb_vprintk(dvbdev, OPS, "video device release\n"); + + vb2_queue_release(vdev->queue); + + video_device_release(vdev); +} + +static int cxusb_medion_register_analog_video(struct dvb_usb_device *dvbdev) +{ + struct cxusb_medion_dev *cxdev = dvbdev->priv; + int ret; + + cxdev->videoqueue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cxdev->videoqueue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ | + VB2_DMABUF; + cxdev->videoqueue.ops = &cxdev_video_qops; + cxdev->videoqueue.mem_ops = &vb2_vmalloc_memops; + cxdev->videoqueue.drv_priv = dvbdev; + cxdev->videoqueue.buf_struct_size = + sizeof(struct cxusb_medion_vbuffer); + cxdev->videoqueue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + cxdev->videoqueue.min_buffers_needed = 6; + cxdev->videoqueue.lock = &cxdev->dev_lock; + + ret = vb2_queue_init(&cxdev->videoqueue); + if (ret) { + dev_err(&dvbdev->udev->dev, + "video queue init failed, ret = %d\n", ret); + return ret; + } + + cxdev->videodev = video_device_alloc(); + if (!cxdev->videodev) { + dev_err(&dvbdev->udev->dev, "video device alloc failed\n"); + ret = -ENOMEM; + goto ret_qrelease; + } + + cxdev->videodev->device_caps = videocaps; + cxdev->videodev->fops = &cxusb_video_fops; + cxdev->videodev->v4l2_dev = &cxdev->v4l2dev; + cxdev->videodev->queue = &cxdev->videoqueue; + strscpy(cxdev->videodev->name, "cxusb", sizeof(cxdev->videodev->name)); + cxdev->videodev->vfl_dir = VFL_DIR_RX; + cxdev->videodev->ioctl_ops = &cxusb_video_ioctl; + cxdev->videodev->tvnorms = V4L2_STD_ALL; + cxdev->videodev->release = cxusb_medion_videodev_release; + cxdev->videodev->lock = &cxdev->dev_lock; + video_set_drvdata(cxdev->videodev, dvbdev); + + ret = video_register_device(cxdev->videodev, VFL_TYPE_GRABBER, -1); + if (ret) { + dev_err(&dvbdev->udev->dev, + "video device register failed, ret = %d\n", ret); + goto ret_vrelease; + } + + return 0; + +ret_vrelease: + video_device_release(cxdev->videodev); + +ret_qrelease: + vb2_queue_release(&cxdev->videoqueue); + + return ret; +} + +static int cxusb_medion_register_analog_radio(struct dvb_usb_device *dvbdev) +{ + struct cxusb_medion_dev *cxdev = dvbdev->priv; + int ret; + + cxdev->radiodev = video_device_alloc(); + if (!cxdev->radiodev) { + dev_err(&dvbdev->udev->dev, "radio device alloc failed\n"); + return -ENOMEM; + } + + cxdev->radiodev->device_caps = radiocaps; + cxdev->radiodev->fops = &cxusb_radio_fops; + cxdev->radiodev->v4l2_dev = &cxdev->v4l2dev; + strscpy(cxdev->radiodev->name, "cxusb", sizeof(cxdev->radiodev->name)); + cxdev->radiodev->vfl_dir = VFL_DIR_RX; + cxdev->radiodev->ioctl_ops = &cxusb_radio_ioctl; + cxdev->radiodev->release = video_device_release; + cxdev->radiodev->lock = &cxdev->dev_lock; + video_set_drvdata(cxdev->radiodev, dvbdev); + + ret = video_register_device(cxdev->radiodev, VFL_TYPE_RADIO, -1); + if (ret) { + dev_err(&dvbdev->udev->dev, + "radio device register failed, ret = %d\n", ret); + video_device_release(cxdev->radiodev); + return ret; + } + + return 0; +} + +static int cxusb_medion_register_analog_subdevs(struct dvb_usb_device *dvbdev) +{ + struct cxusb_medion_dev *cxdev = dvbdev->priv; + int ret; + struct tuner_setup tun_setup; + + /* attach cx25840 capture chip */ + cxdev->cx25840 = v4l2_i2c_new_subdev(&cxdev->v4l2dev, + &dvbdev->i2c_adap, + "cx25840", 0x44, NULL); + if (!cxdev->cx25840) { + dev_err(&dvbdev->udev->dev, "cx25840 not found\n"); + return -ENODEV; + } + + /* + * Initialize cx25840 chip by calling its subdevice init core op. + * + * This switches it into the generic mode that disables some of + * ivtv-related hacks in the cx25840 driver while allowing setting + * of the chip video output configuration (passed in the call below + * as the last argument). + */ + ret = v4l2_subdev_call(cxdev->cx25840, core, init, + CX25840_VCONFIG_FMT_BT656 | + CX25840_VCONFIG_RES_8BIT | + CX25840_VCONFIG_VBIRAW_DISABLED | + CX25840_VCONFIG_ANCDATA_DISABLED | + CX25840_VCONFIG_ACTIVE_COMPOSITE | + CX25840_VCONFIG_VALID_ANDACTIVE | + CX25840_VCONFIG_HRESETW_NORMAL | + CX25840_VCONFIG_CLKGATE_NONE | + CX25840_VCONFIG_DCMODE_DWORDS); + if (ret != 0) { + dev_err(&dvbdev->udev->dev, + "cx25840 init failed (%d)\n", ret); + return ret; + } + + /* attach analog tuner */ + cxdev->tuner = v4l2_i2c_new_subdev(&cxdev->v4l2dev, + &dvbdev->i2c_adap, + "tuner", 0x61, NULL); + if (!cxdev->tuner) { + dev_err(&dvbdev->udev->dev, "tuner not found\n"); + return -ENODEV; + } + + /* configure it */ + memset(&tun_setup, 0, sizeof(tun_setup)); + tun_setup.addr = 0x61; + tun_setup.type = TUNER_PHILIPS_FMD1216ME_MK3; + tun_setup.mode_mask = T_RADIO | T_ANALOG_TV; + v4l2_subdev_call(cxdev->tuner, tuner, s_type_addr, &tun_setup); + + /* attach IF demod */ + cxdev->tda9887 = v4l2_i2c_new_subdev(&cxdev->v4l2dev, + &dvbdev->i2c_adap, + "tuner", 0x43, NULL); + if (!cxdev->tda9887) { + dev_err(&dvbdev->udev->dev, "tda9887 not found\n"); + return -ENODEV; + } + + return 0; +} + +int cxusb_medion_register_analog(struct dvb_usb_device *dvbdev) +{ + struct cxusb_medion_dev *cxdev = dvbdev->priv; + int ret; + + mutex_init(&cxdev->dev_lock); + + init_completion(&cxdev->v4l2_release); + + cxdev->v4l2dev.release = cxusb_medion_v4l2_release; + + ret = v4l2_device_register(&dvbdev->udev->dev, &cxdev->v4l2dev); + if (ret != 0) { + dev_err(&dvbdev->udev->dev, + "V4L2 device registration failed, ret = %d\n", ret); + mutex_destroy(&cxdev->dev_lock); + return ret; + } + + ret = cxusb_medion_register_analog_subdevs(dvbdev); + if (ret) + goto ret_unregister; + + INIT_WORK(&cxdev->urbwork, cxusb_medion_v_complete_work); + INIT_LIST_HEAD(&cxdev->buflist); + + cxdev->width = 320; + cxdev->height = 240; + + ret = cxusb_medion_register_analog_video(dvbdev); + if (ret) + goto ret_unregister; + + ret = cxusb_medion_register_analog_radio(dvbdev); + if (ret) + goto ret_vunreg; + + return 0; + +ret_vunreg: + video_unregister_device(cxdev->videodev); + +ret_unregister: + v4l2_device_put(&cxdev->v4l2dev); + wait_for_completion(&cxdev->v4l2_release); + + return ret; +} + +void cxusb_medion_unregister_analog(struct dvb_usb_device *dvbdev) +{ + struct cxusb_medion_dev *cxdev = dvbdev->priv; + + cxusb_vprintk(dvbdev, OPS, "unregistering analog\n"); + + video_unregister_device(cxdev->radiodev); + video_unregister_device(cxdev->videodev); + + v4l2_device_put(&cxdev->v4l2dev); + wait_for_completion(&cxdev->v4l2_release); + + cxusb_vprintk(dvbdev, OPS, "analog unregistered\n"); +} diff --git a/drivers/media/usb/dvb-usb/cxusb.c b/drivers/media/usb/dvb-usb/cxusb.c index 8039ba4ebf68..bac0778f7def 100644 --- a/drivers/media/usb/dvb-usb/cxusb.c +++ b/drivers/media/usb/dvb-usb/cxusb.c @@ -12,18 +12,21 @@ * design, so it can be reused for the "analogue-only" device (if it will * appear at all). * - * TODO: Use the cx25840-driver for the analogue part * * Copyright (C) 2005 Patrick Boettcher (patrick.boettcher@posteo.de) * Copyright (C) 2006 Michael Krufky (mkrufky@linuxtv.org) * Copyright (C) 2006, 2007 Chris Pascoe (c.pascoe@itee.uq.edu.au) + * Copyright (C) 2011, 2017 Maciej S. Szmigiero (mail@maciej.szmigiero.name) * * see Documentation/media/dvb-drivers/dvb-usb.rst for more information */ #include <media/tuner.h> -#include <linux/vmalloc.h> -#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/device.h> #include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/vmalloc.h> #include "cxusb.h" @@ -44,17 +47,45 @@ #include "si2157.h" /* debug */ -static int dvb_usb_cxusb_debug; +int dvb_usb_cxusb_debug; module_param_named(debug, dvb_usb_cxusb_debug, int, 0644); -MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS); +MODULE_PARM_DESC(debug, "set debugging level (see cxusb.h)." + DVB_USB_DEBUG_STATUS); DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); -#define deb_info(args...) dprintk(dvb_usb_cxusb_debug, 0x03, args) -#define deb_i2c(args...) dprintk(dvb_usb_cxusb_debug, 0x02, args) +#define deb_info(args...) dprintk(dvb_usb_cxusb_debug, CXUSB_DBG_MISC, args) +#define deb_i2c(args...) dprintk(dvb_usb_cxusb_debug, CXUSB_DBG_I2C, args) -static int cxusb_ctrl_msg(struct dvb_usb_device *d, - u8 cmd, const u8 *wbuf, int wlen, u8 *rbuf, int rlen) +enum cxusb_table_index { + MEDION_MD95700, + DVICO_BLUEBIRD_LG064F_COLD, + DVICO_BLUEBIRD_LG064F_WARM, + DVICO_BLUEBIRD_DUAL_1_COLD, + DVICO_BLUEBIRD_DUAL_1_WARM, + DVICO_BLUEBIRD_LGZ201_COLD, + DVICO_BLUEBIRD_LGZ201_WARM, + DVICO_BLUEBIRD_TH7579_COLD, + DVICO_BLUEBIRD_TH7579_WARM, + DIGITALNOW_BLUEBIRD_DUAL_1_COLD, + DIGITALNOW_BLUEBIRD_DUAL_1_WARM, + DVICO_BLUEBIRD_DUAL_2_COLD, + DVICO_BLUEBIRD_DUAL_2_WARM, + DVICO_BLUEBIRD_DUAL_4, + DVICO_BLUEBIRD_DVB_T_NANO_2, + DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM, + AVERMEDIA_VOLAR_A868R, + DVICO_BLUEBIRD_DUAL_4_REV_2, + CONEXANT_D680_DMB, + MYGICA_D689, + MYGICA_T230, + NR__cxusb_table_index +}; + +static struct usb_device_id cxusb_table[]; + +int cxusb_ctrl_msg(struct dvb_usb_device *d, + u8 cmd, const u8 *wbuf, int wlen, u8 *rbuf, int rlen) { struct cxusb_state *st = d->priv; int ret; @@ -86,7 +117,8 @@ static void cxusb_gpio_tuner(struct dvb_usb_device *d, int onoff) struct cxusb_state *st = d->priv; u8 o[2], i; - if (st->gpio_write_state[GPIO_TUNER] == onoff) + if (st->gpio_write_state[GPIO_TUNER] == onoff && + !st->gpio_write_refresh[GPIO_TUNER]) return; o[0] = GPIO_TUNER; @@ -97,10 +129,11 @@ static void cxusb_gpio_tuner(struct dvb_usb_device *d, int onoff) deb_info("gpio_write failed.\n"); st->gpio_write_state[GPIO_TUNER] = onoff; + st->gpio_write_refresh[GPIO_TUNER] = false; } static int cxusb_bluebird_gpio_rw(struct dvb_usb_device *d, u8 changemask, - u8 newval) + u8 newval) { u8 o[2], gpio_state; int rc; @@ -128,7 +161,7 @@ static void cxusb_nano2_led(struct dvb_usb_device *d, int onoff) } static int cxusb_d680_dmb_gpio_tuner(struct dvb_usb_device *d, - u8 addr, int onoff) + u8 addr, int onoff) { u8 o[2] = {addr, onoff}; u8 i; @@ -138,12 +171,12 @@ static int cxusb_d680_dmb_gpio_tuner(struct dvb_usb_device *d, if (rc < 0) return rc; + if (i == 0x01) return 0; - else { - deb_info("gpio_write failed.\n"); - return -EIO; - } + + deb_info("gpio_write failed.\n"); + return -EIO; } /* I2C */ @@ -158,7 +191,6 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], return -EAGAIN; for (i = 0; i < num; i++) { - if (le16_to_cpu(d->udev->descriptor.idVendor) == USB_VID_MEDION) switch (msg[i].addr) { case 0x63: @@ -184,13 +216,13 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], obuf[2] = msg[i].addr; if (cxusb_ctrl_msg(d, CMD_I2C_READ, obuf, 3, - ibuf, 1+msg[i].len) < 0) { + ibuf, 1 + msg[i].len) < 0) { warn("i2c read failed"); break; } memcpy(msg[i].buf, &ibuf[1], msg[i].len); - } else if (i+1 < num && (msg[i+1].flags & I2C_M_RD) && - msg[i].addr == msg[i+1].addr) { + } else if (i + 1 < num && (msg[i + 1].flags & I2C_M_RD) && + msg[i].addr == msg[i + 1].addr) { /* write to then read from same address */ u8 obuf[MAX_XFER_SIZE], ibuf[MAX_XFER_SIZE]; @@ -207,19 +239,19 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], goto unlock; } obuf[0] = msg[i].len; - obuf[1] = msg[i+1].len; + obuf[1] = msg[i + 1].len; obuf[2] = msg[i].addr; memcpy(&obuf[3], msg[i].buf, msg[i].len); if (cxusb_ctrl_msg(d, CMD_I2C_READ, - obuf, 3+msg[i].len, - ibuf, 1+msg[i+1].len) < 0) + obuf, 3 + msg[i].len, + ibuf, 1 + msg[i + 1].len) < 0) break; if (ibuf[0] != 0x08) deb_i2c("i2c read may have failed\n"); - memcpy(msg[i+1].buf, &ibuf[1], msg[i+1].len); + memcpy(msg[i + 1].buf, &ibuf[1], msg[i + 1].len); i++; } else { @@ -237,7 +269,7 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], memcpy(&obuf[2], msg[i].buf, msg[i].len); if (cxusb_ctrl_msg(d, CMD_I2C_WRITE, obuf, - 2+msg[i].len, &ibuf,1) < 0) + 2 + msg[i].len, &ibuf, 1) < 0) break; if (ibuf != 0x08) deb_i2c("i2c write may have failed\n"); @@ -256,7 +288,7 @@ unlock: static u32 cxusb_i2c_func(struct i2c_adapter *adapter) { - return I2C_FUNC_I2C; + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; } static struct i2c_algorithm cxusb_i2c_algo = { @@ -264,29 +296,67 @@ static struct i2c_algorithm cxusb_i2c_algo = { .functionality = cxusb_i2c_func, }; -static int cxusb_power_ctrl(struct dvb_usb_device *d, int onoff) +static int _cxusb_power_ctrl(struct dvb_usb_device *d, int onoff) { u8 b = 0; + + deb_info("setting power %s\n", onoff ? "ON" : "OFF"); + if (onoff) return cxusb_ctrl_msg(d, CMD_POWER_ON, &b, 1, NULL, 0); else return cxusb_ctrl_msg(d, CMD_POWER_OFF, &b, 1, NULL, 0); } +static int cxusb_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + bool is_medion = d->props.devices[0].warm_ids[0] == &cxusb_table[MEDION_MD95700]; + int ret; + + if (is_medion && !onoff) { + struct cxusb_medion_dev *cxdev = d->priv; + + mutex_lock(&cxdev->open_lock); + + if (cxdev->open_type == CXUSB_OPEN_ANALOG) { + deb_info("preventing DVB core from setting power OFF while we are in analog mode\n"); + ret = -EBUSY; + goto ret_unlock; + } + } + + ret = _cxusb_power_ctrl(d, onoff); + +ret_unlock: + if (is_medion && !onoff) { + struct cxusb_medion_dev *cxdev = d->priv; + + mutex_unlock(&cxdev->open_lock); + } + + return ret; +} + static int cxusb_aver_power_ctrl(struct dvb_usb_device *d, int onoff) { int ret; + if (!onoff) return cxusb_ctrl_msg(d, CMD_POWER_OFF, NULL, 0, NULL, 0); if (d->state == DVB_USB_STATE_INIT && usb_set_interface(d->udev, 0, 0) < 0) err("set interface failed"); - do {} while (!(ret = cxusb_ctrl_msg(d, CMD_POWER_ON, NULL, 0, NULL, 0)) && - !(ret = cxusb_ctrl_msg(d, 0x15, NULL, 0, NULL, 0)) && - !(ret = cxusb_ctrl_msg(d, 0x17, NULL, 0, NULL, 0)) && 0); + do { + /* Nothing */ + } while (!(ret = cxusb_ctrl_msg(d, CMD_POWER_ON, NULL, 0, NULL, 0)) && + !(ret = cxusb_ctrl_msg(d, 0x15, NULL, 0, NULL, 0)) && + !(ret = cxusb_ctrl_msg(d, 0x17, NULL, 0, NULL, 0)) && 0); + if (!ret) { - /* FIXME: We don't know why, but we need to configure the - * lgdt3303 with the register settings below on resume */ + /* + * FIXME: We don't know why, but we need to configure the + * lgdt3303 with the register settings below on resume + */ int i; u8 buf; static const u8 bufs[] = { @@ -304,7 +374,7 @@ static int cxusb_aver_power_ctrl(struct dvb_usb_device *d, int onoff) msleep(20); for (i = 0; i < ARRAY_SIZE(bufs); i += 4 / sizeof(u8)) { ret = cxusb_ctrl_msg(d, CMD_I2C_WRITE, - bufs+i, 4, &buf, 1); + bufs + i, 4, &buf, 1); if (ret) break; if (buf != 0x8) @@ -317,6 +387,7 @@ static int cxusb_aver_power_ctrl(struct dvb_usb_device *d, int onoff) static int cxusb_bluebird_power_ctrl(struct dvb_usb_device *d, int onoff) { u8 b = 0; + if (onoff) return cxusb_ctrl_msg(d, CMD_POWER_ON, &b, 1, NULL, 0); else @@ -338,6 +409,7 @@ static int cxusb_d680_dmb_power_ctrl(struct dvb_usb_device *d, int onoff) { int ret; u8 b; + ret = cxusb_power_ctrl(d, onoff); if (!onoff) return ret; @@ -350,11 +422,26 @@ static int cxusb_d680_dmb_power_ctrl(struct dvb_usb_device *d, int onoff) static int cxusb_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) { + struct dvb_usb_device *dvbdev = adap->dev; + bool is_medion = dvbdev->props.devices[0].warm_ids[0] == + &cxusb_table[MEDION_MD95700]; u8 buf[2] = { 0x03, 0x00 }; + + if (is_medion && onoff) { + int ret; + + ret = cxusb_medion_get(dvbdev, CXUSB_OPEN_DIGITAL); + if (ret != 0) + return ret; + } + if (onoff) - cxusb_ctrl_msg(adap->dev, CMD_STREAMING_ON, buf, 2, NULL, 0); + cxusb_ctrl_msg(dvbdev, CMD_STREAMING_ON, buf, 2, NULL, 0); else - cxusb_ctrl_msg(adap->dev, CMD_STREAMING_OFF, NULL, 0, NULL, 0); + cxusb_ctrl_msg(dvbdev, CMD_STREAMING_OFF, NULL, 0, NULL, 0); + + if (is_medion && !onoff) + cxusb_medion_put(dvbdev); return 0; } @@ -370,7 +457,7 @@ static int cxusb_aver_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) } static int cxusb_read_status(struct dvb_frontend *fe, - enum fe_status *status) + enum fe_status *status) { struct dvb_usb_adapter *adap = (struct dvb_usb_adapter *)fe->dvb->priv; struct cxusb_state *state = (struct cxusb_state *)adap->dev->priv; @@ -403,8 +490,8 @@ static void cxusb_d680_dmb_drain_message(struct dvb_usb_device *d) return; while (1) { if (usb_bulk_msg(d->udev, - usb_rcvbulkpipe(d->udev, ep), - junk, junk_len, &rd_count, timeout) < 0) + usb_rcvbulkpipe(d->udev, ep), + junk, junk_len, &rd_count, timeout) < 0) break; if (!rd_count) break; @@ -426,8 +513,8 @@ static void cxusb_d680_dmb_drain_video(struct dvb_usb_device *d) return; while (1) { if (usb_bulk_msg(d->udev, - usb_rcvbulkpipe(d->udev, p->endpoint), - junk, junk_len, &rd_count, timeout) < 0) + usb_rcvbulkpipe(d->udev, p->endpoint), + junk, junk_len, &rd_count, timeout) < 0) break; if (!rd_count) break; @@ -435,17 +522,18 @@ static void cxusb_d680_dmb_drain_video(struct dvb_usb_device *d) kfree(junk); } -static int cxusb_d680_dmb_streaming_ctrl( - struct dvb_usb_adapter *adap, int onoff) +static int cxusb_d680_dmb_streaming_ctrl(struct dvb_usb_adapter *adap, + int onoff) { if (onoff) { u8 buf[2] = { 0x03, 0x00 }; + cxusb_d680_dmb_drain_video(adap->dev); return cxusb_ctrl_msg(adap->dev, CMD_STREAMING_ON, - buf, sizeof(buf), NULL, 0); + buf, sizeof(buf), NULL, 0); } else { int ret = cxusb_ctrl_msg(adap->dev, - CMD_STREAMING_OFF, NULL, 0, NULL, 0); + CMD_STREAMING_OFF, NULL, 0, NULL, 0); return ret; } } @@ -465,8 +553,12 @@ static int cxusb_rc_query(struct dvb_usb_device *d) static int cxusb_bluebird2_rc_query(struct dvb_usb_device *d) { u8 ircode[4]; - struct i2c_msg msg = { .addr = 0x6b, .flags = I2C_M_RD, - .buf = ircode, .len = 4 }; + struct i2c_msg msg = { + .addr = 0x6b, + .flags = I2C_M_RD, + .buf = ircode, + .len = 4 + }; if (cxusb_i2c_xfer(&d->i2c_adap, &msg, 1) != 1) return 0; @@ -490,13 +582,13 @@ static int cxusb_d680_dmb_rc_query(struct dvb_usb_device *d) return 0; } -static int cxusb_dee1601_demod_init(struct dvb_frontend* fe) +static int cxusb_dee1601_demod_init(struct dvb_frontend *fe) { - static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x28 }; - static u8 reset [] = { RESET, 0x80 }; - static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 }; - static u8 agc_cfg [] = { AGC_TARGET, 0x28, 0x20 }; - static u8 gpp_ctl_cfg [] = { GPP_CTL, 0x33 }; + static u8 clock_config[] = { CLOCK_CTL, 0x38, 0x28 }; + static u8 reset[] = { RESET, 0x80 }; + static u8 adc_ctl_1_cfg[] = { ADC_CTL_1, 0x40 }; + static u8 agc_cfg[] = { AGC_TARGET, 0x28, 0x20 }; + static u8 gpp_ctl_cfg[] = { GPP_CTL, 0x33 }; static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 }; mt352_write(fe, clock_config, sizeof(clock_config)); @@ -511,13 +603,14 @@ static int cxusb_dee1601_demod_init(struct dvb_frontend* fe) return 0; } -static int cxusb_mt352_demod_init(struct dvb_frontend* fe) -{ /* used in both lgz201 and th7579 */ - static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x29 }; - static u8 reset [] = { RESET, 0x80 }; - static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 }; - static u8 agc_cfg [] = { AGC_TARGET, 0x24, 0x20 }; - static u8 gpp_ctl_cfg [] = { GPP_CTL, 0x33 }; +static int cxusb_mt352_demod_init(struct dvb_frontend *fe) +{ + /* used in both lgz201 and th7579 */ + static u8 clock_config[] = { CLOCK_CTL, 0x38, 0x29 }; + static u8 reset[] = { RESET, 0x80 }; + static u8 adc_ctl_1_cfg[] = { ADC_CTL_1, 0x40 }; + static u8 agc_cfg[] = { AGC_TARGET, 0x24, 0x20 }; + static u8 gpp_ctl_cfg[] = { GPP_CTL, 0x33 }; static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 }; mt352_write(fe, clock_config, sizeof(clock_config)); @@ -627,9 +720,21 @@ static struct max2165_config mygica_d689_max2165_cfg = { /* Callbacks for DVB USB */ static int cxusb_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap) { + struct dvb_usb_device *dvbdev = adap->dev; + bool is_medion = dvbdev->props.devices[0].warm_ids[0] == + &cxusb_table[MEDION_MD95700]; + dvb_attach(simple_tuner_attach, adap->fe_adap[0].fe, - &adap->dev->i2c_adap, 0x61, + &dvbdev->i2c_adap, 0x61, TUNER_PHILIPS_FMD1216ME_MK3); + + if (is_medion && adap->fe_adap[0].fe) + /* + * make sure that DVB core won't put to sleep (reset, really) + * tuner when we might be open in analog mode + */ + adap->fe_adap[0].fe->ops.tuner_ops.sleep = NULL; + return 0; } @@ -642,7 +747,8 @@ static int cxusb_dee1601_tuner_attach(struct dvb_usb_adapter *adap) static int cxusb_lgz201_tuner_attach(struct dvb_usb_adapter *adap) { - dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x61, NULL, DVB_PLL_LG_Z201); + dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x61, + NULL, DVB_PLL_LG_Z201); return 0; } @@ -702,7 +808,7 @@ static int cxusb_dvico_xc3028_tuner_attach(struct dvb_usb_adapter *adap) adap->fe_adap[0].fe->callback = dvico_bluebird_xc2028_callback; fe = dvb_attach(xc2028_attach, adap->fe_adap[0].fe, &cfg); - if (fe == NULL || fe->ops.tuner_ops.set_config == NULL) + if (!fe || !fe->ops.tuner_ops.set_config) return -EIO; fe->ops.tuner_ops.set_config(fe, &ctl); @@ -720,33 +826,120 @@ static int cxusb_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap) static int cxusb_d680_dmb_tuner_attach(struct dvb_usb_adapter *adap) { struct dvb_frontend *fe; + fe = dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, &d680_dmb_tuner); - return (fe == NULL) ? -EIO : 0; + return (!fe) ? -EIO : 0; } static int cxusb_mygica_d689_tuner_attach(struct dvb_usb_adapter *adap) { struct dvb_frontend *fe; + fe = dvb_attach(max2165_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, &mygica_d689_max2165_cfg); - return (fe == NULL) ? -EIO : 0; + return (!fe) ? -EIO : 0; } -static int cxusb_cx22702_frontend_attach(struct dvb_usb_adapter *adap) +static int cxusb_medion_fe_ts_bus_ctrl(struct dvb_frontend *fe, int acquire) { + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dvb_usb_device *dvbdev = adap->dev; + + if (acquire) + return cxusb_medion_get(dvbdev, CXUSB_OPEN_DIGITAL); + + cxusb_medion_put(dvbdev); + + return 0; +} + +static int cxusb_medion_set_mode(struct dvb_usb_device *dvbdev, bool digital) +{ + struct cxusb_state *st = dvbdev->priv; + int ret; u8 b; - if (usb_set_interface(adap->dev->udev, 0, 6) < 0) - err("set interface failed"); + unsigned int i; - cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, &b, 1); + /* + * switching mode while doing an I2C transaction often causes + * the device to crash + */ + mutex_lock(&dvbdev->i2c_mutex); + + if (digital) { + ret = usb_set_interface(dvbdev->udev, 0, 6); + if (ret != 0) { + dev_err(&dvbdev->udev->dev, + "digital interface selection failed (%d)\n", + ret); + goto ret_unlock; + } + } else { + ret = usb_set_interface(dvbdev->udev, 0, 1); + if (ret != 0) { + dev_err(&dvbdev->udev->dev, + "analog interface selection failed (%d)\n", + ret); + goto ret_unlock; + } + } + + /* pipes need to be cleared after setting interface */ + ret = usb_clear_halt(dvbdev->udev, usb_rcvbulkpipe(dvbdev->udev, 1)); + if (ret != 0) + dev_warn(&dvbdev->udev->dev, + "clear halt on IN pipe failed (%d)\n", + ret); + + ret = usb_clear_halt(dvbdev->udev, usb_sndbulkpipe(dvbdev->udev, 1)); + if (ret != 0) + dev_warn(&dvbdev->udev->dev, + "clear halt on OUT pipe failed (%d)\n", + ret); + + ret = cxusb_ctrl_msg(dvbdev, digital ? CMD_DIGITAL : CMD_ANALOG, + NULL, 0, &b, 1); + if (ret != 0) { + dev_err(&dvbdev->udev->dev, "mode switch failed (%d)\n", + ret); + goto ret_unlock; + } + + /* mode switch seems to reset GPIO states */ + for (i = 0; i < ARRAY_SIZE(st->gpio_write_refresh); i++) + st->gpio_write_refresh[i] = true; + +ret_unlock: + mutex_unlock(&dvbdev->i2c_mutex); + + return ret; +} + +static int cxusb_cx22702_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct dvb_usb_device *dvbdev = adap->dev; + bool is_medion = dvbdev->props.devices[0].warm_ids[0] == + &cxusb_table[MEDION_MD95700]; + + if (is_medion) { + int ret; + + ret = cxusb_medion_set_mode(dvbdev, true); + if (ret) + return ret; + } adap->fe_adap[0].fe = dvb_attach(cx22702_attach, &cxusb_cx22702_config, - &adap->dev->i2c_adap); - if ((adap->fe_adap[0].fe) != NULL) - return 0; + &dvbdev->i2c_adap); + if (!adap->fe_adap[0].fe) + return -EIO; - return -EIO; + if (is_medion) + adap->fe_adap[0].fe->ops.ts_bus_ctrl = + cxusb_medion_fe_ts_bus_ctrl; + + return 0; } static int cxusb_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap) @@ -760,7 +953,7 @@ static int cxusb_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap) &cxusb_lgdt3303_config, 0x0e, &adap->dev->i2c_adap); - if ((adap->fe_adap[0].fe) != NULL) + if (adap->fe_adap[0].fe) return 0; return -EIO; @@ -772,7 +965,7 @@ static int cxusb_aver_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap) &cxusb_aver_lgdt3303_config, 0x0e, &adap->dev->i2c_adap); - if (adap->fe_adap[0].fe != NULL) + if (adap->fe_adap[0].fe) return 0; return -EIO; @@ -788,7 +981,7 @@ static int cxusb_mt352_frontend_attach(struct dvb_usb_adapter *adap) adap->fe_adap[0].fe = dvb_attach(mt352_attach, &cxusb_mt352_config, &adap->dev->i2c_adap); - if ((adap->fe_adap[0].fe) != NULL) + if (adap->fe_adap[0].fe) return 0; return -EIO; @@ -803,13 +996,13 @@ static int cxusb_dee1601_frontend_attach(struct dvb_usb_adapter *adap) adap->fe_adap[0].fe = dvb_attach(mt352_attach, &cxusb_dee1601_config, &adap->dev->i2c_adap); - if ((adap->fe_adap[0].fe) != NULL) + if (adap->fe_adap[0].fe) return 0; adap->fe_adap[0].fe = dvb_attach(zl10353_attach, &cxusb_zl10353_dee1601_config, &adap->dev->i2c_adap); - if ((adap->fe_adap[0].fe) != NULL) + if (adap->fe_adap[0].fe) return 0; return -EIO; @@ -819,8 +1012,12 @@ static int cxusb_dualdig4_frontend_attach(struct dvb_usb_adapter *adap) { u8 ircode[4]; int i; - struct i2c_msg msg = { .addr = 0x6b, .flags = I2C_M_RD, - .buf = ircode, .len = 4 }; + struct i2c_msg msg = { + .addr = 0x6b, + .flags = I2C_M_RD, + .buf = ircode, + .len = 4 + }; if (usb_set_interface(adap->dev->udev, 0, 1) < 0) err("set interface failed"); @@ -836,7 +1033,7 @@ static int cxusb_dualdig4_frontend_attach(struct dvb_usb_adapter *adap) dvb_attach(zl10353_attach, &cxusb_zl10353_xc3028_config_no_i2c_gate, &adap->dev->i2c_adap); - if ((adap->fe_adap[0].fe) == NULL) + if (!adap->fe_adap[0].fe) return -EIO; /* try to determine if there is no IR decoder on the I2C bus */ @@ -934,7 +1131,7 @@ static struct dib7000p_config cxusb_dualdig4_rev2_config = { }; struct dib0700_adapter_state { - int (*set_param_save)(struct dvb_frontend *); + int (*set_param_save)(struct dvb_frontend *fe); struct dib7000p_ops dib7000p_ops; }; @@ -953,14 +1150,15 @@ static int cxusb_dualdig4_rev2_frontend_attach(struct dvb_usb_adapter *adap) return -ENODEV; if (state->dib7000p_ops.i2c_enumeration(&adap->dev->i2c_adap, 1, 18, - &cxusb_dualdig4_rev2_config) < 0) { - printk(KERN_WARNING "Unable to enumerate dib7000p\n"); + &cxusb_dualdig4_rev2_config) < 0) { + pr_warn("Unable to enumerate dib7000p\n"); return -ENODEV; } - adap->fe_adap[0].fe = state->dib7000p_ops.init(&adap->dev->i2c_adap, 0x80, - &cxusb_dualdig4_rev2_config); - if (adap->fe_adap[0].fe == NULL) + adap->fe_adap[0].fe = state->dib7000p_ops.init(&adap->dev->i2c_adap, + 0x80, + &cxusb_dualdig4_rev2_config); + if (!adap->fe_adap[0].fe) return -EIO; return 0; @@ -993,11 +1191,16 @@ static int dib7070_set_param_override(struct dvb_frontend *fe) struct dib0700_adapter_state *state = adap->priv; u16 offset; - u8 band = BAND_OF_FREQUENCY(p->frequency/1000); + u8 band = BAND_OF_FREQUENCY(p->frequency / 1000); + switch (band) { - case BAND_VHF: offset = 950; break; + case BAND_VHF: + offset = 950; + break; default: - case BAND_UHF: offset = 550; break; + case BAND_UHF: + offset = 550; + break; } state->dib7000p_ops.set_wbd_ref(fe, offset + dib0070_wbd_offset(fe)); @@ -1019,7 +1222,7 @@ static int cxusb_dualdig4_rev2_tuner_attach(struct dvb_usb_adapter *adap) DIBX000_I2C_INTERFACE_TUNER, 1); if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c, - &dib7070p_dib0070_config) == NULL) + &dib7070p_dib0070_config) == NULL) return -ENODEV; st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; @@ -1042,13 +1245,13 @@ static int cxusb_nano2_frontend_attach(struct dvb_usb_adapter *adap) adap->fe_adap[0].fe = dvb_attach(zl10353_attach, &cxusb_zl10353_xc3028_config, &adap->dev->i2c_adap); - if ((adap->fe_adap[0].fe) != NULL) + if (adap->fe_adap[0].fe) return 0; adap->fe_adap[0].fe = dvb_attach(mt352_attach, &cxusb_mt352_xc3028_config, &adap->dev->i2c_adap); - if ((adap->fe_adap[0].fe) != NULL) + if (adap->fe_adap[0].fe) return 0; return -EIO; @@ -1079,11 +1282,14 @@ static int cxusb_d680_dmb_frontend_attach(struct dvb_usb_adapter *adap) /* Unblock all USB pipes */ usb_clear_halt(d->udev, - usb_sndbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); + usb_sndbulkpipe(d->udev, + d->props.generic_bulk_ctrl_endpoint)); usb_clear_halt(d->udev, - usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); + usb_rcvbulkpipe(d->udev, + d->props.generic_bulk_ctrl_endpoint)); usb_clear_halt(d->udev, - usb_rcvbulkpipe(d->udev, d->props.adapter[0].fe[0].stream.endpoint)); + usb_rcvbulkpipe(d->udev, + d->props.adapter[0].fe[0].stream.endpoint)); /* Drain USB pipes to avoid hang after reboot */ for (n = 0; n < 5; n++) { @@ -1105,8 +1311,9 @@ static int cxusb_d680_dmb_frontend_attach(struct dvb_usb_adapter *adap) msleep(100); /* Attach frontend */ - adap->fe_adap[0].fe = dvb_attach(lgs8gxx_attach, &d680_lgs8gl5_cfg, &d->i2c_adap); - if (adap->fe_adap[0].fe == NULL) + adap->fe_adap[0].fe = dvb_attach(lgs8gxx_attach, + &d680_lgs8gl5_cfg, &d->i2c_adap); + if (!adap->fe_adap[0].fe) return -EIO; return 0; @@ -1136,12 +1343,14 @@ static int cxusb_mygica_d689_frontend_attach(struct dvb_usb_adapter *adap) /* Unblock all USB pipes */ usb_clear_halt(d->udev, - usb_sndbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); + usb_sndbulkpipe(d->udev, + d->props.generic_bulk_ctrl_endpoint)); usb_clear_halt(d->udev, - usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); + usb_rcvbulkpipe(d->udev, + d->props.generic_bulk_ctrl_endpoint)); usb_clear_halt(d->udev, - usb_rcvbulkpipe(d->udev, d->props.adapter[0].fe[0].stream.endpoint)); - + usb_rcvbulkpipe(d->udev, + d->props.adapter[0].fe[0].stream.endpoint)); /* Reset the tuner */ if (cxusb_d680_dmb_gpio_tuner(d, 0x07, 0) < 0) { @@ -1156,9 +1365,10 @@ static int cxusb_mygica_d689_frontend_attach(struct dvb_usb_adapter *adap) msleep(100); /* Attach frontend */ - adap->fe_adap[0].fe = dvb_attach(atbm8830_attach, &mygica_d689_atbm8830_cfg, - &d->i2c_adap); - if (adap->fe_adap[0].fe == NULL) + adap->fe_adap[0].fe = dvb_attach(atbm8830_attach, + &mygica_d689_atbm8830_cfg, + &d->i2c_adap); + if (!adap->fe_adap[0].fe) return -EIO; return 0; @@ -1181,11 +1391,14 @@ static int cxusb_mygica_t230_frontend_attach(struct dvb_usb_adapter *adap) /* Unblock all USB pipes */ usb_clear_halt(d->udev, - usb_sndbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); + usb_sndbulkpipe(d->udev, + d->props.generic_bulk_ctrl_endpoint)); usb_clear_halt(d->udev, - usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); + usb_rcvbulkpipe(d->udev, + d->props.generic_bulk_ctrl_endpoint)); usb_clear_halt(d->udev, - usb_rcvbulkpipe(d->udev, d->props.adapter[0].fe[0].stream.endpoint)); + usb_rcvbulkpipe(d->udev, + d->props.adapter[0].fe[0].stream.endpoint)); /* attach frontend */ si2168_config.i2c_adapter = &adapter; @@ -1198,7 +1411,7 @@ static int cxusb_mygica_t230_frontend_attach(struct dvb_usb_adapter *adap) info.platform_data = &si2168_config; request_module(info.type); client_demod = i2c_new_device(&d->i2c_adap, &info); - if (client_demod == NULL || client_demod->dev.driver == NULL) + if (!client_demod || !client_demod->dev.driver) return -ENODEV; if (!try_module_get(client_demod->dev.driver->owner)) { @@ -1218,7 +1431,7 @@ static int cxusb_mygica_t230_frontend_attach(struct dvb_usb_adapter *adap) info.platform_data = &si2157_config; request_module(info.type); client_tuner = i2c_new_device(adapter, &info); - if (client_tuner == NULL || client_tuner->dev.driver == NULL) { + if (!client_tuner || !client_tuner->dev.driver) { module_put(client_demod->dev.driver->owner); i2c_unregister_device(client_demod); return -ENODEV; @@ -1309,6 +1522,104 @@ static int bluebird_patch_dvico_firmware_download(struct usb_device *udev, return -EINVAL; } +int cxusb_medion_get(struct dvb_usb_device *dvbdev, + enum cxusb_open_type open_type) +{ + struct cxusb_medion_dev *cxdev = dvbdev->priv; + int ret = 0; + + mutex_lock(&cxdev->open_lock); + + if (WARN_ON((cxdev->open_type == CXUSB_OPEN_INIT || + cxdev->open_type == CXUSB_OPEN_NONE) && + cxdev->open_ctr != 0)) { + ret = -EINVAL; + goto ret_unlock; + } + + if (cxdev->open_type == CXUSB_OPEN_INIT) { + ret = -EAGAIN; + goto ret_unlock; + } + + if (cxdev->open_ctr == 0) { + if (cxdev->open_type != open_type) { + deb_info("will acquire and switch to %s\n", + open_type == CXUSB_OPEN_ANALOG ? + "analog" : "digital"); + + if (open_type == CXUSB_OPEN_ANALOG) { + ret = _cxusb_power_ctrl(dvbdev, 1); + if (ret != 0) + dev_warn(&dvbdev->udev->dev, + "powerup for analog switch failed (%d)\n", + ret); + + ret = cxusb_medion_set_mode(dvbdev, false); + if (ret != 0) + goto ret_unlock; + + ret = cxusb_medion_analog_init(dvbdev); + if (ret != 0) + goto ret_unlock; + } else { /* digital */ + ret = _cxusb_power_ctrl(dvbdev, 1); + if (ret != 0) + dev_warn(&dvbdev->udev->dev, + "powerup for digital switch failed (%d)\n", + ret); + + ret = cxusb_medion_set_mode(dvbdev, true); + if (ret != 0) + goto ret_unlock; + } + + cxdev->open_type = open_type; + } else { + deb_info("reacquired idle %s\n", + open_type == CXUSB_OPEN_ANALOG ? + "analog" : "digital"); + } + + cxdev->open_ctr = 1; + } else if (cxdev->open_type == open_type) { + cxdev->open_ctr++; + deb_info("acquired %s\n", open_type == CXUSB_OPEN_ANALOG ? + "analog" : "digital"); + } else { + ret = -EBUSY; + } + +ret_unlock: + mutex_unlock(&cxdev->open_lock); + + return ret; +} + +void cxusb_medion_put(struct dvb_usb_device *dvbdev) +{ + struct cxusb_medion_dev *cxdev = dvbdev->priv; + + mutex_lock(&cxdev->open_lock); + + if (cxdev->open_type == CXUSB_OPEN_INIT) { + WARN_ON(cxdev->open_ctr != 0); + cxdev->open_type = CXUSB_OPEN_NONE; + goto unlock; + } + + if (!WARN_ON(cxdev->open_ctr < 1)) { + cxdev->open_ctr--; + + deb_info("release %s\n", + cxdev->open_type == CXUSB_OPEN_ANALOG ? + "analog" : "digital"); + } + +unlock: + mutex_unlock(&cxdev->open_lock); +} + /* DVB USB Driver stuff */ static struct dvb_usb_device_properties cxusb_medion_properties; static struct dvb_usb_device_properties cxusb_bluebird_lgh064f_properties; @@ -1324,41 +1635,141 @@ static struct dvb_usb_device_properties cxusb_d680_dmb_properties; static struct dvb_usb_device_properties cxusb_mygica_d689_properties; static struct dvb_usb_device_properties cxusb_mygica_t230_properties; +static int cxusb_medion_priv_init(struct dvb_usb_device *dvbdev) +{ + struct cxusb_medion_dev *cxdev = dvbdev->priv; + + cxdev->dvbdev = dvbdev; + cxdev->open_type = CXUSB_OPEN_INIT; + mutex_init(&cxdev->open_lock); + + return 0; +} + +static void cxusb_medion_priv_destroy(struct dvb_usb_device *dvbdev) +{ + struct cxusb_medion_dev *cxdev = dvbdev->priv; + + mutex_destroy(&cxdev->open_lock); +} + +static bool cxusb_medion_check_altsetting(struct usb_host_interface *as) +{ + unsigned int ctr; + + for (ctr = 0; ctr < as->desc.bNumEndpoints; ctr++) { + if ((as->endpoint[ctr].desc.bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK) != 2) + continue; + + if (as->endpoint[ctr].desc.bEndpointAddress & USB_DIR_IN && + ((as->endpoint[ctr].desc.bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_ISOC)) + return true; + + break; + } + + return false; +} + +static bool cxusb_medion_check_intf(struct usb_interface *intf) +{ + unsigned int ctr; + + if (intf->num_altsetting < 2) { + dev_err(intf->usb_dev, "no alternate interface"); + + return false; + } + + for (ctr = 0; ctr < intf->num_altsetting; ctr++) { + if (intf->altsetting[ctr].desc.bAlternateSetting != 1) + continue; + + if (cxusb_medion_check_altsetting(&intf->altsetting[ctr])) + return true; + + break; + } + + dev_err(intf->usb_dev, "no iso interface"); + + return false; +} + static int cxusb_probe(struct usb_interface *intf, const struct usb_device_id *id) { - if (0 == dvb_usb_device_init(intf, &cxusb_medion_properties, - THIS_MODULE, NULL, adapter_nr) || - 0 == dvb_usb_device_init(intf, &cxusb_bluebird_lgh064f_properties, - THIS_MODULE, NULL, adapter_nr) || - 0 == dvb_usb_device_init(intf, &cxusb_bluebird_dee1601_properties, - THIS_MODULE, NULL, adapter_nr) || - 0 == dvb_usb_device_init(intf, &cxusb_bluebird_lgz201_properties, - THIS_MODULE, NULL, adapter_nr) || - 0 == dvb_usb_device_init(intf, &cxusb_bluebird_dtt7579_properties, - THIS_MODULE, NULL, adapter_nr) || - 0 == dvb_usb_device_init(intf, &cxusb_bluebird_dualdig4_properties, - THIS_MODULE, NULL, adapter_nr) || - 0 == dvb_usb_device_init(intf, &cxusb_bluebird_nano2_properties, - THIS_MODULE, NULL, adapter_nr) || - 0 == dvb_usb_device_init(intf, - &cxusb_bluebird_nano2_needsfirmware_properties, - THIS_MODULE, NULL, adapter_nr) || - 0 == dvb_usb_device_init(intf, &cxusb_aver_a868r_properties, - THIS_MODULE, NULL, adapter_nr) || - 0 == dvb_usb_device_init(intf, - &cxusb_bluebird_dualdig4_rev2_properties, - THIS_MODULE, NULL, adapter_nr) || - 0 == dvb_usb_device_init(intf, &cxusb_d680_dmb_properties, - THIS_MODULE, NULL, adapter_nr) || - 0 == dvb_usb_device_init(intf, &cxusb_mygica_d689_properties, - THIS_MODULE, NULL, adapter_nr) || - 0 == dvb_usb_device_init(intf, &cxusb_mygica_t230_properties, - THIS_MODULE, NULL, adapter_nr) || - 0) + struct dvb_usb_device *dvbdev; + int ret; + + /* Medion 95700 */ + if (!dvb_usb_device_init(intf, &cxusb_medion_properties, + THIS_MODULE, &dvbdev, adapter_nr)) { + if (!cxusb_medion_check_intf(intf)) { + ret = -ENODEV; + goto ret_uninit; + } + + _cxusb_power_ctrl(dvbdev, 1); + ret = cxusb_medion_set_mode(dvbdev, false); + if (ret) + goto ret_uninit; + + ret = cxusb_medion_register_analog(dvbdev); + + cxusb_medion_set_mode(dvbdev, true); + _cxusb_power_ctrl(dvbdev, 0); + + if (ret != 0) + goto ret_uninit; + + /* release device from INIT mode to normal operation */ + cxusb_medion_put(dvbdev); + + return 0; + } else if (!dvb_usb_device_init(intf, + &cxusb_bluebird_lgh064f_properties, + THIS_MODULE, NULL, adapter_nr) || + !dvb_usb_device_init(intf, + &cxusb_bluebird_dee1601_properties, + THIS_MODULE, NULL, adapter_nr) || + !dvb_usb_device_init(intf, + &cxusb_bluebird_lgz201_properties, + THIS_MODULE, NULL, adapter_nr) || + !dvb_usb_device_init(intf, + &cxusb_bluebird_dtt7579_properties, + THIS_MODULE, NULL, adapter_nr) || + !dvb_usb_device_init(intf, + &cxusb_bluebird_dualdig4_properties, + THIS_MODULE, NULL, adapter_nr) || + !dvb_usb_device_init(intf, + &cxusb_bluebird_nano2_properties, + THIS_MODULE, NULL, adapter_nr) || + !dvb_usb_device_init(intf, + &cxusb_bluebird_nano2_needsfirmware_properties, + THIS_MODULE, NULL, adapter_nr) || + !dvb_usb_device_init(intf, &cxusb_aver_a868r_properties, + THIS_MODULE, NULL, adapter_nr) || + !dvb_usb_device_init(intf, + &cxusb_bluebird_dualdig4_rev2_properties, + THIS_MODULE, NULL, adapter_nr) || + !dvb_usb_device_init(intf, &cxusb_d680_dmb_properties, + THIS_MODULE, NULL, adapter_nr) || + !dvb_usb_device_init(intf, &cxusb_mygica_d689_properties, + THIS_MODULE, NULL, adapter_nr) || + !dvb_usb_device_init(intf, &cxusb_mygica_t230_properties, + THIS_MODULE, NULL, adapter_nr) || + 0) return 0; return -EINVAL; + +ret_uninit: + dvb_usb_device_exit(intf); + + return ret; } static void cxusb_disconnect(struct usb_interface *intf) @@ -1367,6 +1778,9 @@ static void cxusb_disconnect(struct usb_interface *intf) struct cxusb_state *st = d->priv; struct i2c_client *client; + if (d->props.devices[0].warm_ids[0] == &cxusb_table[MEDION_MD95700]) + cxusb_medion_unregister_analog(d); + /* remove I2C client for tuner */ client = st->i2c_client_tuner; if (client) { @@ -1384,31 +1798,6 @@ static void cxusb_disconnect(struct usb_interface *intf) dvb_usb_device_exit(intf); } -enum cxusb_table_index { - MEDION_MD95700, - DVICO_BLUEBIRD_LG064F_COLD, - DVICO_BLUEBIRD_LG064F_WARM, - DVICO_BLUEBIRD_DUAL_1_COLD, - DVICO_BLUEBIRD_DUAL_1_WARM, - DVICO_BLUEBIRD_LGZ201_COLD, - DVICO_BLUEBIRD_LGZ201_WARM, - DVICO_BLUEBIRD_TH7579_COLD, - DVICO_BLUEBIRD_TH7579_WARM, - DIGITALNOW_BLUEBIRD_DUAL_1_COLD, - DIGITALNOW_BLUEBIRD_DUAL_1_WARM, - DVICO_BLUEBIRD_DUAL_2_COLD, - DVICO_BLUEBIRD_DUAL_2_WARM, - DVICO_BLUEBIRD_DUAL_4, - DVICO_BLUEBIRD_DVB_T_NANO_2, - DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM, - AVERMEDIA_VOLAR_A868R, - DVICO_BLUEBIRD_DUAL_4_REV_2, - CONEXANT_D680_DMB, - MYGICA_D689, - MYGICA_T230, - NR__cxusb_table_index -}; - static struct usb_device_id cxusb_table[NR__cxusb_table_index + 1] = { [MEDION_MD95700] = { USB_DEVICE(USB_VID_MEDION, USB_PID_MEDION_MD95700) @@ -1438,10 +1827,12 @@ static struct usb_device_id cxusb_table[NR__cxusb_table_index + 1] = { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_TH7579_WARM) }, [DIGITALNOW_BLUEBIRD_DUAL_1_COLD] = { - USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD) + USB_DEVICE(USB_VID_DVICO, + USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD) }, [DIGITALNOW_BLUEBIRD_DUAL_1_WARM] = { - USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM) + USB_DEVICE(USB_VID_DVICO, + USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM) }, [DVICO_BLUEBIRD_DUAL_2_COLD] = { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD) @@ -1456,7 +1847,8 @@ static struct usb_device_id cxusb_table[NR__cxusb_table_index + 1] = { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2) }, [DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM] = { - USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM) + USB_DEVICE(USB_VID_DVICO, + USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM) }, [AVERMEDIA_VOLAR_A868R] = { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_A868R) @@ -1475,14 +1867,16 @@ static struct usb_device_id cxusb_table[NR__cxusb_table_index + 1] = { }, {} /* Terminating entry */ }; -MODULE_DEVICE_TABLE (usb, cxusb_table); +MODULE_DEVICE_TABLE(usb, cxusb_table); static struct dvb_usb_device_properties cxusb_medion_properties = { .caps = DVB_USB_IS_AN_I2C_ADAPTER, .usb_ctrl = CYPRESS_FX2, - .size_of_priv = sizeof(struct cxusb_state), + .size_of_priv = sizeof(struct cxusb_medion_dev), + .priv_init = cxusb_medion_priv_init, + .priv_destroy = cxusb_medion_priv_destroy, .num_adapters = 1, .adapter = { @@ -1503,7 +1897,7 @@ static struct dvb_usb_device_properties cxusb_medion_properties = { } } }, - }}, + } }, }, }, .power_ctrl = cxusb_power_ctrl, @@ -1514,7 +1908,8 @@ static struct dvb_usb_device_properties cxusb_medion_properties = { .num_device_descs = 1, .devices = { - { "Medion MD95700 (MDUSBTV-HYBRID)", + { + "Medion MD95700 (MDUSBTV-HYBRID)", { NULL }, { &cxusb_table[MEDION_MD95700], NULL }, }, @@ -1527,8 +1922,10 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgh064f_properties = { .usb_ctrl = DEVICE_SPECIFIC, .firmware = "dvb-usb-bluebird-01.fw", .download_firmware = bluebird_patch_dvico_firmware_download, - /* use usb alt setting 0 for EP4 transfer (dvb-t), - use usb alt setting 7 for EP2 transfer (atsc) */ + /* + * use usb alt setting 0 for EP4 transfer (dvb-t), + * use usb alt setting 7 for EP2 transfer (atsc) + */ .size_of_priv = sizeof(struct cxusb_state), @@ -1552,7 +1949,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgh064f_properties = { } } }, - }}, + } }, }, }, @@ -1585,8 +1982,10 @@ static struct dvb_usb_device_properties cxusb_bluebird_dee1601_properties = { .usb_ctrl = DEVICE_SPECIFIC, .firmware = "dvb-usb-bluebird-01.fw", .download_firmware = bluebird_patch_dvico_firmware_download, - /* use usb alt setting 0 for EP4 transfer (dvb-t), - use usb alt setting 7 for EP2 transfer (atsc) */ + /* + * use usb alt setting 0 for EP4 transfer (dvb-t), + * use usb alt setting 7 for EP2 transfer (atsc) + */ .size_of_priv = sizeof(struct cxusb_state), @@ -1609,7 +2008,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_dee1601_properties = { } } }, - }}, + } }, }, }, @@ -1634,7 +2033,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_dee1601_properties = { { &cxusb_table[DVICO_BLUEBIRD_DUAL_1_WARM], NULL }, }, { "DigitalNow DVB-T Dual USB", - { &cxusb_table[DIGITALNOW_BLUEBIRD_DUAL_1_COLD], NULL }, + { &cxusb_table[DIGITALNOW_BLUEBIRD_DUAL_1_COLD], NULL }, { &cxusb_table[DIGITALNOW_BLUEBIRD_DUAL_1_WARM], NULL }, }, { "DViCO FusionHDTV DVB-T Dual Digital 2", @@ -1650,8 +2049,10 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgz201_properties = { .usb_ctrl = DEVICE_SPECIFIC, .firmware = "dvb-usb-bluebird-01.fw", .download_firmware = bluebird_patch_dvico_firmware_download, - /* use usb alt setting 0 for EP4 transfer (dvb-t), - use usb alt setting 7 for EP2 transfer (atsc) */ + /* + * use usb alt setting 0 for EP4 transfer (dvb-t), + * use usb alt setting 7 for EP2 transfer (atsc) + */ .size_of_priv = sizeof(struct cxusb_state), @@ -1675,7 +2076,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgz201_properties = { } } }, - }}, + } }, }, }, .power_ctrl = cxusb_bluebird_power_ctrl, @@ -1706,8 +2107,11 @@ static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties = { .usb_ctrl = DEVICE_SPECIFIC, .firmware = "dvb-usb-bluebird-01.fw", .download_firmware = bluebird_patch_dvico_firmware_download, - /* use usb alt setting 0 for EP4 transfer (dvb-t), - use usb alt setting 7 for EP2 transfer (atsc) */ + + /* + * use usb alt setting 0 for EP4 transfer (dvb-t), + * use usb alt setting 7 for EP2 transfer (atsc) + */ .size_of_priv = sizeof(struct cxusb_state), @@ -1731,7 +2135,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties = { } } }, - }}, + } }, }, }, .power_ctrl = cxusb_bluebird_power_ctrl, @@ -1783,7 +2187,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_dualdig4_properties = { } } }, - }}, + } }, }, }, @@ -1837,7 +2241,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_properties = { } } }, - }}, + } }, }, }, @@ -1864,7 +2268,8 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_properties = { } }; -static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_properties = { +static struct dvb_usb_device_properties +cxusb_bluebird_nano2_needsfirmware_properties = { .caps = DVB_USB_IS_AN_I2C_ADAPTER, .usb_ctrl = DEVICE_SPECIFIC, @@ -1893,7 +2298,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_prope } } }, - }}, + } }, }, }, @@ -1912,10 +2317,11 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_prope }, .num_device_descs = 1, - .devices = { - { "DViCO FusionHDTV DVB-T NANO2 w/o firmware", + .devices = { { + "DViCO FusionHDTV DVB-T NANO2 w/o firmware", { &cxusb_table[DVICO_BLUEBIRD_DVB_T_NANO_2], NULL }, - { &cxusb_table[DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM], NULL }, + { &cxusb_table[DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM], + NULL }, }, } }; @@ -1946,7 +2352,7 @@ static struct dvb_usb_device_properties cxusb_aver_a868r_properties = { } } }, - }}, + } }, }, }, .power_ctrl = cxusb_aver_power_ctrl, @@ -1992,7 +2398,7 @@ struct dvb_usb_device_properties cxusb_bluebird_dualdig4_rev2_properties = { } } }, - }}, + } }, }, }, @@ -2046,7 +2452,7 @@ static struct dvb_usb_device_properties cxusb_d680_dmb_properties = { } } }, - }}, + } }, }, }, @@ -2101,7 +2507,7 @@ static struct dvb_usb_device_properties cxusb_mygica_d689_properties = { } } }, - }}, + } }, }, }, @@ -2195,6 +2601,6 @@ module_usb_driver(cxusb_driver); MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>"); MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>"); MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>"); +MODULE_AUTHOR("Maciej S. Szmigiero <mail@maciej.szmigiero.name>"); MODULE_DESCRIPTION("Driver for Conexant USB2.0 hybrid reference design"); -MODULE_VERSION("1.0-alpha"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/usb/dvb-usb/cxusb.h b/drivers/media/usb/dvb-usb/cxusb.h index 88f9b9804b25..9e374e53125b 100644 --- a/drivers/media/usb/dvb-usb/cxusb.h +++ b/drivers/media/usb/dvb-usb/cxusb.h @@ -2,9 +2,29 @@ #ifndef _DVB_USB_CXUSB_H_ #define _DVB_USB_CXUSB_H_ +#include <linux/completion.h> +#include <linux/i2c.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/usb.h> +#include <linux/workqueue.h> +#include <media/v4l2-common.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-device.h> +#include <media/videobuf2-core.h> +#include <media/videobuf2-v4l2.h> + #define DVB_USB_LOG_PREFIX "cxusb" #include "dvb-usb.h" +#define CXUSB_VIDEO_URBS (5) +#define CXUSB_VIDEO_URB_MAX_SIZE (512 * 1024) + +#define CXUSB_VIDEO_PKT_SIZE 3030 +#define CXUSB_VIDEO_MAX_FRAME_PKTS 346 +#define CXUSB_VIDEO_MAX_FRAME_SIZE (CXUSB_VIDEO_MAX_FRAME_PKTS * \ + CXUSB_VIDEO_PKT_SIZE) + /* usb commands - some of it are guesses, don't have a reference yet */ #define CMD_BLUEBIRD_GPIO_RW 0x05 @@ -29,11 +49,26 @@ #define CMD_ANALOG 0x50 #define CMD_DIGITAL 0x51 +#define CXUSB_BT656_PREAMBLE ((const u8 *)"\xff\x00\x00") + +#define CXUSB_BT656_FIELD_MASK BIT(6) +#define CXUSB_BT656_FIELD_1 0 +#define CXUSB_BT656_FIELD_2 BIT(6) + +#define CXUSB_BT656_VBI_MASK BIT(5) +#define CXUSB_BT656_VBI_ON BIT(5) +#define CXUSB_BT656_VBI_OFF 0 + +#define CXUSB_BT656_SEAV_MASK BIT(4) +#define CXUSB_BT656_SEAV_EAV BIT(4) +#define CXUSB_BT656_SEAV_SAV 0 + /* Max transfer size done by I2C transfer functions */ #define MAX_XFER_SIZE 80 struct cxusb_state { u8 gpio_write_state[3]; + bool gpio_write_refresh[3]; struct i2c_client *i2c_client_demod; struct i2c_client *i2c_client_tuner; @@ -42,7 +77,128 @@ struct cxusb_state { struct mutex stream_mutex; u8 last_lock; int (*fe_read_status)(struct dvb_frontend *fe, - enum fe_status *status); + enum fe_status *status); +}; + +enum cxusb_open_type { + CXUSB_OPEN_INIT, + CXUSB_OPEN_NONE, + CXUSB_OPEN_ANALOG, + CXUSB_OPEN_DIGITAL +}; + +struct cxusb_medion_auxbuf { + u8 *buf; + unsigned int len; + unsigned int paylen; +}; + +enum cxusb_bt656_mode { + NEW_FRAME, FIRST_FIELD, SECOND_FIELD +}; + +enum cxusb_bt656_fmode { + START_SEARCH, LINE_SAMPLES, VBI_SAMPLES }; +struct cxusb_bt656_params { + enum cxusb_bt656_mode mode; + enum cxusb_bt656_fmode fmode; + unsigned int pos; + unsigned int line; + unsigned int linesamples; + u8 *buf; +}; + +struct cxusb_medion_dev { + /* has to be the first one */ + struct cxusb_state state; + + struct dvb_usb_device *dvbdev; + + enum cxusb_open_type open_type; + unsigned int open_ctr; + struct mutex open_lock; + +#ifdef CONFIG_DVB_USB_CXUSB_ANALOG + struct v4l2_device v4l2dev; + struct v4l2_subdev *cx25840; + struct v4l2_subdev *tuner; + struct v4l2_subdev *tda9887; + struct video_device *videodev, *radiodev; + struct mutex dev_lock; + + struct vb2_queue videoqueue; + u32 input; + bool stop_streaming; + u32 width, height; + u32 field_order; + struct cxusb_medion_auxbuf auxbuf; + v4l2_std_id norm; + + struct urb *streamurbs[CXUSB_VIDEO_URBS]; + unsigned long urbcomplete; + struct work_struct urbwork; + unsigned int nexturb; + + struct cxusb_bt656_params bt656; + struct cxusb_medion_vbuffer *vbuf; + __u32 vbuf_sequence; + + struct list_head buflist; + + struct completion v4l2_release; +#endif +}; + +struct cxusb_medion_vbuffer { + struct vb2_v4l2_buffer vb2; + struct list_head list; +}; + +/* defines for "debug" module parameter */ +#define CXUSB_DBG_RC BIT(0) +#define CXUSB_DBG_I2C BIT(1) +#define CXUSB_DBG_MISC BIT(2) +#define CXUSB_DBG_BT656 BIT(3) +#define CXUSB_DBG_URB BIT(4) +#define CXUSB_DBG_OPS BIT(5) +#define CXUSB_DBG_AUXB BIT(6) + +extern int dvb_usb_cxusb_debug; + +#define cxusb_vprintk(dvbdev, lvl, ...) do { \ + struct cxusb_medion_dev *_cxdev = (dvbdev)->priv; \ + if (dvb_usb_cxusb_debug & CXUSB_DBG_##lvl) \ + v4l2_printk(KERN_DEBUG, \ + &_cxdev->v4l2dev, __VA_ARGS__); \ + } while (0) + +int cxusb_ctrl_msg(struct dvb_usb_device *d, + u8 cmd, const u8 *wbuf, int wlen, u8 *rbuf, int rlen); + +#ifdef CONFIG_DVB_USB_CXUSB_ANALOG +int cxusb_medion_analog_init(struct dvb_usb_device *dvbdev); +int cxusb_medion_register_analog(struct dvb_usb_device *dvbdev); +void cxusb_medion_unregister_analog(struct dvb_usb_device *dvbdev); +#else +static inline int cxusb_medion_analog_init(struct dvb_usb_device *dvbdev) +{ + return -EINVAL; +} + +static inline int cxusb_medion_register_analog(struct dvb_usb_device *dvbdev) +{ + return 0; +} + +static inline void cxusb_medion_unregister_analog(struct dvb_usb_device *dvbdev) +{ +} +#endif + +int cxusb_medion_get(struct dvb_usb_device *dvbdev, + enum cxusb_open_type open_type); +void cxusb_medion_put(struct dvb_usb_device *dvbdev); + #endif diff --git a/drivers/media/usb/dvb-usb/dvb-usb-dvb.c b/drivers/media/usb/dvb-usb/dvb-usb-dvb.c index 8056053c9ab0..0a7f8ba90992 100644 --- a/drivers/media/usb/dvb-usb/dvb-usb-dvb.c +++ b/drivers/media/usb/dvb-usb/dvb-usb-dvb.c @@ -56,9 +56,6 @@ static int dvb_usb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff) * for reception. */ if (adap->feedcount == onoff && adap->feedcount > 0) { - deb_ts("submitting all URBs\n"); - usb_urb_submit(&adap->fe_adap[adap->active_fe].stream); - deb_ts("controlling pid parser\n"); if (adap->props.fe[adap->active_fe].caps & DVB_USB_ADAP_HAS_PID_FILTER && adap->props.fe[adap->active_fe].caps & @@ -80,6 +77,8 @@ static int dvb_usb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff) } } + deb_ts("submitting all URBs\n"); + usb_urb_submit(&adap->fe_adap[adap->active_fe].stream); } return 0; } diff --git a/drivers/media/usb/dvb-usb/dvb-usb-init.c b/drivers/media/usb/dvb-usb/dvb-usb-init.c index e97f6edc98de..16a0b4a359ea 100644 --- a/drivers/media/usb/dvb-usb/dvb-usb-init.c +++ b/drivers/media/usb/dvb-usb/dvb-usb-init.c @@ -130,6 +130,10 @@ static int dvb_usb_exit(struct dvb_usb_device *d) dvb_usb_i2c_exit(d); deb_info("state should be zero now: %x\n", d->state); d->state = DVB_USB_STATE_INIT; + + if (d->priv != NULL && d->props.priv_destroy != NULL) + d->props.priv_destroy(d); + kfree(d->priv); kfree(d); return 0; @@ -151,6 +155,15 @@ static int dvb_usb_init(struct dvb_usb_device *d, short *adapter_nums) err("no memory for priv in 'struct dvb_usb_device'"); return -ENOMEM; } + + if (d->props.priv_init != NULL) { + ret = d->props.priv_init(d); + if (ret != 0) { + kfree(d->priv); + d->priv = NULL; + return ret; + } + } } /* check the capabilities and set appropriate variables */ @@ -284,12 +297,15 @@ EXPORT_SYMBOL(dvb_usb_device_init); void dvb_usb_device_exit(struct usb_interface *intf) { struct dvb_usb_device *d = usb_get_intfdata(intf); - const char *name = "generic DVB-USB module"; + const char *default_name = "generic DVB-USB module"; + char name[40]; usb_set_intfdata(intf, NULL); if (d != NULL && d->desc != NULL) { - name = d->desc->name; + strscpy(name, d->desc->name, sizeof(name)); dvb_usb_exit(d); + } else { + strscpy(name, default_name, sizeof(name)); } info("%s successfully deinitialized and disconnected.", name); diff --git a/drivers/media/usb/dvb-usb/dvb-usb.h b/drivers/media/usb/dvb-usb/dvb-usb.h index 32829bdd5f22..2eb0e24e8943 100644 --- a/drivers/media/usb/dvb-usb/dvb-usb.h +++ b/drivers/media/usb/dvb-usb/dvb-usb.h @@ -129,6 +129,9 @@ struct usb_data_stream_properties { * @frontend_ctrl: called to power on/off active frontend. * @streaming_ctrl: called to start and stop the MPEG2-TS streaming of the * device (not URB submitting/killing). + * This callback will be called without data URBs being active - data URBs + * will be submitted only after streaming_ctrl(1) returns successfully and + * they will be killed before streaming_ctrl(0) gets called. * @pid_filter_ctrl: called to en/disable the PID filter, if any. * @pid_filter: called to set/unset a PID for filtering. * @frontend_attach: called to attach the possible frontends (fill fe-field @@ -234,6 +237,11 @@ enum dvb_usb_mode { * * @size_of_priv: how many bytes shall be allocated for the private field * of struct dvb_usb_device. + * @priv_init: optional callback to initialize the variable that private field + * of struct dvb_usb_device has pointer to just after it had been allocated and + * zeroed. + * @priv_destroy: just like priv_init, only called before deallocating + * the memory pointed by private field of struct dvb_usb_device. * * @power_ctrl: called to enable/disable power of the device. * @read_mac_address: called to read the MAC address of the device. @@ -275,6 +283,8 @@ struct dvb_usb_device_properties { int no_reconnect; int size_of_priv; + int (*priv_init)(struct dvb_usb_device *); + void (*priv_destroy)(struct dvb_usb_device *); int num_adapters; struct dvb_usb_adapter_properties adapter[MAX_NO_OF_ADAPTER_PER_DEVICE]; diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index d85ea1af6aa1..5aa15a7a49de 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -24,6 +24,7 @@ #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/usb.h> +#include <linux/usb/input.h> #include <linux/slab.h> #include <linux/bitrev.h> @@ -58,7 +59,6 @@ struct em28xx_ir_poll_result { struct em28xx_IR { struct em28xx *dev; struct rc_dev *rc; - char name[32]; char phys[32]; /* poll decoder */ @@ -277,21 +277,8 @@ static int em2874_polling_getkey(struct em28xx_IR *ir, break; case RC_PROTO_BIT_NEC: - poll_result->scancode = msg[1] << 8 | msg[2]; - if ((msg[3] ^ msg[4]) != 0xff) { /* 32 bits NEC */ - poll_result->protocol = RC_PROTO_NEC32; - poll_result->scancode = RC_SCANCODE_NEC32((msg[1] << 24) | - (msg[2] << 16) | - (msg[3] << 8) | - (msg[4])); - } else if ((msg[1] ^ msg[2]) != 0xff) { /* 24 bits NEC */ - poll_result->protocol = RC_PROTO_NECX; - poll_result->scancode = RC_SCANCODE_NECX(msg[1] << 8 | - msg[2], msg[3]); - } else { /* Normal NEC */ - poll_result->protocol = RC_PROTO_NEC; - poll_result->scancode = RC_SCANCODE_NEC(msg[1], msg[3]); - } + poll_result->scancode = ir_nec_bytes_to_scancode(msg[1], msg[2], msg[3], msg[4], + &poll_result->protocol); break; case RC_PROTO_BIT_RC6_0: @@ -617,10 +604,7 @@ static int em28xx_register_snapshot_button(struct em28xx *dev) set_bit(EM28XX_SNAPSHOT_KEY, input_dev->keybit); input_dev->keycodesize = 0; input_dev->keycodemax = 0; - input_dev->id.bustype = BUS_USB; - input_dev->id.vendor = le16_to_cpu(udev->descriptor.idVendor); - input_dev->id.product = le16_to_cpu(udev->descriptor.idProduct); - input_dev->id.version = 1; + usb_to_input_id(udev, &input_dev->id); input_dev->dev.parent = &dev->intf->dev; err = input_register_device(input_dev); @@ -832,19 +816,12 @@ static int em28xx_ir_init(struct em28xx *dev) /* This is how often we ask the chip for IR information */ ir->polling = 100; /* ms */ - /* init input device */ - snprintf(ir->name, sizeof(ir->name), "%s IR", - dev_name(&dev->intf->dev)); - usb_make_path(udev, ir->phys, sizeof(ir->phys)); strlcat(ir->phys, "/input0", sizeof(ir->phys)); - rc->device_name = ir->name; + rc->device_name = em28xx_boards[dev->model].name; rc->input_phys = ir->phys; - rc->input_id.bustype = BUS_USB; - rc->input_id.version = 1; - rc->input_id.vendor = le16_to_cpu(udev->descriptor.idVendor); - rc->input_id.product = le16_to_cpu(udev->descriptor.idProduct); + usb_to_input_id(udev, &rc->input_id); rc->dev.parent = &dev->intf->dev; rc->driver_name = MODULE_NAME; diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index f43717ea831d..0512e1959394 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1984,7 +1984,6 @@ static int vidioc_s_register(struct file *file, void *priv, static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct video_device *vdev = video_devdata(file); struct em28xx *dev = video_drvdata(file); struct em28xx_v4l2 *v4l2 = dev->v4l2; struct usb_device *udev = interface_to_usbdev(dev->intf); @@ -1993,23 +1992,12 @@ static int vidioc_querycap(struct file *file, void *priv, strscpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card)); usb_make_path(udev, cap->bus_info, sizeof(cap->bus_info)); - if (vdev->vfl_type == VFL_TYPE_GRABBER) - cap->device_caps = V4L2_CAP_READWRITE | - V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - else if (vdev->vfl_type == VFL_TYPE_RADIO) - cap->device_caps = V4L2_CAP_RADIO; - else - cap->device_caps = V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE; - + cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_READWRITE | + V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; if (dev->int_audio_type != EM28XX_INT_AUDIO_NONE) - cap->device_caps |= V4L2_CAP_AUDIO; - + cap->capabilities |= V4L2_CAP_AUDIO; if (dev->tuner_type != TUNER_ABSENT) - cap->device_caps |= V4L2_CAP_TUNER; - - cap->capabilities = cap->device_caps | - V4L2_CAP_DEVICE_CAPS | V4L2_CAP_READWRITE | - V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + cap->capabilities |= V4L2_CAP_TUNER; if (video_is_registered(&v4l2->vbi_dev)) cap->capabilities |= V4L2_CAP_VBI_CAPTURE; if (video_is_registered(&v4l2->radio_dev)) @@ -2782,6 +2770,13 @@ static int em28xx_v4l2_init(struct em28xx *dev) mutex_init(&v4l2->vb_vbi_queue_lock); v4l2->vdev.queue = &v4l2->vb_vidq; v4l2->vdev.queue->lock = &v4l2->vb_queue_lock; + v4l2->vdev.device_caps = V4L2_CAP_READWRITE | V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING; + if (dev->int_audio_type != EM28XX_INT_AUDIO_NONE) + v4l2->vdev.device_caps |= V4L2_CAP_AUDIO; + if (dev->tuner_type != TUNER_ABSENT) + v4l2->vdev.device_caps |= V4L2_CAP_TUNER; + /* disable inapplicable ioctls */ if (dev->is_webcam) { @@ -2818,6 +2813,10 @@ static int em28xx_v4l2_init(struct em28xx *dev) v4l2->vbi_dev.queue = &v4l2->vb_vbiq; v4l2->vbi_dev.queue->lock = &v4l2->vb_vbi_queue_lock; + v4l2->vbi_dev.device_caps = V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE; + if (dev->tuner_type != TUNER_ABSENT) + v4l2->vbi_dev.device_caps |= V4L2_CAP_TUNER; /* disable inapplicable ioctls */ v4l2_disable_ioctl(&v4l2->vbi_dev, VIDIOC_S_PARM); @@ -2845,6 +2844,7 @@ static int em28xx_v4l2_init(struct em28xx *dev) if (em28xx_boards[dev->model].radio.type == EM28XX_RADIO) { em28xx_vdev_init(dev, &v4l2->radio_dev, &em28xx_radio_template, "radio"); + v4l2->radio_dev.device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER; ret = video_register_device(&v4l2->radio_dev, VFL_TYPE_RADIO, radio_nr[dev->devno]); if (ret < 0) { diff --git a/drivers/media/usb/go7007/go7007-v4l2.c b/drivers/media/usb/go7007/go7007-v4l2.c index b63b7bb7745c..88edfef80b40 100644 --- a/drivers/media/usb/go7007/go7007-v4l2.c +++ b/drivers/media/usb/go7007/go7007-v4l2.c @@ -279,15 +279,6 @@ static int vidioc_querycap(struct file *file, void *priv, strscpy(cap->driver, "go7007", sizeof(cap->driver)); strscpy(cap->card, go->name, sizeof(cap->card)); strscpy(cap->bus_info, go->bus_info, sizeof(cap->bus_info)); - - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING; - - if (go->board_info->num_aud_inputs) - cap->device_caps |= V4L2_CAP_AUDIO; - if (go->board_info->flags & GO7007_BOARD_HAS_TUNER) - cap->device_caps |= V4L2_CAP_TUNER; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -1114,6 +1105,12 @@ int go7007_v4l2_init(struct go7007 *go) *vdev = go7007_template; vdev->lock = &go->serialize_lock; vdev->queue = &go->vidq; + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + if (go->board_info->num_aud_inputs) + vdev->device_caps |= V4L2_CAP_AUDIO; + if (go->board_info->flags & GO7007_BOARD_HAS_TUNER) + vdev->device_caps |= V4L2_CAP_TUNER; video_set_drvdata(vdev, go); vdev->v4l2_dev = &go->v4l2_dev; if (!v4l2_device_has_op(&go->v4l2_dev, 0, video, querystd)) diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c index a7ed5257cdba..be11f7830bca 100644 --- a/drivers/media/usb/gspca/gspca.c +++ b/drivers/media/usb/gspca/gspca.c @@ -1209,10 +1209,6 @@ static int vidioc_querycap(struct file *file, void *priv, } usb_make_path(gspca_dev->dev, (char *) cap->bus_info, sizeof(cap->bus_info)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE - | V4L2_CAP_STREAMING - | V4L2_CAP_READWRITE; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -1508,6 +1504,8 @@ int gspca_dev_probe2(struct usb_interface *intf, gspca_dev->empty_packet = -1; /* don't check the empty packets */ gspca_dev->vdev = gspca_template; gspca_dev->vdev.v4l2_dev = &gspca_dev->v4l2_dev; + gspca_dev->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; video_set_drvdata(&gspca_dev->vdev, gspca_dev); gspca_dev->module = module; diff --git a/drivers/media/usb/hackrf/hackrf.c b/drivers/media/usb/hackrf/hackrf.c index 7d4a9452f545..cec841ad7495 100644 --- a/drivers/media/usb/hackrf/hackrf.c +++ b/drivers/media/usb/hackrf/hackrf.c @@ -896,19 +896,13 @@ static int hackrf_querycap(struct file *file, void *fh, { struct hackrf_dev *dev = video_drvdata(file); struct usb_interface *intf = dev->intf; - struct video_device *vdev = video_devdata(file); dev_dbg(&intf->dev, "\n"); - cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; - if (vdev->vfl_dir == VFL_DIR_RX) - cap->device_caps |= V4L2_CAP_SDR_CAPTURE | V4L2_CAP_TUNER; - else - cap->device_caps |= V4L2_CAP_SDR_OUTPUT | V4L2_CAP_MODULATOR; - cap->capabilities = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_TUNER | V4L2_CAP_SDR_OUTPUT | V4L2_CAP_MODULATOR | - V4L2_CAP_DEVICE_CAPS | cap->device_caps; + V4L2_CAP_STREAMING | V4L2_CAP_READWRITE | + V4L2_CAP_DEVICE_CAPS; strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); strscpy(cap->card, dev->rx_vdev.name, sizeof(cap->card)); usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); @@ -1487,6 +1481,8 @@ static int hackrf_probe(struct usb_interface *intf, dev->rx_vdev.ctrl_handler = &dev->rx_ctrl_handler; dev->rx_vdev.lock = &dev->v4l2_lock; dev->rx_vdev.vfl_dir = VFL_DIR_RX; + dev->rx_vdev.device_caps = V4L2_CAP_STREAMING | V4L2_CAP_READWRITE | + V4L2_CAP_SDR_CAPTURE | V4L2_CAP_TUNER; video_set_drvdata(&dev->rx_vdev, dev); ret = video_register_device(&dev->rx_vdev, VFL_TYPE_SDR, -1); if (ret) { @@ -1505,6 +1501,8 @@ static int hackrf_probe(struct usb_interface *intf, dev->tx_vdev.ctrl_handler = &dev->tx_ctrl_handler; dev->tx_vdev.lock = &dev->v4l2_lock; dev->tx_vdev.vfl_dir = VFL_DIR_TX; + dev->tx_vdev.device_caps = V4L2_CAP_STREAMING | V4L2_CAP_READWRITE | + V4L2_CAP_SDR_OUTPUT | V4L2_CAP_MODULATOR; video_set_drvdata(&dev->tx_vdev, dev); ret = video_register_device(&dev->tx_vdev, VFL_TYPE_SDR, -1); if (ret) { diff --git a/drivers/media/usb/hdpvr/hdpvr-video.c b/drivers/media/usb/hdpvr/hdpvr-video.c index 7580fc5f2f12..3786ddcc0d18 100644 --- a/drivers/media/usb/hdpvr/hdpvr-video.c +++ b/drivers/media/usb/hdpvr/hdpvr-video.c @@ -577,9 +577,6 @@ static int vidioc_querycap(struct file *file, void *priv, strscpy(cap->driver, "hdpvr", sizeof(cap->driver)); strscpy(cap->card, "Hauppauge HD PVR", sizeof(cap->card)); usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_AUDIO | - V4L2_CAP_READWRITE; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -1150,6 +1147,8 @@ static const struct video_device hdpvr_video_template = { .release = hdpvr_device_release, .ioctl_ops = &hdpvr_ioctl_ops, .tvnorms = V4L2_STD_ALL, + .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_AUDIO | + V4L2_CAP_READWRITE, }; static const struct v4l2_ctrl_ops hdpvr_ctrl_ops = { diff --git a/drivers/media/usb/msi2500/msi2500.c b/drivers/media/usb/msi2500/msi2500.c index b405bc3c2781..4c9b2a12acfb 100644 --- a/drivers/media/usb/msi2500/msi2500.c +++ b/drivers/media/usb/msi2500/msi2500.c @@ -598,9 +598,6 @@ static int msi2500_querycap(struct file *file, void *fh, strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); strscpy(cap->card, dev->vdev.name, sizeof(cap->card)); usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE | V4L2_CAP_TUNER; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -1274,6 +1271,8 @@ static int msi2500_probe(struct usb_interface *intf, dev->v4l2_dev.ctrl_handler = &dev->hdl; dev->vdev.v4l2_dev = &dev->v4l2_dev; dev->vdev.lock = &dev->v4l2_lock; + dev->vdev.device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE | V4L2_CAP_TUNER; ret = video_register_device(&dev->vdev, VFL_TYPE_SDR, -1); if (ret) { diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c index 816c85786c2a..191439109788 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c @@ -1680,7 +1680,7 @@ static int pvr2_decoder_enable(struct pvr2_hdw *hdw,int enablefl) } if (!hdw->flag_decoder_missed) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "WARNING: No decoder present"); + "***WARNING*** No decoder present"); hdw->flag_decoder_missed = !0; trace_stbit("flag_decoder_missed", hdw->flag_decoder_missed); @@ -2366,7 +2366,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, if (hdw_desc->flag_is_experimental) { pvr2_trace(PVR2_TRACE_INFO, "**********"); pvr2_trace(PVR2_TRACE_INFO, - "WARNING: Support for this device (%s) is experimental.", + "***WARNING*** Support for this device (%s) is experimental.", hdw_desc->description); pvr2_trace(PVR2_TRACE_INFO, "Important functionality might not be entirely working."); diff --git a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c index 8f023085c2d9..43e54bdbd4aa 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c @@ -343,11 +343,11 @@ static int i2c_hack_cx25840(struct pvr2_hdw *hdw, if ((ret != 0) || (*rdata == 0x04) || (*rdata == 0x0a)) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "WARNING: Detected a wedged cx25840 chip; the device will not work."); + "***WARNING*** Detected a wedged cx25840 chip; the device will not work."); pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "WARNING: Try power cycling the pvrusb2 device."); + "***WARNING*** Try power cycling the pvrusb2 device."); pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "WARNING: Disabling further access to the device to prevent other foul-ups."); + "***WARNING*** Disabling further access to the device to prevent other foul-ups."); // This blocks all further communication with the part. hdw->i2c_func[0x44] = NULL; pvr2_hdw_render_useless(hdw); diff --git a/drivers/media/usb/pvrusb2/pvrusb2-std.c b/drivers/media/usb/pvrusb2/pvrusb2-std.c index 6b651f8b54df..37dc299a1ca2 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-std.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-std.c @@ -353,7 +353,7 @@ struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr, bcnt = pvr2_std_id_to_str(buf,sizeof(buf),fmsk); pvr2_trace( PVR2_TRACE_ERROR_LEGS, - "WARNING: Failed to classify the following standard(s): %.*s", + "***WARNING*** Failed to classify the following standard(s): %.*s", bcnt,buf); } diff --git a/drivers/media/usb/pvrusb2/pvrusb2-sysfs.c b/drivers/media/usb/pvrusb2/pvrusb2-sysfs.c index 7bc6d090358e..b6c6b314fadc 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-sysfs.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-sysfs.c @@ -802,7 +802,8 @@ struct pvr2_sysfs_class *pvr2_sysfs_class_create(void) void pvr2_sysfs_class_destroy(struct pvr2_sysfs_class *clp) { pvr2_sysfs_trace("Unregistering pvr2_sysfs_class id=%p", clp); - class_unregister(&clp->class); + if (clp) + class_unregister(&clp->class); } diff --git a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c index cb6668580d77..30701ecc84c5 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c @@ -128,17 +128,6 @@ static int pvr2_querycap(struct file *file, void *priv, struct v4l2_capability * cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO | V4L2_CAP_READWRITE | V4L2_CAP_DEVICE_CAPS; - switch (fh->pdi->devbase.vfl_type) { - case VFL_TYPE_GRABBER: - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_AUDIO; - break; - case VFL_TYPE_RADIO: - cap->device_caps = V4L2_CAP_RADIO; - break; - default: - return -EINVAL; - } - cap->device_caps |= V4L2_CAP_TUNER | V4L2_CAP_READWRITE; return 0; } @@ -1205,6 +1194,8 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, int unit_number; struct pvr2_hdw *hdw; int *nr_ptr = NULL; + u32 caps = V4L2_CAP_TUNER | V4L2_CAP_READWRITE; + dip->v4lp = vp; hdw = vp->channel.mc_head->hdw; @@ -1215,6 +1206,7 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, dip->config = pvr2_config_mpeg; dip->minor_type = pvr2_v4l_type_video; nr_ptr = video_nr; + caps |= V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_AUDIO; if (!dip->stream) { pr_err(KBUILD_MODNAME ": Failed to set up pvrusb2 v4l video dev due to missing stream instance\n"); @@ -1225,12 +1217,14 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, dip->config = pvr2_config_vbi; dip->minor_type = pvr2_v4l_type_vbi; nr_ptr = vbi_nr; + caps |= V4L2_CAP_VBI_CAPTURE; break; case VFL_TYPE_RADIO: dip->stream = &vp->channel.mc_head->video_stream; dip->config = pvr2_config_mpeg; dip->minor_type = pvr2_v4l_type_radio; nr_ptr = radio_nr; + caps |= V4L2_CAP_RADIO; break; default: /* Bail out (this should be impossible) */ @@ -1241,6 +1235,7 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, dip->devbase = vdev_template; dip->devbase.release = pvr2_video_device_release; dip->devbase.ioctl_ops = &pvr2_ioctl_ops; + dip->devbase.device_caps = caps; { int val; pvr2_ctrl_get_value( diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index a15ad0f3faf1..9b76cf133d52 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -1113,6 +1113,8 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pdev->v4l2_dev.ctrl_handler = &pdev->ctrl_handler; pdev->vdev.v4l2_dev = &pdev->v4l2_dev; pdev->vdev.lock = &pdev->v4l2_lock; + pdev->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE; rc = video_register_device(&pdev->vdev, VFL_TYPE_GRABBER, -1); if (rc < 0) { diff --git a/drivers/media/usb/pwc/pwc-v4l.c b/drivers/media/usb/pwc/pwc-v4l.c index 5212898db77c..76c498cccc49 100644 --- a/drivers/media/usb/pwc/pwc-v4l.c +++ b/drivers/media/usb/pwc/pwc-v4l.c @@ -483,9 +483,6 @@ static int pwc_querycap(struct file *file, void *fh, struct v4l2_capability *cap strscpy(cap->driver, PWC_NAME, sizeof(cap->driver)); strscpy(cap->card, pdev->vdev.name, sizeof(cap->card)); usb_make_path(pdev->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } diff --git a/drivers/media/usb/pwc/pwc.h b/drivers/media/usb/pwc/pwc.h index 8aa7e868e6b1..3362962d0d00 100644 --- a/drivers/media/usb/pwc/pwc.h +++ b/drivers/media/usb/pwc/pwc.h @@ -43,15 +43,15 @@ /* Trace certain actions in the driver */ -#define PWC_DEBUG_LEVEL_MODULE (1<<0) -#define PWC_DEBUG_LEVEL_PROBE (1<<1) -#define PWC_DEBUG_LEVEL_OPEN (1<<2) -#define PWC_DEBUG_LEVEL_READ (1<<3) -#define PWC_DEBUG_LEVEL_MEMORY (1<<4) -#define PWC_DEBUG_LEVEL_FLOW (1<<5) -#define PWC_DEBUG_LEVEL_SIZE (1<<6) -#define PWC_DEBUG_LEVEL_IOCTL (1<<7) -#define PWC_DEBUG_LEVEL_TRACE (1<<8) +#define PWC_DEBUG_LEVEL_MODULE BIT(0) +#define PWC_DEBUG_LEVEL_PROBE BIT(1) +#define PWC_DEBUG_LEVEL_OPEN BIT(2) +#define PWC_DEBUG_LEVEL_READ BIT(3) +#define PWC_DEBUG_LEVEL_MEMORY BIT(4) +#define PWC_DEBUG_LEVEL_FLOW BIT(5) +#define PWC_DEBUG_LEVEL_SIZE BIT(6) +#define PWC_DEBUG_LEVEL_IOCTL BIT(7) +#define PWC_DEBUG_LEVEL_TRACE BIT(8) #define PWC_DEBUG_MODULE(fmt, args...) PWC_DEBUG(MODULE, fmt, ##args) #define PWC_DEBUG_PROBE(fmt, args...) PWC_DEBUG(PROBE, fmt, ##args) diff --git a/drivers/media/usb/s2255/Kconfig b/drivers/media/usb/s2255/Kconfig index e0e3c0ba3f23..e4a0c914d9c3 100644 --- a/drivers/media/usb/s2255/Kconfig +++ b/drivers/media/usb/s2255/Kconfig @@ -3,7 +3,6 @@ config USB_S2255 tristate "USB Sensoray 2255 video capture device" depends on VIDEO_V4L2 select VIDEOBUF2_VMALLOC - default n help Say Y here if you want support for the Sensoray 2255 USB device. This driver can be compiled as a module, called s2255drv. diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index 3eccbd48bdac..aa90558479f7 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -724,9 +724,6 @@ static int vidioc_querycap(struct file *file, void *priv, strscpy(cap->driver, "s2255", sizeof(cap->driver)); strscpy(cap->card, "s2255", sizeof(cap->card)); usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -1657,6 +1654,8 @@ static int s2255_probe_v4l(struct s2255_dev *dev) vc->vdev.ctrl_handler = &vc->hdl; vc->vdev.lock = &dev->lock; vc->vdev.v4l2_dev = &dev->v4l2_dev; + vc->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; video_set_drvdata(&vc->vdev, vc); if (video_nr == -1) ret = video_register_device(&vc->vdev, diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c index 38016632c6d8..b71a0f4b40b5 100644 --- a/drivers/media/usb/stk1160/stk1160-v4l.c +++ b/drivers/media/usb/stk1160/stk1160-v4l.c @@ -337,11 +337,6 @@ static int vidioc_querycap(struct file *file, strscpy(cap->driver, "stk1160", sizeof(cap->driver)); strscpy(cap->card, "stk1160", sizeof(cap->card)); usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->device_caps = - V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -821,6 +816,8 @@ int stk1160_video_register(struct stk1160 *dev) /* This will be used to set video_device parent */ dev->vdev.v4l2_dev = &dev->v4l2_dev; + dev->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE; /* NTSC is default */ dev->norm = V4L2_STD_NTSC_M; diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c index cb7d6454bbe1..be8041e3e6b8 100644 --- a/drivers/media/usb/stkwebcam/stk-webcam.c +++ b/drivers/media/usb/stkwebcam/stk-webcam.c @@ -798,10 +798,6 @@ static int stk_vidioc_querycap(struct file *filp, strscpy(cap->driver, "stk", sizeof(cap->driver)); strscpy(cap->card, "stk", sizeof(cap->card)); usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE - | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -1261,6 +1257,8 @@ static int stk_register_video_device(struct stk_camera *dev) dev->vdev = stk_v4l_data; dev->vdev.lock = &dev->lock; dev->vdev.v4l2_dev = &dev->v4l2_dev; + dev->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; video_set_drvdata(&dev->vdev, dev); err = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1); if (err) diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index 072210f5f92f..85fcddfb0202 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -854,22 +854,17 @@ static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { struct tm6000_core *dev = ((struct tm6000_fh *)priv)->dev; - struct video_device *vdev = video_devdata(file); strscpy(cap->driver, "tm6000", sizeof(cap->driver)); strscpy(cap->card, "Trident TVMaster 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->device_caps |= V4L2_CAP_TUNER; - if (vdev->vfl_type == VFL_TYPE_GRABBER) - cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE; - else - cap->device_caps |= V4L2_CAP_RADIO; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS | - V4L2_CAP_RADIO | V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + cap->capabilities |= V4L2_CAP_TUNER; + if (dev->caps.has_radio) + cap->capabilities |= V4L2_CAP_RADIO; return 0; } @@ -1639,6 +1634,10 @@ int tm6000_v4l2_register(struct tm6000_core *dev) 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); @@ -1659,6 +1658,7 @@ int tm6000_v4l2_register(struct tm6000_core *dev) 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) { diff --git a/drivers/media/usb/usbtv/usbtv-video.c b/drivers/media/usb/usbtv/usbtv-video.c index 4a1eab711bdc..51f784479e91 100644 --- a/drivers/media/usb/usbtv/usbtv-video.c +++ b/drivers/media/usb/usbtv/usbtv-video.c @@ -603,9 +603,6 @@ static int usbtv_querycap(struct file *file, void *priv, strscpy(cap->driver, "usbtv", sizeof(cap->driver)); strscpy(cap->card, "usbtv", sizeof(cap->card)); usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE; - cap->device_caps |= V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -942,6 +939,8 @@ int usbtv_video_init(struct usbtv *usbtv) usbtv->vdev.tvnorms = USBTV_TV_STD; usbtv->vdev.queue = &usbtv->vb2q; usbtv->vdev.lock = &usbtv->v4l2_lock; + usbtv->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; video_set_drvdata(&usbtv->vdev, usbtv); ret = video_register_device(&usbtv->vdev, VFL_TYPE_GRABBER, -1); if (ret < 0) { diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c index 6d42154e3d0a..93750af82d98 100644 --- a/drivers/media/usb/usbvision/usbvision-video.c +++ b/drivers/media/usb/usbvision/usbvision-video.c @@ -452,24 +452,18 @@ static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *vc) { struct usb_usbvision *usbvision = video_drvdata(file); - struct video_device *vdev = video_devdata(file); strscpy(vc->driver, "USBVision", sizeof(vc->driver)); strscpy(vc->card, usbvision_device_data[usbvision->dev_model].model_string, sizeof(vc->card)); usb_make_path(usbvision->dev, vc->bus_info, sizeof(vc->bus_info)); - vc->device_caps = usbvision->have_tuner ? V4L2_CAP_TUNER : 0; - if (vdev->vfl_type == VFL_TYPE_GRABBER) - vc->device_caps |= V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; - else - vc->device_caps |= V4L2_CAP_RADIO; - - vc->capabilities = vc->device_caps | V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | V4L2_CAP_DEVICE_CAPS; + vc->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING | V4L2_CAP_DEVICE_CAPS; if (usbvision_device_data[usbvision->dev_model].radio) vc->capabilities |= V4L2_CAP_RADIO; + if (usbvision->have_tuner) + vc->capabilities |= V4L2_CAP_TUNER; return 0; } @@ -1267,6 +1261,11 @@ static int usbvision_register_video(struct usb_usbvision *usbvision) v4l2_disable_ioctl(&usbvision->vdev, VIDIOC_G_FREQUENCY); v4l2_disable_ioctl(&usbvision->vdev, VIDIOC_S_TUNER); } + usbvision->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + if (usbvision->have_tuner) + usbvision->vdev.device_caps |= V4L2_CAP_TUNER; + if (video_register_device(&usbvision->vdev, VFL_TYPE_GRABBER, video_nr) < 0) goto err_exit; printk(KERN_INFO "USBVision[%d]: registered USBVision Video device %s [v4l2]\n", @@ -1277,6 +1276,7 @@ static int usbvision_register_video(struct usb_usbvision *usbvision) /* usbvision has radio */ usbvision_vdev_init(usbvision, &usbvision->rdev, &usbvision_radio_template, "USBVision Radio"); + usbvision->rdev.device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER; if (video_register_device(&usbvision->rdev, VFL_TYPE_RADIO, radio_nr) < 0) goto err_exit; printk(KERN_INFO "USBVision[%d]: registered USBVision Radio device %s [v4l2]\n", diff --git a/drivers/media/usb/zr364xx/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c index 37a7992585df..a9bcba4fa9c6 100644 --- a/drivers/media/usb/zr364xx/zr364xx.c +++ b/drivers/media/usb/zr364xx/zr364xx.c @@ -694,14 +694,10 @@ static int zr364xx_vidioc_querycap(struct file *file, void *priv, struct zr364xx_camera *cam = video_drvdata(file); strscpy(cap->driver, DRIVER_DESC, sizeof(cap->driver)); - strscpy(cap->card, cam->udev->product, sizeof(cap->card)); + 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)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - return 0; } @@ -1328,6 +1324,8 @@ static const struct video_device zr364xx_template = { .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, }; diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig index 8b9d4b3ec10e..7c5f62f196e5 100644 --- a/drivers/media/v4l2-core/Kconfig +++ b/drivers/media/v4l2-core/Kconfig @@ -13,7 +13,6 @@ config VIDEO_V4L2 config VIDEO_ADV_DEBUG bool "Enable advanced debug functionality on V4L2 drivers" - default n help Say Y here to enable advanced debugging functionality on some V4L devices. @@ -21,7 +20,6 @@ config VIDEO_ADV_DEBUG config VIDEO_FIXED_MINOR_RANGES bool "Enable old-style fixed minor ranges on drivers/video devices" - default n help Say Y here to enable the old-style fixed-range minor assignments. Only useful if you rely on the old behavior and use mknod instead of udev. diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index c9efb2de710d..f8ad1c580a3e 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -321,6 +321,16 @@ static unsigned int clamp_align(unsigned int x, unsigned int min, return x; } +static unsigned int clamp_roundup(unsigned int x, unsigned int min, + unsigned int max, unsigned int alignment) +{ + x = clamp(x, min, max); + if (alignment) + x = round_up(x, alignment); + + return x; +} + void v4l_bound_align_image(u32 *w, unsigned int wmin, unsigned int wmax, unsigned int walign, u32 *h, unsigned int hmin, unsigned int hmax, @@ -531,8 +541,25 @@ static inline unsigned int v4l2_format_block_height(const struct v4l2_format_inf return info->block_h[plane]; } +void v4l2_apply_frmsize_constraints(u32 *width, u32 *height, + const struct v4l2_frmsize_stepwise *frmsize) +{ + if (!frmsize) + return; + + /* + * Clamp width/height to meet min/max constraints and round it up to + * macroblock alignment. + */ + *width = clamp_roundup(*width, frmsize->min_width, frmsize->max_width, + frmsize->step_width); + *height = clamp_roundup(*height, frmsize->min_height, frmsize->max_height, + frmsize->step_height); +} +EXPORT_SYMBOL_GPL(v4l2_apply_frmsize_constraints); + int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt, - int pixelformat, int width, int height) + u32 pixelformat, u32 width, u32 height) { const struct v4l2_format_info *info; struct v4l2_plane_pix_format *plane; @@ -586,7 +613,8 @@ int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt, } EXPORT_SYMBOL_GPL(v4l2_fill_pixfmt_mp); -int v4l2_fill_pixfmt(struct v4l2_pix_format *pixfmt, int pixelformat, int width, int height) +int v4l2_fill_pixfmt(struct v4l2_pix_format *pixfmt, u32 pixelformat, + u32 width, u32 height) { const struct v4l2_format_info *info; int i; diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 7d3a33258748..f2b9bdedbf8c 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -394,6 +394,21 @@ const char * const *v4l2_ctrl_get_menu(u32 id) "Explicit", NULL, }; + static const char * const mpeg_mpeg2_level[] = { + "Low", + "Main", + "High 1440", + "High", + NULL, + }; + static const char * const mpeg2_profile[] = { + "Simple", + "Main", + "SNR Scalable", + "Spatially Scalable", + "High", + NULL, + }; static const char * const mpeg_mpeg4_level[] = { "0", "0b", @@ -610,6 +625,10 @@ const char * const *v4l2_ctrl_get_menu(u32 id) return h264_fp_arrangement_type; case V4L2_CID_MPEG_VIDEO_H264_FMO_MAP_TYPE: return h264_fmo_map_type; + case V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL: + return mpeg_mpeg2_level; + case V4L2_CID_MPEG_VIDEO_MPEG2_PROFILE: + return mpeg2_profile; case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: return mpeg_mpeg4_level; case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: @@ -820,6 +839,13 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_MAX_QP: return "H264 I-Frame Maximum QP Value"; case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_MIN_QP: return "H264 P-Frame Minimum QP Value"; case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_MAX_QP: return "H264 P-Frame Maximum QP Value"; + case V4L2_CID_MPEG_VIDEO_H264_SPS: return "H264 Sequence Parameter Set"; + case V4L2_CID_MPEG_VIDEO_H264_PPS: return "H264 Picture Parameter Set"; + case V4L2_CID_MPEG_VIDEO_H264_SCALING_MATRIX: return "H264 Scaling Matrix"; + case V4L2_CID_MPEG_VIDEO_H264_SLICE_PARAMS: return "H264 Slice Parameters"; + case V4L2_CID_MPEG_VIDEO_H264_DECODE_PARAMS: return "H264 Decode Parameters"; + case V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL: return "MPEG2 Level"; + case V4L2_CID_MPEG_VIDEO_MPEG2_PROFILE: return "MPEG2 Profile"; case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP: return "MPEG4 I-Frame QP Value"; case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP: return "MPEG4 P-Frame QP Value"; case V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP: return "MPEG4 B-Frame QP Value"; @@ -1145,6 +1171,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_FLASH_STROBE_STOP: case V4L2_CID_AUTO_FOCUS_START: case V4L2_CID_AUTO_FOCUS_STOP: + case V4L2_CID_DO_WHITE_BALANCE: *type = V4L2_CTRL_TYPE_BUTTON; *flags |= V4L2_CTRL_FLAG_WRITE_ONLY | V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; @@ -1184,6 +1211,8 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC: case V4L2_CID_MPEG_VIDEO_H264_SEI_FP_ARRANGEMENT_TYPE: case V4L2_CID_MPEG_VIDEO_H264_FMO_MAP_TYPE: + case V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL: + case V4L2_CID_MPEG_VIDEO_MPEG2_PROFILE: case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: @@ -1301,6 +1330,21 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_VIDEO_FWHT_PARAMS: *type = V4L2_CTRL_TYPE_FWHT_PARAMS; break; + case V4L2_CID_MPEG_VIDEO_H264_SPS: + *type = V4L2_CTRL_TYPE_H264_SPS; + break; + case V4L2_CID_MPEG_VIDEO_H264_PPS: + *type = V4L2_CTRL_TYPE_H264_PPS; + break; + case V4L2_CID_MPEG_VIDEO_H264_SCALING_MATRIX: + *type = V4L2_CTRL_TYPE_H264_SCALING_MATRIX; + break; + case V4L2_CID_MPEG_VIDEO_H264_SLICE_PARAMS: + *type = V4L2_CTRL_TYPE_H264_SLICE_PARAMS; + break; + case V4L2_CID_MPEG_VIDEO_H264_DECODE_PARAMS: + *type = V4L2_CTRL_TYPE_H264_DECODE_PARAMS; + break; default: *type = V4L2_CTRL_TYPE_INTEGER; break; @@ -1453,7 +1497,14 @@ static bool std_equal(const struct v4l2_ctrl *ctrl, u32 idx, static void std_init(const struct v4l2_ctrl *ctrl, u32 idx, union v4l2_ctrl_ptr ptr) { - switch (ctrl->type) { + struct v4l2_ctrl_mpeg2_slice_params *p_mpeg2_slice_params; + + /* + * The cast is needed to get rid of a gcc warning complaining that + * V4L2_CTRL_TYPE_MPEG2_SLICE_PARAMS is not part of the + * v4l2_ctrl_type enum. + */ + switch ((u32)ctrl->type) { case V4L2_CTRL_TYPE_STRING: idx *= ctrl->elem_size; memset(ptr.p_char + idx, ' ', ctrl->minimum); @@ -1478,6 +1529,17 @@ static void std_init(const struct v4l2_ctrl *ctrl, u32 idx, case V4L2_CTRL_TYPE_U32: ptr.p_u32[idx] = ctrl->default_value; break; + case V4L2_CTRL_TYPE_MPEG2_SLICE_PARAMS: + p_mpeg2_slice_params = ptr.p; + /* 4:2:0 */ + p_mpeg2_slice_params->sequence.chroma_format = 1; + /* 8 bits */ + p_mpeg2_slice_params->picture.intra_dc_precision = 0; + /* interlaced top field */ + p_mpeg2_slice_params->picture.picture_structure = 1; + p_mpeg2_slice_params->picture.picture_coding_type = + V4L2_MPEG2_PICTURE_CODING_TYPE_I; + break; default: idx *= ctrl->elem_size; memset(ptr.p + idx, 0, ctrl->elem_size); @@ -1670,6 +1732,13 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx, case V4L2_CTRL_TYPE_FWHT_PARAMS: return 0; + case V4L2_CTRL_TYPE_H264_SPS: + case V4L2_CTRL_TYPE_H264_PPS: + case V4L2_CTRL_TYPE_H264_SCALING_MATRIX: + case V4L2_CTRL_TYPE_H264_SLICE_PARAMS: + case V4L2_CTRL_TYPE_H264_DECODE_PARAMS: + return 0; + default: return -EINVAL; } @@ -2149,15 +2218,6 @@ static int handler_new_ref(struct v4l2_ctrl_handler *hdl, if (size_extra_req) new_ref->p_req.p = &new_ref[1]; - if (ctrl->handler == hdl) { - /* By default each control starts in a cluster of its own. - new_ref->ctrl is basically a cluster array with one - element, so that's perfect to use as the cluster pointer. - But only do this for the handler that owns the control. */ - ctrl->cluster = &new_ref->ctrl; - ctrl->ncontrols = 1; - } - INIT_LIST_HEAD(&new_ref->node); mutex_lock(hdl->lock); @@ -2190,6 +2250,15 @@ insert_in_hash: hdl->buckets[bucket] = new_ref; if (ctrl_ref) *ctrl_ref = new_ref; + if (ctrl->handler == hdl) { + /* By default each control starts in a cluster of its own. + * new_ref->ctrl is basically a cluster array with one + * element, so that's perfect to use as the cluster pointer. + * But only do this for the handler that owns the control. + */ + ctrl->cluster = &new_ref->ctrl; + ctrl->ncontrols = 1; + } unlock: mutex_unlock(hdl->lock); @@ -2253,6 +2322,21 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, case V4L2_CTRL_TYPE_FWHT_PARAMS: elem_size = sizeof(struct v4l2_ctrl_fwht_params); break; + case V4L2_CTRL_TYPE_H264_SPS: + elem_size = sizeof(struct v4l2_ctrl_h264_sps); + break; + case V4L2_CTRL_TYPE_H264_PPS: + elem_size = sizeof(struct v4l2_ctrl_h264_pps); + break; + case V4L2_CTRL_TYPE_H264_SCALING_MATRIX: + elem_size = sizeof(struct v4l2_ctrl_h264_scaling_matrix); + break; + case V4L2_CTRL_TYPE_H264_SLICE_PARAMS: + elem_size = sizeof(struct v4l2_ctrl_h264_slice_params); + break; + case V4L2_CTRL_TYPE_H264_DECODE_PARAMS: + elem_size = sizeof(struct v4l2_ctrl_h264_decode_params); + break; default: if (type < V4L2_CTRL_COMPOUND_TYPES) elem_size = sizeof(s32); diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index 414636dedffd..cbb74f748555 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -589,11 +589,9 @@ static void determine_valid_ioctls(struct video_device *vdev) if (is_vid || is_tch) { /* video and metadata specific ioctls */ if ((is_rx && (ops->vidioc_enum_fmt_vid_cap || - ops->vidioc_enum_fmt_vid_cap_mplane || ops->vidioc_enum_fmt_vid_overlay || ops->vidioc_enum_fmt_meta_cap)) || (is_tx && (ops->vidioc_enum_fmt_vid_out || - ops->vidioc_enum_fmt_vid_out_mplane || ops->vidioc_enum_fmt_meta_out))) set_bit(_IOC_NR(VIDIOC_ENUM_FMT), valid_ioctls); if ((is_rx && (ops->vidioc_g_fmt_vid_cap || diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index c2d980ab3af7..44e9bcb67935 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -209,10 +209,10 @@ static int v4l2_fwnode_endpoint_parse_csi2_bus(struct fwnode_handle *fwnode, have_clk_lane = true; } - if (lanes_used & BIT(clock_lane)) { - if (have_clk_lane || !use_default_lane_mapping) - pr_warn("duplicated lane %u in clock-lanes, using defaults\n", - v); + if (have_clk_lane && lanes_used & BIT(clock_lane) && + !use_default_lane_mapping) { + pr_warn("duplicated lane %u in clock-lanes, using defaults\n", + v); use_default_lane_mapping = true; } diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 6859bdac86fe..b1f4b991dba6 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1321,6 +1321,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_PIX_FMT_H264: descr = "H.264"; break; case V4L2_PIX_FMT_H264_NO_SC: descr = "H.264 (No Start Codes)"; break; case V4L2_PIX_FMT_H264_MVC: descr = "H.264 MVC"; break; + case V4L2_PIX_FMT_H264_SLICE_RAW: descr = "H.264 Parsed Slice Data"; break; case V4L2_PIX_FMT_H263: descr = "H.263"; break; case V4L2_PIX_FMT_MPEG1: descr = "MPEG-1 ES"; break; case V4L2_PIX_FMT_MPEG2: descr = "MPEG-2 ES"; break; @@ -1377,8 +1378,10 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { + struct video_device *vdev = video_devdata(file); struct v4l2_fmtdesc *p = arg; int ret = check_fmt(file, p->type); + u32 cap_mask; if (ret) return ret; @@ -1386,30 +1389,34 @@ static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops, switch (p->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + cap_mask = V4L2_CAP_VIDEO_CAPTURE_MPLANE | + V4L2_CAP_VIDEO_M2M_MPLANE; + if (!!(vdev->device_caps & cap_mask) != + (p->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)) + break; + if (unlikely(!ops->vidioc_enum_fmt_vid_cap)) break; ret = ops->vidioc_enum_fmt_vid_cap(file, fh, arg); break; - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - if (unlikely(!ops->vidioc_enum_fmt_vid_cap_mplane)) - break; - ret = ops->vidioc_enum_fmt_vid_cap_mplane(file, fh, arg); - break; case V4L2_BUF_TYPE_VIDEO_OVERLAY: if (unlikely(!ops->vidioc_enum_fmt_vid_overlay)) break; ret = ops->vidioc_enum_fmt_vid_overlay(file, fh, arg); break; case V4L2_BUF_TYPE_VIDEO_OUTPUT: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + cap_mask = V4L2_CAP_VIDEO_OUTPUT_MPLANE | + V4L2_CAP_VIDEO_M2M_MPLANE; + if (!!(vdev->device_caps & cap_mask) != + (p->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)) + break; + if (unlikely(!ops->vidioc_enum_fmt_vid_out)) break; ret = ops->vidioc_enum_fmt_vid_out(file, fh, arg); break; - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - if (unlikely(!ops->vidioc_enum_fmt_vid_out_mplane)) - break; - ret = ops->vidioc_enum_fmt_vid_out_mplane(file, fh, arg); - break; case V4L2_BUF_TYPE_SDR_CAPTURE: if (unlikely(!ops->vidioc_enum_fmt_sdr_cap)) break; diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index fd96df98c780..4f5176702937 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -1118,6 +1118,35 @@ int v4l2_m2m_ioctl_streamoff(struct file *file, void *priv, } EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_streamoff); +int v4l2_m2m_ioctl_try_encoder_cmd(struct file *file, void *fh, + struct v4l2_encoder_cmd *ec) +{ + if (ec->cmd != V4L2_ENC_CMD_STOP && ec->cmd != V4L2_ENC_CMD_START) + return -EINVAL; + + ec->flags = 0; + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_try_encoder_cmd); + +int v4l2_m2m_ioctl_try_decoder_cmd(struct file *file, void *fh, + struct v4l2_decoder_cmd *dc) +{ + if (dc->cmd != V4L2_DEC_CMD_STOP && dc->cmd != V4L2_DEC_CMD_START) + return -EINVAL; + + dc->flags = 0; + + if (dc->cmd == V4L2_DEC_CMD_STOP) { + dc->stop.pts = 0; + } else if (dc->cmd == V4L2_DEC_CMD_START) { + dc->start.speed = 0; + dc->start.format = V4L2_DEC_START_FMT_NONE; + } + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_try_decoder_cmd); + /* * v4l2_file_operations helpers. It is assumed here same lock is used * for the output and the capture buffer queue. diff --git a/drivers/media/v4l2-core/videobuf-dma-contig.c b/drivers/media/v4l2-core/videobuf-dma-contig.c index e1bf50df4c70..65e2655d22b7 100644 --- a/drivers/media/v4l2-core/videobuf-dma-contig.c +++ b/drivers/media/v4l2-core/videobuf-dma-contig.c @@ -280,7 +280,6 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, struct videobuf_dma_contig_memory *mem; struct videobuf_mapping *map; int retval; - unsigned long size; dev_dbg(q->dev, "%s\n", __func__); @@ -303,7 +302,6 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, goto error; /* Try to remap memory */ - size = vma->vm_end - vma->vm_start; vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); /* the "vm_pgoff" is just used in v4l2 to find the @@ -314,7 +312,7 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, */ vma->vm_pgoff = 0; - retval = vm_iomap_memory(vma, mem->dma_handle, size); + retval = vm_iomap_memory(vma, mem->dma_handle, mem->size); if (retval) { dev_err(q->dev, "mmap: remap failed with error %d. ", retval); diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index f77f5eee7fc2..7212762035b4 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -20,12 +20,16 @@ menuconfig STAGING_MEDIA if STAGING_MEDIA && MEDIA_SUPPORT # Please keep them in alphabetic order +source "drivers/staging/media/allegro-dvt/Kconfig" + source "drivers/staging/media/bcm2048/Kconfig" source "drivers/staging/media/davinci_vpfe/Kconfig" source "drivers/staging/media/imx/Kconfig" +source "drivers/staging/media/meson/vdec/Kconfig" + source "drivers/staging/media/omap4iss/Kconfig" source "drivers/staging/media/rockchip/vpu/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 99218bfc997f..4222584a9bcb 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -1,7 +1,9 @@ # SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_VIDEO_ALLEGRO_DVT) += allegro-dvt/ obj-$(CONFIG_I2C_BCM2048) += bcm2048/ obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx/ obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/ +obj-$(CONFIG_VIDEO_MESON_VDEC) += meson/vdec/ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ obj-$(CONFIG_VIDEO_SUNXI) += sunxi/ obj-$(CONFIG_TEGRA_VDE) += tegra-vde/ diff --git a/drivers/staging/media/allegro-dvt/Kconfig b/drivers/staging/media/allegro-dvt/Kconfig new file mode 100644 index 000000000000..6b7107d9995c --- /dev/null +++ b/drivers/staging/media/allegro-dvt/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0 +config VIDEO_ALLEGRO_DVT + tristate "Allegro DVT Video IP Core" + depends on VIDEO_DEV && VIDEO_V4L2 + depends on ARCH_ZYNQMP || COMPILE_TEST + select V4L2_MEM2MEM_DEV + select VIDEOBUF2_DMA_CONTIG + select REGMAP + select REGMAP_MMIO + help + Support for the encoder video IP core by Allegro DVT. This core is + found for example on the Xilinx ZynqMP SoC in the EV family and is + called VCU in the reference manual. + + To compile this driver as a module, choose M here: the module + will be called allegro. diff --git a/drivers/staging/media/allegro-dvt/Makefile b/drivers/staging/media/allegro-dvt/Makefile new file mode 100644 index 000000000000..80817160815c --- /dev/null +++ b/drivers/staging/media/allegro-dvt/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 + +allegro-objs := allegro-core.o nal-h264.o + +obj-$(CONFIG_VIDEO_ALLEGRO_DVT) += allegro.o diff --git a/drivers/staging/media/allegro-dvt/TODO b/drivers/staging/media/allegro-dvt/TODO new file mode 100644 index 000000000000..99e19be0e45a --- /dev/null +++ b/drivers/staging/media/allegro-dvt/TODO @@ -0,0 +1,4 @@ +TODO: + +- This driver is waiting for the stateful encoder spec and corresponding + v4l2-compliance tests to be finalized. diff --git a/drivers/staging/media/allegro-dvt/allegro-core.c b/drivers/staging/media/allegro-dvt/allegro-core.c new file mode 100644 index 000000000000..20b38b737869 --- /dev/null +++ b/drivers/staging/media/allegro-dvt/allegro-core.c @@ -0,0 +1,3032 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Pengutronix, Michael Tretter <kernel@pengutronix.de> + * + * Allegro DVT video encoder driver + */ + +#include <linux/firmware.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/log2.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/sizes.h> +#include <linux/slab.h> +#include <linux/videodev2.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-mem2mem.h> +#include <media/videobuf2-dma-contig.h> +#include <media/videobuf2-v4l2.h> + +#include "nal-h264.h" + +/* + * Support up to 4k video streams. The hardware actually supports higher + * resolutions, which are specified in PG252 June 6, 2018 (H.264/H.265 Video + * Codec Unit v1.1) Chapter 3. + */ +#define ALLEGRO_WIDTH_MIN 128 +#define ALLEGRO_WIDTH_DEFAULT 1920 +#define ALLEGRO_WIDTH_MAX 3840 +#define ALLEGRO_HEIGHT_MIN 64 +#define ALLEGRO_HEIGHT_DEFAULT 1080 +#define ALLEGRO_HEIGHT_MAX 2160 + +#define ALLEGRO_GOP_SIZE_DEFAULT 25 +#define ALLEGRO_GOP_SIZE_MAX 1000 + +/* + * MCU Control Registers + * + * The Zynq UltraScale+ Devices Register Reference documents the registers + * with an offset of 0x9000, which equals the size of the SRAM and one page + * gap. The driver handles SRAM and registers separately and, therefore, is + * oblivious of the offset. + */ +#define AL5_MCU_RESET 0x0000 +#define AL5_MCU_RESET_SOFT BIT(0) +#define AL5_MCU_RESET_REGS BIT(1) +#define AL5_MCU_RESET_MODE 0x0004 +#define AL5_MCU_RESET_MODE_SLEEP BIT(0) +#define AL5_MCU_RESET_MODE_HALT BIT(1) +#define AL5_MCU_STA 0x0008 +#define AL5_MCU_STA_SLEEP BIT(0) +#define AL5_MCU_WAKEUP 0x000c + +#define AL5_ICACHE_ADDR_OFFSET_MSB 0x0010 +#define AL5_ICACHE_ADDR_OFFSET_LSB 0x0014 +#define AL5_DCACHE_ADDR_OFFSET_MSB 0x0018 +#define AL5_DCACHE_ADDR_OFFSET_LSB 0x001c + +#define AL5_MCU_INTERRUPT 0x0100 +#define AL5_ITC_CPU_IRQ_MSK 0x0104 +#define AL5_ITC_CPU_IRQ_CLR 0x0108 +#define AL5_ITC_CPU_IRQ_STA 0x010C +#define AL5_ITC_CPU_IRQ_STA_TRIGGERED BIT(0) + +#define AXI_ADDR_OFFSET_IP 0x0208 + +/* + * The MCU accesses the system memory with a 2G offset compared to CPU + * physical addresses. + */ +#define MCU_CACHE_OFFSET SZ_2G + +/* + * The driver needs to reserve some space at the beginning of capture buffers, + * because it needs to write SPS/PPS NAL units. The encoder writes the actual + * frame data after the offset. + */ +#define ENCODER_STREAM_OFFSET SZ_64 + +#define SIZE_MACROBLOCK 16 + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-2)"); + +struct allegro_buffer { + void *vaddr; + dma_addr_t paddr; + size_t size; + struct list_head head; +}; + +struct allegro_channel; + +struct allegro_mbox { + unsigned int head; + unsigned int tail; + unsigned int data; + size_t size; + /* protect mailbox from simultaneous accesses */ + struct mutex lock; +}; + +struct allegro_dev { + struct v4l2_device v4l2_dev; + struct video_device video_dev; + struct v4l2_m2m_dev *m2m_dev; + struct platform_device *plat_dev; + + /* mutex protecting vb2_queue structure */ + struct mutex lock; + + struct regmap *regmap; + struct regmap *sram; + + struct allegro_buffer firmware; + struct allegro_buffer suballocator; + + struct completion init_complete; + + /* The mailbox interface */ + struct allegro_mbox mbox_command; + struct allegro_mbox mbox_status; + + /* + * The downstream driver limits the users to 64 users, thus I can use + * a bitfield for the user_ids that are in use. See also user_id in + * struct allegro_channel. + */ + unsigned long channel_user_ids; + struct list_head channels; +}; + +static struct regmap_config allegro_regmap_config = { + .name = "regmap", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0xfff, + .cache_type = REGCACHE_NONE, +}; + +static struct regmap_config allegro_sram_config = { + .name = "sram", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x7fff, + .cache_type = REGCACHE_NONE, +}; + +enum allegro_state { + ALLEGRO_STATE_ENCODING, + ALLEGRO_STATE_DRAIN, + ALLEGRO_STATE_WAIT_FOR_BUFFER, + ALLEGRO_STATE_STOPPED, +}; + +#define fh_to_channel(__fh) container_of(__fh, struct allegro_channel, fh) + +struct allegro_channel { + struct allegro_dev *dev; + struct v4l2_fh fh; + struct v4l2_ctrl_handler ctrl_handler; + + unsigned int width; + unsigned int height; + unsigned int stride; + + enum v4l2_colorspace colorspace; + enum v4l2_ycbcr_encoding ycbcr_enc; + enum v4l2_quantization quantization; + enum v4l2_xfer_func xfer_func; + + u32 pixelformat; + unsigned int sizeimage_raw; + unsigned int osequence; + + u32 codec; + enum v4l2_mpeg_video_h264_profile profile; + enum v4l2_mpeg_video_h264_level level; + unsigned int sizeimage_encoded; + unsigned int csequence; + + enum v4l2_mpeg_video_bitrate_mode bitrate_mode; + unsigned int bitrate; + unsigned int bitrate_peak; + unsigned int cpb_size; + unsigned int gop_size; + + struct v4l2_ctrl *mpeg_video_h264_profile; + struct v4l2_ctrl *mpeg_video_h264_level; + struct v4l2_ctrl *mpeg_video_bitrate_mode; + struct v4l2_ctrl *mpeg_video_bitrate; + struct v4l2_ctrl *mpeg_video_bitrate_peak; + struct v4l2_ctrl *mpeg_video_cpb_size; + struct v4l2_ctrl *mpeg_video_gop_size; + + /* user_id is used to identify the channel during CREATE_CHANNEL */ + /* not sure, what to set here and if this is actually required */ + int user_id; + /* channel_id is set by the mcu and used by all later commands */ + int mcu_channel_id; + + struct list_head buffers_reference; + struct list_head buffers_intermediate; + + struct list_head list; + struct completion completion; + + unsigned int error; + enum allegro_state state; +}; + +static inline int +allegro_set_state(struct allegro_channel *channel, enum allegro_state state) +{ + channel->state = state; + + return 0; +} + +static inline enum allegro_state +allegro_get_state(struct allegro_channel *channel) +{ + return channel->state; +} + +struct fw_info { + unsigned int id; + unsigned int id_codec; + char *version; + unsigned int mailbox_cmd; + unsigned int mailbox_status; + size_t mailbox_size; + size_t suballocator_size; +}; + +static const struct fw_info supported_firmware[] = { + { + .id = 18296, + .id_codec = 96272, + .version = "v2018.2", + .mailbox_cmd = 0x7800, + .mailbox_status = 0x7c00, + .mailbox_size = 0x400 - 0x8, + .suballocator_size = SZ_16M, + }, +}; + +enum mcu_msg_type { + MCU_MSG_TYPE_INIT = 0x0000, + MCU_MSG_TYPE_CREATE_CHANNEL = 0x0005, + MCU_MSG_TYPE_DESTROY_CHANNEL = 0x0006, + MCU_MSG_TYPE_ENCODE_FRAME = 0x0007, + MCU_MSG_TYPE_PUT_STREAM_BUFFER = 0x0012, + MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE = 0x000e, + MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE = 0x000f, +}; + +static const char *msg_type_name(enum mcu_msg_type type) +{ + static char buf[9]; + + switch (type) { + case MCU_MSG_TYPE_INIT: + return "INIT"; + case MCU_MSG_TYPE_CREATE_CHANNEL: + return "CREATE_CHANNEL"; + case MCU_MSG_TYPE_DESTROY_CHANNEL: + return "DESTROY_CHANNEL"; + case MCU_MSG_TYPE_ENCODE_FRAME: + return "ENCODE_FRAME"; + case MCU_MSG_TYPE_PUT_STREAM_BUFFER: + return "PUT_STREAM_BUFFER"; + case MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE: + return "PUSH_BUFFER_INTERMEDIATE"; + case MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE: + return "PUSH_BUFFER_REFERENCE"; + default: + snprintf(buf, sizeof(buf), "(0x%04x)", type); + return buf; + } +} + +struct mcu_msg_header { + u16 length; /* length of the body in bytes */ + u16 type; +} __attribute__ ((__packed__)); + +struct mcu_msg_init_request { + struct mcu_msg_header header; + u32 reserved0; /* maybe a unused channel id */ + u32 suballoc_dma; + u32 suballoc_size; + s32 l2_cache[3]; +} __attribute__ ((__packed__)); + +struct mcu_msg_init_response { + struct mcu_msg_header header; + u32 reserved0; +} __attribute__ ((__packed__)); + +struct mcu_msg_create_channel { + struct mcu_msg_header header; + u32 user_id; + u16 width; + u16 height; + u32 format; + u32 colorspace; + u32 src_mode; + u8 profile; + u16 constraint_set_flags; + s8 codec; + u16 level; + u16 tier; + u32 sps_param; + u32 pps_param; + + u32 enc_option; +#define AL_OPT_WPP BIT(0) +#define AL_OPT_TILE BIT(1) +#define AL_OPT_LF BIT(2) +#define AL_OPT_LF_X_SLICE BIT(3) +#define AL_OPT_LF_X_TILE BIT(4) +#define AL_OPT_SCL_LST BIT(5) +#define AL_OPT_CONST_INTRA_PRED BIT(6) +#define AL_OPT_QP_TAB_RELATIVE BIT(7) +#define AL_OPT_FIX_PREDICTOR BIT(8) +#define AL_OPT_CUSTOM_LDA BIT(9) +#define AL_OPT_ENABLE_AUTO_QP BIT(10) +#define AL_OPT_ADAPT_AUTO_QP BIT(11) +#define AL_OPT_TRANSFO_SKIP BIT(13) +#define AL_OPT_FORCE_REC BIT(15) +#define AL_OPT_FORCE_MV_OUT BIT(16) +#define AL_OPT_FORCE_MV_CLIP BIT(17) +#define AL_OPT_LOWLAT_SYNC BIT(18) +#define AL_OPT_LOWLAT_INT BIT(19) +#define AL_OPT_RDO_COST_MODE BIT(20) + + s8 beta_offset; + s8 tc_offset; + u16 reserved10; + u32 unknown11; + u32 unknown12; + u16 num_slices; + u16 prefetch_auto; + u32 prefetch_mem_offset; + u32 prefetch_mem_size; + u16 clip_hrz_range; + u16 clip_vrt_range; + u16 me_range[4]; + u8 max_cu_size; + u8 min_cu_size; + u8 max_tu_size; + u8 min_tu_size; + u8 max_transfo_depth_inter; + u8 max_transfo_depth_intra; + u16 reserved20; + u32 entropy_mode; + u32 wp_mode; + + /* rate control param */ + u32 rate_control_mode; + u32 initial_rem_delay; + u32 cpb_size; + u16 framerate; + u16 clk_ratio; + u32 target_bitrate; + u32 max_bitrate; + u16 initial_qp; + u16 min_qp; + u16 max_qp; + s16 ip_delta; + s16 pb_delta; + u16 golden_ref; + u16 golden_delta; + u16 golden_ref_frequency; + u32 rate_control_option; + + /* gop param */ + u32 gop_ctrl_mode; + u32 freq_ird; + u32 freq_lt; + u32 gdr_mode; + u32 gop_length; + u32 unknown39; + + u32 subframe_latency; + u32 lda_control_mode; +} __attribute__ ((__packed__)); + +struct mcu_msg_create_channel_response { + struct mcu_msg_header header; + u32 channel_id; + u32 user_id; + u32 options; + u32 num_core; + u32 pps_param; + u32 int_buffers_count; + u32 int_buffers_size; + u32 rec_buffers_count; + u32 rec_buffers_size; + u32 reserved; + u32 error_code; +} __attribute__ ((__packed__)); + +struct mcu_msg_destroy_channel { + struct mcu_msg_header header; + u32 channel_id; +} __attribute__ ((__packed__)); + +struct mcu_msg_destroy_channel_response { + struct mcu_msg_header header; + u32 channel_id; +} __attribute__ ((__packed__)); + +struct mcu_msg_push_buffers_internal_buffer { + u32 dma_addr; + u32 mcu_addr; + u32 size; +} __attribute__ ((__packed__)); + +struct mcu_msg_push_buffers_internal { + struct mcu_msg_header header; + u32 channel_id; + struct mcu_msg_push_buffers_internal_buffer buffer[0]; +} __attribute__ ((__packed__)); + +struct mcu_msg_put_stream_buffer { + struct mcu_msg_header header; + u32 channel_id; + u32 dma_addr; + u32 mcu_addr; + u32 size; + u32 offset; + u64 stream_id; +} __attribute__ ((__packed__)); + +struct mcu_msg_encode_frame { + struct mcu_msg_header header; + u32 channel_id; + u32 reserved; + + u32 encoding_options; +#define AL_OPT_USE_QP_TABLE BIT(0) +#define AL_OPT_FORCE_LOAD BIT(1) +#define AL_OPT_USE_L2 BIT(2) +#define AL_OPT_DISABLE_INTRA BIT(3) +#define AL_OPT_DEPENDENT_SLICES BIT(4) + + s16 pps_qp; + u16 padding; + u64 user_param; + u64 src_handle; + + u32 request_options; +#define AL_OPT_SCENE_CHANGE BIT(0) +#define AL_OPT_RESTART_GOP BIT(1) +#define AL_OPT_USE_LONG_TERM BIT(2) +#define AL_OPT_UPDATE_PARAMS BIT(3) + + /* u32 scene_change_delay (optional) */ + /* rate control param (optional) */ + /* gop param (optional) */ + u32 src_y; + u32 src_uv; + u32 stride; + u32 ep2; + u64 ep2_v; +} __attribute__ ((__packed__)); + +struct mcu_msg_encode_frame_response { + struct mcu_msg_header header; + u32 channel_id; + u64 stream_id; /* see mcu_msg_put_stream_buffer */ + u64 user_param; /* see mcu_msg_encode_frame */ + u64 src_handle; /* see mcu_msg_encode_frame */ + u16 skip; + u16 is_ref; + u32 initial_removal_delay; + u32 dpb_output_delay; + u32 size; + u32 frame_tag_size; + s32 stuffing; + s32 filler; + u16 num_column; + u16 num_row; + u16 qp; + u8 num_ref_idx_l0; + u8 num_ref_idx_l1; + u32 partition_table_offset; + s32 partition_table_size; + u32 sum_complex; + s32 tile_width[4]; + s32 tile_height[22]; + u32 error_code; + + u32 slice_type; +#define AL_ENC_SLICE_TYPE_B 0 +#define AL_ENC_SLICE_TYPE_P 1 +#define AL_ENC_SLICE_TYPE_I 2 + + u32 pic_struct; + u8 is_idr; + u8 is_first_slice; + u8 is_last_slice; + u8 reserved; + u16 pps_qp; + u16 reserved1; + u32 reserved2; +} __attribute__ ((__packed__)); + +union mcu_msg_response { + struct mcu_msg_header header; + struct mcu_msg_init_response init; + struct mcu_msg_create_channel_response create_channel; + struct mcu_msg_destroy_channel_response destroy_channel; + struct mcu_msg_encode_frame_response encode_frame; +}; + +/* Helper functions for channel and user operations */ + +static unsigned long allegro_next_user_id(struct allegro_dev *dev) +{ + if (dev->channel_user_ids == ~0UL) + return -EBUSY; + + return ffz(dev->channel_user_ids); +} + +static struct allegro_channel * +allegro_find_channel_by_user_id(struct allegro_dev *dev, + unsigned int user_id) +{ + struct allegro_channel *channel; + + list_for_each_entry(channel, &dev->channels, list) { + if (channel->user_id == user_id) + return channel; + } + + return ERR_PTR(-EINVAL); +} + +static struct allegro_channel * +allegro_find_channel_by_channel_id(struct allegro_dev *dev, + unsigned int channel_id) +{ + struct allegro_channel *channel; + + list_for_each_entry(channel, &dev->channels, list) { + if (channel->mcu_channel_id == channel_id) + return channel; + } + + return ERR_PTR(-EINVAL); +} + +static inline bool channel_exists(struct allegro_channel *channel) +{ + return channel->mcu_channel_id != -1; +} + +static unsigned int estimate_stream_size(unsigned int width, + unsigned int height) +{ + unsigned int offset = ENCODER_STREAM_OFFSET; + unsigned int num_blocks = DIV_ROUND_UP(width, SIZE_MACROBLOCK) * + DIV_ROUND_UP(height, SIZE_MACROBLOCK); + unsigned int pcm_size = SZ_256; + unsigned int partition_table = SZ_256; + + return round_up(offset + num_blocks * pcm_size + partition_table, 32); +} + +static enum v4l2_mpeg_video_h264_level +select_minimum_h264_level(unsigned int width, unsigned int height) +{ + unsigned int pic_width_in_mb = DIV_ROUND_UP(width, SIZE_MACROBLOCK); + unsigned int frame_height_in_mb = DIV_ROUND_UP(height, SIZE_MACROBLOCK); + unsigned int frame_size_in_mb = pic_width_in_mb * frame_height_in_mb; + enum v4l2_mpeg_video_h264_level level = V4L2_MPEG_VIDEO_H264_LEVEL_4_0; + + /* + * The level limits are specified in Rec. ITU-T H.264 Annex A.3.1 and + * also specify limits regarding bit rate and CBP size. Only approximate + * the levels using the frame size. + * + * Level 5.1 allows up to 4k video resolution. + */ + if (frame_size_in_mb <= 99) + level = V4L2_MPEG_VIDEO_H264_LEVEL_1_0; + else if (frame_size_in_mb <= 396) + level = V4L2_MPEG_VIDEO_H264_LEVEL_1_1; + else if (frame_size_in_mb <= 792) + level = V4L2_MPEG_VIDEO_H264_LEVEL_2_1; + else if (frame_size_in_mb <= 1620) + level = V4L2_MPEG_VIDEO_H264_LEVEL_2_2; + else if (frame_size_in_mb <= 3600) + level = V4L2_MPEG_VIDEO_H264_LEVEL_3_1; + else if (frame_size_in_mb <= 5120) + level = V4L2_MPEG_VIDEO_H264_LEVEL_3_2; + else if (frame_size_in_mb <= 8192) + level = V4L2_MPEG_VIDEO_H264_LEVEL_4_0; + else if (frame_size_in_mb <= 8704) + level = V4L2_MPEG_VIDEO_H264_LEVEL_4_2; + else if (frame_size_in_mb <= 22080) + level = V4L2_MPEG_VIDEO_H264_LEVEL_5_0; + else + level = V4L2_MPEG_VIDEO_H264_LEVEL_5_1; + + return level; +} + +static unsigned int maximum_bitrate(enum v4l2_mpeg_video_h264_level level) +{ + switch (level) { + case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: + return 64000; + case V4L2_MPEG_VIDEO_H264_LEVEL_1B: + return 128000; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: + return 192000; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: + return 384000; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: + return 768000; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: + return 2000000; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: + return 4000000; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: + return 4000000; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: + return 10000000; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: + return 14000000; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: + return 20000000; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: + return 20000000; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: + return 50000000; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_2: + return 50000000; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: + return 135000000; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: + default: + return 240000000; + } +} + +static unsigned int maximum_cpb_size(enum v4l2_mpeg_video_h264_level level) +{ + switch (level) { + case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: + return 175; + case V4L2_MPEG_VIDEO_H264_LEVEL_1B: + return 350; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: + return 500; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: + return 1000; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: + return 2000; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: + return 2000; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: + return 4000; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: + return 4000; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: + return 10000; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: + return 14000; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: + return 20000; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: + return 25000; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: + return 62500; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_2: + return 62500; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: + return 135000; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: + default: + return 240000; + } +} + +static const struct fw_info * +allegro_get_firmware_info(struct allegro_dev *dev, + const struct firmware *fw, + const struct firmware *fw_codec) +{ + int i; + unsigned int id = fw->size; + unsigned int id_codec = fw_codec->size; + + for (i = 0; i < ARRAY_SIZE(supported_firmware); i++) + if (supported_firmware[i].id == id && + supported_firmware[i].id_codec == id_codec) + return &supported_firmware[i]; + + return NULL; +} + +/* + * Buffers that are used internally by the MCU. + */ + +static int allegro_alloc_buffer(struct allegro_dev *dev, + struct allegro_buffer *buffer, size_t size) +{ + buffer->vaddr = dma_alloc_coherent(&dev->plat_dev->dev, size, + &buffer->paddr, GFP_KERNEL); + if (!buffer->vaddr) + return -ENOMEM; + buffer->size = size; + + return 0; +} + +static void allegro_free_buffer(struct allegro_dev *dev, + struct allegro_buffer *buffer) +{ + if (buffer->vaddr) { + dma_free_coherent(&dev->plat_dev->dev, buffer->size, + buffer->vaddr, buffer->paddr); + buffer->vaddr = NULL; + buffer->size = 0; + } +} + +/* + * Mailbox interface to send messages to the MCU. + */ + +static int allegro_mbox_init(struct allegro_dev *dev, + struct allegro_mbox *mbox, + unsigned int base, size_t size) +{ + if (!mbox) + return -EINVAL; + + mbox->head = base; + mbox->tail = base + 0x4; + mbox->data = base + 0x8; + mbox->size = size; + mutex_init(&mbox->lock); + + regmap_write(dev->sram, mbox->head, 0); + regmap_write(dev->sram, mbox->tail, 0); + + return 0; +} + +static int allegro_mbox_write(struct allegro_dev *dev, + struct allegro_mbox *mbox, void *src, size_t size) +{ + struct mcu_msg_header *header = src; + unsigned int tail; + size_t size_no_wrap; + int err = 0; + + if (!src) + return -EINVAL; + + if (size > mbox->size) { + v4l2_err(&dev->v4l2_dev, + "message (%zu bytes) to large for mailbox (%zu bytes)\n", + size, mbox->size); + return -EINVAL; + } + + if (header->length != size - sizeof(*header)) { + v4l2_err(&dev->v4l2_dev, + "invalid message length: %u bytes (expected %zu bytes)\n", + header->length, size - sizeof(*header)); + return -EINVAL; + } + + v4l2_dbg(2, debug, &dev->v4l2_dev, + "write command message: type %s, body length %d\n", + msg_type_name(header->type), header->length); + + mutex_lock(&mbox->lock); + regmap_read(dev->sram, mbox->tail, &tail); + if (tail > mbox->size) { + v4l2_err(&dev->v4l2_dev, + "invalid tail (0x%x): must be smaller than mailbox size (0x%zx)\n", + tail, mbox->size); + err = -EIO; + goto out; + } + size_no_wrap = min(size, mbox->size - (size_t)tail); + regmap_bulk_write(dev->sram, mbox->data + tail, src, size_no_wrap / 4); + regmap_bulk_write(dev->sram, mbox->data, + src + size_no_wrap, (size - size_no_wrap) / 4); + regmap_write(dev->sram, mbox->tail, (tail + size) % mbox->size); + +out: + mutex_unlock(&mbox->lock); + + return err; +} + +static ssize_t allegro_mbox_read(struct allegro_dev *dev, + struct allegro_mbox *mbox, + void *dst, size_t nbyte) +{ + struct mcu_msg_header *header; + unsigned int head; + ssize_t size; + size_t body_no_wrap; + + regmap_read(dev->sram, mbox->head, &head); + if (head > mbox->size) { + v4l2_err(&dev->v4l2_dev, + "invalid head (0x%x): must be smaller than mailbox size (0x%zx)\n", + head, mbox->size); + return -EIO; + } + + /* Assume that the header does not wrap. */ + regmap_bulk_read(dev->sram, mbox->data + head, + dst, sizeof(*header) / 4); + header = dst; + size = header->length + sizeof(*header); + if (size > mbox->size || size & 0x3) { + v4l2_err(&dev->v4l2_dev, + "invalid message length: %zu bytes (maximum %zu bytes)\n", + header->length + sizeof(*header), mbox->size); + return -EIO; + } + if (size > nbyte) { + v4l2_err(&dev->v4l2_dev, + "destination buffer too small: %zu bytes (need %zu bytes)\n", + nbyte, size); + return -EINVAL; + } + + /* + * The message might wrap within the mailbox. If the message does not + * wrap, the first read will read the entire message, otherwise the + * first read will read message until the end of the mailbox and the + * second read will read the remaining bytes from the beginning of the + * mailbox. + * + * Skip the header, as was already read to get the size of the body. + */ + body_no_wrap = min((size_t)header->length, + (size_t)(mbox->size - (head + sizeof(*header)))); + regmap_bulk_read(dev->sram, mbox->data + head + sizeof(*header), + dst + sizeof(*header), body_no_wrap / 4); + regmap_bulk_read(dev->sram, mbox->data, + dst + sizeof(*header) + body_no_wrap, + (header->length - body_no_wrap) / 4); + + regmap_write(dev->sram, mbox->head, (head + size) % mbox->size); + + v4l2_dbg(2, debug, &dev->v4l2_dev, + "read status message: type %s, body length %d\n", + msg_type_name(header->type), header->length); + + return size; +} + +static void allegro_mcu_interrupt(struct allegro_dev *dev) +{ + regmap_write(dev->regmap, AL5_MCU_INTERRUPT, BIT(0)); +} + +static void allegro_mcu_send_init(struct allegro_dev *dev, + dma_addr_t suballoc_dma, size_t suballoc_size) +{ + struct mcu_msg_init_request msg; + + memset(&msg, 0, sizeof(msg)); + + msg.header.type = MCU_MSG_TYPE_INIT; + msg.header.length = sizeof(msg) - sizeof(msg.header); + + msg.suballoc_dma = lower_32_bits(suballoc_dma) | MCU_CACHE_OFFSET; + msg.suballoc_size = suballoc_size; + + /* disable L2 cache */ + msg.l2_cache[0] = -1; + msg.l2_cache[1] = -1; + msg.l2_cache[2] = -1; + + allegro_mbox_write(dev, &dev->mbox_command, &msg, sizeof(msg)); + allegro_mcu_interrupt(dev); +} + +static u32 v4l2_pixelformat_to_mcu_format(u32 pixelformat) +{ + switch (pixelformat) { + case V4L2_PIX_FMT_NV12: + /* AL_420_8BITS: 0x100 -> NV12, 0x88 -> 8 bit */ + return 0x100 | 0x88; + default: + return -EINVAL; + } +} + +static u32 v4l2_colorspace_to_mcu_colorspace(enum v4l2_colorspace colorspace) +{ + switch (colorspace) { + case V4L2_COLORSPACE_REC709: + return 2; + case V4L2_COLORSPACE_SMPTE170M: + return 3; + case V4L2_COLORSPACE_SMPTE240M: + return 4; + case V4L2_COLORSPACE_SRGB: + return 7; + default: + /* UNKNOWN */ + return 0; + } +} + +static s8 v4l2_pixelformat_to_mcu_codec(u32 pixelformat) +{ + switch (pixelformat) { + case V4L2_PIX_FMT_H264: + default: + return 1; + } +} + +static u8 v4l2_profile_to_mcu_profile(enum v4l2_mpeg_video_h264_profile profile) +{ + switch (profile) { + case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: + default: + return 66; + } +} + +static u16 v4l2_level_to_mcu_level(enum v4l2_mpeg_video_h264_level level) +{ + switch (level) { + case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: + return 10; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: + return 11; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: + return 12; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: + return 13; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: + return 20; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: + return 21; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: + return 22; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: + return 30; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: + return 31; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: + return 32; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: + return 40; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: + return 41; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_2: + return 42; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: + return 50; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: + default: + return 51; + } +} + +static u32 +v4l2_bitrate_mode_to_mcu_mode(enum v4l2_mpeg_video_bitrate_mode mode) +{ + switch (mode) { + case V4L2_MPEG_VIDEO_BITRATE_MODE_VBR: + return 2; + case V4L2_MPEG_VIDEO_BITRATE_MODE_CBR: + default: + return 1; + } +} + +static int allegro_mcu_send_create_channel(struct allegro_dev *dev, + struct allegro_channel *channel) +{ + struct mcu_msg_create_channel msg; + + memset(&msg, 0, sizeof(msg)); + + msg.header.type = MCU_MSG_TYPE_CREATE_CHANNEL; + msg.header.length = sizeof(msg) - sizeof(msg.header); + + msg.user_id = channel->user_id; + msg.width = channel->width; + msg.height = channel->height; + msg.format = v4l2_pixelformat_to_mcu_format(channel->pixelformat); + msg.colorspace = v4l2_colorspace_to_mcu_colorspace(channel->colorspace); + msg.src_mode = 0x0; + msg.profile = v4l2_profile_to_mcu_profile(channel->profile); + msg.constraint_set_flags = BIT(1); + msg.codec = v4l2_pixelformat_to_mcu_codec(channel->codec); + msg.level = v4l2_level_to_mcu_level(channel->level); + msg.tier = 0; + msg.sps_param = BIT(20) | 0x4a; + msg.pps_param = BIT(2); + msg.enc_option = AL_OPT_RDO_COST_MODE | AL_OPT_LF_X_TILE | + AL_OPT_LF_X_SLICE | AL_OPT_LF; + msg.beta_offset = -1; + msg.tc_offset = -1; + msg.num_slices = 1; + msg.me_range[0] = 8; + msg.me_range[1] = 8; + msg.me_range[2] = 16; + msg.me_range[3] = 16; + msg.max_cu_size = ilog2(SIZE_MACROBLOCK); + msg.min_cu_size = ilog2(8); + msg.max_tu_size = 2; + msg.min_tu_size = 2; + msg.max_transfo_depth_intra = 1; + msg.max_transfo_depth_inter = 1; + + msg.rate_control_mode = + v4l2_bitrate_mode_to_mcu_mode(channel->bitrate_mode); + /* Shall be ]0;cpb_size in 90 kHz units]. Use maximum value. */ + msg.initial_rem_delay = + ((channel->cpb_size * 1000) / channel->bitrate_peak) * 90000; + /* Encoder expects cpb_size in units of a 90 kHz clock. */ + msg.cpb_size = + ((channel->cpb_size * 1000) / channel->bitrate_peak) * 90000; + msg.framerate = 25; + msg.clk_ratio = 1000; + msg.target_bitrate = channel->bitrate; + msg.max_bitrate = channel->bitrate_peak; + msg.initial_qp = 25; + msg.min_qp = 10; + msg.max_qp = 51; + msg.ip_delta = -1; + msg.pb_delta = -1; + msg.golden_ref = 0; + msg.golden_delta = 2; + msg.golden_ref_frequency = 10; + msg.rate_control_option = 0x00000000; + + msg.gop_ctrl_mode = 0x00000000; + msg.freq_ird = 0x7fffffff; + msg.freq_lt = 0; + msg.gdr_mode = 0x00000000; + msg.gop_length = channel->gop_size; + msg.subframe_latency = 0x00000000; + msg.lda_control_mode = 0x700d0000; + + allegro_mbox_write(dev, &dev->mbox_command, &msg, sizeof(msg)); + allegro_mcu_interrupt(dev); + + return 0; +} + +static int allegro_mcu_send_destroy_channel(struct allegro_dev *dev, + struct allegro_channel *channel) +{ + struct mcu_msg_destroy_channel msg; + + memset(&msg, 0, sizeof(msg)); + + msg.header.type = MCU_MSG_TYPE_DESTROY_CHANNEL; + msg.header.length = sizeof(msg) - sizeof(msg.header); + + msg.channel_id = channel->mcu_channel_id; + + allegro_mbox_write(dev, &dev->mbox_command, &msg, sizeof(msg)); + allegro_mcu_interrupt(dev); + + return 0; +} + +static int allegro_mcu_send_put_stream_buffer(struct allegro_dev *dev, + struct allegro_channel *channel, + dma_addr_t paddr, + unsigned long size) +{ + struct mcu_msg_put_stream_buffer msg; + + memset(&msg, 0, sizeof(msg)); + + msg.header.type = MCU_MSG_TYPE_PUT_STREAM_BUFFER; + msg.header.length = sizeof(msg) - sizeof(msg.header); + + msg.channel_id = channel->mcu_channel_id; + msg.dma_addr = paddr; + msg.mcu_addr = paddr | MCU_CACHE_OFFSET; + msg.size = size; + msg.offset = ENCODER_STREAM_OFFSET; + msg.stream_id = 0; /* copied to mcu_msg_encode_frame_response */ + + allegro_mbox_write(dev, &dev->mbox_command, &msg, sizeof(msg)); + allegro_mcu_interrupt(dev); + + return 0; +} + +static int allegro_mcu_send_encode_frame(struct allegro_dev *dev, + struct allegro_channel *channel, + dma_addr_t src_y, dma_addr_t src_uv) +{ + struct mcu_msg_encode_frame msg; + + memset(&msg, 0, sizeof(msg)); + + msg.header.type = MCU_MSG_TYPE_ENCODE_FRAME; + msg.header.length = sizeof(msg) - sizeof(msg.header); + + msg.channel_id = channel->mcu_channel_id; + msg.encoding_options = AL_OPT_FORCE_LOAD; + msg.pps_qp = 26; /* qp are relative to 26 */ + msg.user_param = 0; /* copied to mcu_msg_encode_frame_response */ + msg.src_handle = 0; /* copied to mcu_msg_encode_frame_response */ + msg.src_y = src_y; + msg.src_uv = src_uv; + msg.stride = channel->stride; + msg.ep2 = 0x0; + msg.ep2_v = msg.ep2 | MCU_CACHE_OFFSET; + + allegro_mbox_write(dev, &dev->mbox_command, &msg, sizeof(msg)); + allegro_mcu_interrupt(dev); + + return 0; +} + +static int allegro_mcu_wait_for_init_timeout(struct allegro_dev *dev, + unsigned long timeout_ms) +{ + unsigned long tmo; + + tmo = wait_for_completion_timeout(&dev->init_complete, + msecs_to_jiffies(timeout_ms)); + if (tmo == 0) + return -ETIMEDOUT; + + reinit_completion(&dev->init_complete); + return 0; +} + +static int allegro_mcu_push_buffer_internal(struct allegro_channel *channel, + enum mcu_msg_type type) +{ + struct allegro_dev *dev = channel->dev; + struct mcu_msg_push_buffers_internal *msg; + struct mcu_msg_push_buffers_internal_buffer *buffer; + unsigned int num_buffers = 0; + size_t size; + struct allegro_buffer *al_buffer; + struct list_head *list; + int err; + + switch (type) { + case MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE: + list = &channel->buffers_reference; + break; + case MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE: + list = &channel->buffers_intermediate; + break; + default: + return -EINVAL; + } + + list_for_each_entry(al_buffer, list, head) + num_buffers++; + size = struct_size(msg, buffer, num_buffers); + + msg = kmalloc(size, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + msg->header.length = size - sizeof(msg->header); + msg->header.type = type; + msg->channel_id = channel->mcu_channel_id; + + buffer = msg->buffer; + list_for_each_entry(al_buffer, list, head) { + buffer->dma_addr = lower_32_bits(al_buffer->paddr); + buffer->mcu_addr = + lower_32_bits(al_buffer->paddr) | MCU_CACHE_OFFSET; + buffer->size = al_buffer->size; + buffer++; + } + + err = allegro_mbox_write(dev, &dev->mbox_command, msg, size); + if (err) + goto out; + allegro_mcu_interrupt(dev); + +out: + kfree(msg); + return err; +} + +static int allegro_mcu_push_buffer_intermediate(struct allegro_channel *channel) +{ + enum mcu_msg_type type = MCU_MSG_TYPE_PUSH_BUFFER_INTERMEDIATE; + + return allegro_mcu_push_buffer_internal(channel, type); +} + +static int allegro_mcu_push_buffer_reference(struct allegro_channel *channel) +{ + enum mcu_msg_type type = MCU_MSG_TYPE_PUSH_BUFFER_REFERENCE; + + return allegro_mcu_push_buffer_internal(channel, type); +} + +static int allocate_buffers_internal(struct allegro_channel *channel, + struct list_head *list, + size_t n, size_t size) +{ + struct allegro_dev *dev = channel->dev; + unsigned int i; + int err; + struct allegro_buffer *buffer, *tmp; + + for (i = 0; i < n; i++) { + buffer = kmalloc(sizeof(*buffer), GFP_KERNEL); + if (!buffer) { + err = -ENOMEM; + goto err; + } + INIT_LIST_HEAD(&buffer->head); + + err = allegro_alloc_buffer(dev, buffer, size); + if (err) + goto err; + list_add(&buffer->head, list); + } + + return 0; + +err: + list_for_each_entry_safe(buffer, tmp, list, head) { + list_del(&buffer->head); + allegro_free_buffer(dev, buffer); + kfree(buffer); + } + return err; +} + +static void destroy_buffers_internal(struct allegro_channel *channel, + struct list_head *list) +{ + struct allegro_dev *dev = channel->dev; + struct allegro_buffer *buffer, *tmp; + + list_for_each_entry_safe(buffer, tmp, list, head) { + list_del(&buffer->head); + allegro_free_buffer(dev, buffer); + kfree(buffer); + } +} + +static void destroy_reference_buffers(struct allegro_channel *channel) +{ + return destroy_buffers_internal(channel, &channel->buffers_reference); +} + +static void destroy_intermediate_buffers(struct allegro_channel *channel) +{ + return destroy_buffers_internal(channel, + &channel->buffers_intermediate); +} + +static int allocate_intermediate_buffers(struct allegro_channel *channel, + size_t n, size_t size) +{ + return allocate_buffers_internal(channel, + &channel->buffers_intermediate, + n, size); +} + +static int allocate_reference_buffers(struct allegro_channel *channel, + size_t n, size_t size) +{ + return allocate_buffers_internal(channel, + &channel->buffers_reference, + n, PAGE_ALIGN(size)); +} + +static ssize_t allegro_h264_write_sps(struct allegro_channel *channel, + void *dest, size_t n) +{ + struct allegro_dev *dev = channel->dev; + struct nal_h264_sps *sps; + ssize_t size; + unsigned int size_mb = SIZE_MACROBLOCK; + /* Calculation of crop units in Rec. ITU-T H.264 (04/2017) p. 76 */ + unsigned int crop_unit_x = 2; + unsigned int crop_unit_y = 2; + + sps = kzalloc(sizeof(*sps), GFP_KERNEL); + if (!sps) + return -ENOMEM; + + sps->profile_idc = nal_h264_profile_from_v4l2(channel->profile); + sps->constraint_set0_flag = 0; + sps->constraint_set1_flag = 1; + sps->constraint_set2_flag = 0; + sps->constraint_set3_flag = 0; + sps->constraint_set4_flag = 0; + sps->constraint_set5_flag = 0; + sps->level_idc = nal_h264_level_from_v4l2(channel->level); + sps->seq_parameter_set_id = 0; + sps->log2_max_frame_num_minus4 = 0; + sps->pic_order_cnt_type = 0; + sps->log2_max_pic_order_cnt_lsb_minus4 = 6; + sps->max_num_ref_frames = 3; + sps->gaps_in_frame_num_value_allowed_flag = 0; + sps->pic_width_in_mbs_minus1 = + DIV_ROUND_UP(channel->width, size_mb) - 1; + sps->pic_height_in_map_units_minus1 = + DIV_ROUND_UP(channel->height, size_mb) - 1; + sps->frame_mbs_only_flag = 1; + sps->mb_adaptive_frame_field_flag = 0; + sps->direct_8x8_inference_flag = 1; + sps->frame_cropping_flag = + (channel->width % size_mb) || (channel->height % size_mb); + if (sps->frame_cropping_flag) { + sps->crop_left = 0; + sps->crop_right = (round_up(channel->width, size_mb) - channel->width) / crop_unit_x; + sps->crop_top = 0; + sps->crop_bottom = (round_up(channel->height, size_mb) - channel->height) / crop_unit_y; + } + sps->vui_parameters_present_flag = 1; + sps->vui.aspect_ratio_info_present_flag = 0; + sps->vui.overscan_info_present_flag = 0; + sps->vui.video_signal_type_present_flag = 1; + sps->vui.video_format = 1; + sps->vui.video_full_range_flag = 0; + sps->vui.colour_description_present_flag = 1; + sps->vui.colour_primaries = 5; + sps->vui.transfer_characteristics = 5; + sps->vui.matrix_coefficients = 5; + sps->vui.chroma_loc_info_present_flag = 1; + sps->vui.chroma_sample_loc_type_top_field = 0; + sps->vui.chroma_sample_loc_type_bottom_field = 0; + sps->vui.timing_info_present_flag = 1; + sps->vui.num_units_in_tick = 1; + sps->vui.time_scale = 50; + sps->vui.fixed_frame_rate_flag = 1; + sps->vui.nal_hrd_parameters_present_flag = 0; + sps->vui.vcl_hrd_parameters_present_flag = 1; + sps->vui.vcl_hrd_parameters.cpb_cnt_minus1 = 0; + sps->vui.vcl_hrd_parameters.bit_rate_scale = 0; + sps->vui.vcl_hrd_parameters.cpb_size_scale = 1; + /* See Rec. ITU-T H.264 (04/2017) p. 410 E-53 */ + sps->vui.vcl_hrd_parameters.bit_rate_value_minus1[0] = + channel->bitrate_peak / (1 << (6 + sps->vui.vcl_hrd_parameters.bit_rate_scale)) - 1; + /* See Rec. ITU-T H.264 (04/2017) p. 410 E-54 */ + sps->vui.vcl_hrd_parameters.cpb_size_value_minus1[0] = + (channel->cpb_size * 1000) / (1 << (4 + sps->vui.vcl_hrd_parameters.cpb_size_scale)) - 1; + sps->vui.vcl_hrd_parameters.cbr_flag[0] = 1; + sps->vui.vcl_hrd_parameters.initial_cpb_removal_delay_length_minus1 = 31; + sps->vui.vcl_hrd_parameters.cpb_removal_delay_length_minus1 = 31; + sps->vui.vcl_hrd_parameters.dpb_output_delay_length_minus1 = 31; + sps->vui.vcl_hrd_parameters.time_offset_length = 0; + sps->vui.low_delay_hrd_flag = 0; + sps->vui.pic_struct_present_flag = 1; + sps->vui.bitstream_restriction_flag = 0; + + size = nal_h264_write_sps(&dev->plat_dev->dev, dest, n, sps); + + kfree(sps); + + return size; +} + +static ssize_t allegro_h264_write_pps(struct allegro_channel *channel, + void *dest, size_t n) +{ + struct allegro_dev *dev = channel->dev; + struct nal_h264_pps *pps; + ssize_t size; + + pps = kzalloc(sizeof(*pps), GFP_KERNEL); + if (!pps) + return -ENOMEM; + + pps->pic_parameter_set_id = 0; + pps->seq_parameter_set_id = 0; + pps->entropy_coding_mode_flag = 0; + pps->bottom_field_pic_order_in_frame_present_flag = 0; + pps->num_slice_groups_minus1 = 0; + pps->num_ref_idx_l0_default_active_minus1 = 2; + pps->num_ref_idx_l1_default_active_minus1 = 2; + pps->weighted_pred_flag = 0; + pps->weighted_bipred_idc = 0; + pps->pic_init_qp_minus26 = 0; + pps->pic_init_qs_minus26 = 0; + pps->chroma_qp_index_offset = 0; + pps->deblocking_filter_control_present_flag = 1; + pps->constrained_intra_pred_flag = 0; + pps->redundant_pic_cnt_present_flag = 0; + pps->transform_8x8_mode_flag = 0; + pps->pic_scaling_matrix_present_flag = 0; + pps->second_chroma_qp_index_offset = 0; + + size = nal_h264_write_pps(&dev->plat_dev->dev, dest, n, pps); + + kfree(pps); + + return size; +} + +static bool allegro_channel_is_at_eos(struct allegro_channel *channel) +{ + bool is_at_eos = false; + + switch (allegro_get_state(channel)) { + case ALLEGRO_STATE_STOPPED: + is_at_eos = true; + break; + case ALLEGRO_STATE_DRAIN: + case ALLEGRO_STATE_WAIT_FOR_BUFFER: + if (v4l2_m2m_num_src_bufs_ready(channel->fh.m2m_ctx) == 0) + is_at_eos = true; + break; + default: + break; + } + + return is_at_eos; +} + +static void allegro_channel_buf_done(struct allegro_channel *channel, + struct vb2_v4l2_buffer *buf, + enum vb2_buffer_state state) +{ + const struct v4l2_event eos_event = { + .type = V4L2_EVENT_EOS + }; + + if (allegro_channel_is_at_eos(channel)) { + buf->flags |= V4L2_BUF_FLAG_LAST; + v4l2_event_queue_fh(&channel->fh, &eos_event); + + allegro_set_state(channel, ALLEGRO_STATE_STOPPED); + } + + v4l2_m2m_buf_done(buf, state); +} + +static void allegro_channel_finish_frame(struct allegro_channel *channel, + struct mcu_msg_encode_frame_response *msg) +{ + struct allegro_dev *dev = channel->dev; + struct vb2_v4l2_buffer *src_buf; + struct vb2_v4l2_buffer *dst_buf; + struct { + u32 offset; + u32 size; + } *partition; + enum vb2_buffer_state state = VB2_BUF_STATE_ERROR; + char *curr; + ssize_t len; + ssize_t free; + + src_buf = v4l2_m2m_src_buf_remove(channel->fh.m2m_ctx); + + dst_buf = v4l2_m2m_dst_buf_remove(channel->fh.m2m_ctx); + dst_buf->sequence = channel->csequence++; + + if (msg->error_code) { + v4l2_err(&dev->v4l2_dev, + "channel %d: error while encoding frame: %x\n", + channel->mcu_channel_id, msg->error_code); + goto err; + } + + if (msg->partition_table_size != 1) { + v4l2_warn(&dev->v4l2_dev, + "channel %d: only handling first partition table entry (%d entries)\n", + channel->mcu_channel_id, msg->partition_table_size); + } + + if (msg->partition_table_offset + + msg->partition_table_size * sizeof(*partition) > + vb2_plane_size(&dst_buf->vb2_buf, 0)) { + v4l2_err(&dev->v4l2_dev, + "channel %d: partition table outside of dst_buf\n", + channel->mcu_channel_id); + goto err; + } + + partition = + vb2_plane_vaddr(&dst_buf->vb2_buf, 0) + msg->partition_table_offset; + if (partition->offset + partition->size > + vb2_plane_size(&dst_buf->vb2_buf, 0)) { + v4l2_err(&dev->v4l2_dev, + "channel %d: encoded frame is outside of dst_buf (offset 0x%x, size 0x%x)\n", + channel->mcu_channel_id, partition->offset, + partition->size); + goto err; + } + + v4l2_dbg(2, debug, &dev->v4l2_dev, + "channel %d: encoded frame of size %d is at offset 0x%x\n", + channel->mcu_channel_id, partition->size, partition->offset); + + /* + * The payload must include the data before the partition offset, + * because we will put the sps and pps data there. + */ + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, + partition->offset + partition->size); + + curr = vb2_plane_vaddr(&dst_buf->vb2_buf, 0); + free = partition->offset; + if (msg->is_idr) { + len = allegro_h264_write_sps(channel, curr, free); + if (len < 0) { + v4l2_err(&dev->v4l2_dev, + "not enough space for sequence parameter set: %zd left\n", + free); + goto err; + } + curr += len; + free -= len; + v4l2_dbg(1, debug, &dev->v4l2_dev, + "channel %d: wrote %zd byte SPS nal unit\n", + channel->mcu_channel_id, len); + } + + if (msg->slice_type == AL_ENC_SLICE_TYPE_I) { + len = allegro_h264_write_pps(channel, curr, free); + if (len < 0) { + v4l2_err(&dev->v4l2_dev, + "not enough space for picture parameter set: %zd left\n", + free); + goto err; + } + curr += len; + free -= len; + v4l2_dbg(1, debug, &dev->v4l2_dev, + "channel %d: wrote %zd byte PPS nal unit\n", + channel->mcu_channel_id, len); + } + + len = nal_h264_write_filler(&dev->plat_dev->dev, curr, free); + if (len < 0) { + v4l2_err(&dev->v4l2_dev, + "failed to write %zd filler data\n", free); + goto err; + } + curr += len; + free -= len; + v4l2_dbg(2, debug, &dev->v4l2_dev, + "channel %d: wrote %zd bytes filler nal unit\n", + channel->mcu_channel_id, len); + + if (free != 0) { + v4l2_err(&dev->v4l2_dev, + "non-VCL NAL units do not fill space until VCL NAL unit: %zd bytes left\n", + free); + goto err; + } + + state = VB2_BUF_STATE_DONE; + + v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false); + if (msg->is_idr) + dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; + else + dst_buf->flags |= V4L2_BUF_FLAG_PFRAME; + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "channel %d: encoded frame #%03d (%s%s, %d bytes)\n", + channel->mcu_channel_id, + dst_buf->sequence, + msg->is_idr ? "IDR, " : "", + msg->slice_type == AL_ENC_SLICE_TYPE_I ? "I slice" : + msg->slice_type == AL_ENC_SLICE_TYPE_P ? "P slice" : "unknown", + partition->size); + +err: + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + + allegro_channel_buf_done(channel, dst_buf, state); + + v4l2_m2m_job_finish(dev->m2m_dev, channel->fh.m2m_ctx); +} + +static int allegro_handle_init(struct allegro_dev *dev, + struct mcu_msg_init_response *msg) +{ + complete(&dev->init_complete); + + return 0; +} + +static int +allegro_handle_create_channel(struct allegro_dev *dev, + struct mcu_msg_create_channel_response *msg) +{ + struct allegro_channel *channel; + int err = 0; + + channel = allegro_find_channel_by_user_id(dev, msg->user_id); + if (IS_ERR(channel)) { + v4l2_warn(&dev->v4l2_dev, + "received %s for unknown user %d\n", + msg_type_name(msg->header.type), + msg->user_id); + return -EINVAL; + } + + if (msg->error_code) { + v4l2_err(&dev->v4l2_dev, + "user %d: mcu failed to create channel: error %x\n", + channel->user_id, msg->error_code); + err = -EIO; + goto out; + } + + channel->mcu_channel_id = msg->channel_id; + v4l2_dbg(1, debug, &dev->v4l2_dev, + "user %d: channel has channel id %d\n", + channel->user_id, channel->mcu_channel_id); + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "channel %d: intermediate buffers: %d x %d bytes\n", + channel->mcu_channel_id, + msg->int_buffers_count, msg->int_buffers_size); + err = allocate_intermediate_buffers(channel, msg->int_buffers_count, + msg->int_buffers_size); + if (err) { + v4l2_err(&dev->v4l2_dev, + "channel %d: failed to allocate intermediate buffers\n", + channel->mcu_channel_id); + goto out; + } + err = allegro_mcu_push_buffer_intermediate(channel); + if (err) + goto out; + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "channel %d: reference buffers: %d x %d bytes\n", + channel->mcu_channel_id, + msg->rec_buffers_count, msg->rec_buffers_size); + err = allocate_reference_buffers(channel, msg->rec_buffers_count, + msg->rec_buffers_size); + if (err) { + v4l2_err(&dev->v4l2_dev, + "channel %d: failed to allocate reference buffers\n", + channel->mcu_channel_id); + goto out; + } + err = allegro_mcu_push_buffer_reference(channel); + if (err) + goto out; + +out: + channel->error = err; + complete(&channel->completion); + + /* Handled successfully, error is passed via channel->error */ + return 0; +} + +static int +allegro_handle_destroy_channel(struct allegro_dev *dev, + struct mcu_msg_destroy_channel_response *msg) +{ + struct allegro_channel *channel; + + channel = allegro_find_channel_by_channel_id(dev, msg->channel_id); + if (IS_ERR(channel)) { + v4l2_err(&dev->v4l2_dev, + "received %s for unknown channel %d\n", + msg_type_name(msg->header.type), + msg->channel_id); + return -EINVAL; + } + + v4l2_dbg(2, debug, &dev->v4l2_dev, + "user %d: vcu destroyed channel %d\n", + channel->user_id, channel->mcu_channel_id); + complete(&channel->completion); + + return 0; +} + +static int +allegro_handle_encode_frame(struct allegro_dev *dev, + struct mcu_msg_encode_frame_response *msg) +{ + struct allegro_channel *channel; + + channel = allegro_find_channel_by_channel_id(dev, msg->channel_id); + if (IS_ERR(channel)) { + v4l2_err(&dev->v4l2_dev, + "received %s for unknown channel %d\n", + msg_type_name(msg->header.type), + msg->channel_id); + return -EINVAL; + } + + allegro_channel_finish_frame(channel, msg); + + return 0; +} + +static int allegro_receive_message(struct allegro_dev *dev) +{ + union mcu_msg_response *msg; + ssize_t size; + int err = 0; + + msg = kmalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + size = allegro_mbox_read(dev, &dev->mbox_status, msg, sizeof(*msg)); + if (size < sizeof(msg->header)) { + v4l2_err(&dev->v4l2_dev, + "invalid mbox message (%zd): must be at least %zu\n", + size, sizeof(msg->header)); + err = -EINVAL; + goto out; + } + + switch (msg->header.type) { + case MCU_MSG_TYPE_INIT: + err = allegro_handle_init(dev, &msg->init); + break; + case MCU_MSG_TYPE_CREATE_CHANNEL: + err = allegro_handle_create_channel(dev, &msg->create_channel); + break; + case MCU_MSG_TYPE_DESTROY_CHANNEL: + err = allegro_handle_destroy_channel(dev, + &msg->destroy_channel); + break; + case MCU_MSG_TYPE_ENCODE_FRAME: + err = allegro_handle_encode_frame(dev, &msg->encode_frame); + break; + default: + v4l2_warn(&dev->v4l2_dev, + "%s: unknown message %s\n", + __func__, msg_type_name(msg->header.type)); + err = -EINVAL; + break; + } + +out: + kfree(msg); + + return err; +} + +static irqreturn_t allegro_hardirq(int irq, void *data) +{ + struct allegro_dev *dev = data; + unsigned int status; + + regmap_read(dev->regmap, AL5_ITC_CPU_IRQ_STA, &status); + if (!(status & AL5_ITC_CPU_IRQ_STA_TRIGGERED)) + return IRQ_NONE; + + regmap_write(dev->regmap, AL5_ITC_CPU_IRQ_CLR, status); + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t allegro_irq_thread(int irq, void *data) +{ + struct allegro_dev *dev = data; + + allegro_receive_message(dev); + + return IRQ_HANDLED; +} + +static void allegro_copy_firmware(struct allegro_dev *dev, + const u8 * const buf, size_t size) +{ + int err = 0; + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "copy mcu firmware (%zu B) to SRAM\n", size); + err = regmap_bulk_write(dev->sram, 0x0, buf, size / 4); + if (err) + v4l2_err(&dev->v4l2_dev, + "failed to copy firmware: %d\n", err); +} + +static void allegro_copy_fw_codec(struct allegro_dev *dev, + const u8 * const buf, size_t size) +{ + int err; + dma_addr_t icache_offset, dcache_offset; + + /* + * The downstream allocates 600 KB for the codec firmware to have some + * extra space for "possible extensions." My tests were fine with + * allocating just enough memory for the actual firmware, but I am not + * sure that the firmware really does not use the remaining space. + */ + err = allegro_alloc_buffer(dev, &dev->firmware, size); + if (err) { + v4l2_err(&dev->v4l2_dev, + "failed to allocate %zu bytes for firmware\n", size); + return; + } + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "copy codec firmware (%zd B) to phys %pad\n", + size, &dev->firmware.paddr); + memcpy(dev->firmware.vaddr, buf, size); + + regmap_write(dev->regmap, AXI_ADDR_OFFSET_IP, + upper_32_bits(dev->firmware.paddr)); + + icache_offset = dev->firmware.paddr - MCU_CACHE_OFFSET; + v4l2_dbg(2, debug, &dev->v4l2_dev, + "icache_offset: msb = 0x%x, lsb = 0x%x\n", + upper_32_bits(icache_offset), lower_32_bits(icache_offset)); + regmap_write(dev->regmap, AL5_ICACHE_ADDR_OFFSET_MSB, + upper_32_bits(icache_offset)); + regmap_write(dev->regmap, AL5_ICACHE_ADDR_OFFSET_LSB, + lower_32_bits(icache_offset)); + + dcache_offset = + (dev->firmware.paddr & 0xffffffff00000000ULL) - MCU_CACHE_OFFSET; + v4l2_dbg(2, debug, &dev->v4l2_dev, + "dcache_offset: msb = 0x%x, lsb = 0x%x\n", + upper_32_bits(dcache_offset), lower_32_bits(dcache_offset)); + regmap_write(dev->regmap, AL5_DCACHE_ADDR_OFFSET_MSB, + upper_32_bits(dcache_offset)); + regmap_write(dev->regmap, AL5_DCACHE_ADDR_OFFSET_LSB, + lower_32_bits(dcache_offset)); +} + +static void allegro_free_fw_codec(struct allegro_dev *dev) +{ + allegro_free_buffer(dev, &dev->firmware); +} + +/* + * Control functions for the MCU + */ + +static int allegro_mcu_enable_interrupts(struct allegro_dev *dev) +{ + return regmap_write(dev->regmap, AL5_ITC_CPU_IRQ_MSK, BIT(0)); +} + +static int allegro_mcu_disable_interrupts(struct allegro_dev *dev) +{ + return regmap_write(dev->regmap, AL5_ITC_CPU_IRQ_MSK, 0); +} + +static int allegro_mcu_wait_for_sleep(struct allegro_dev *dev) +{ + unsigned long timeout; + unsigned int status; + + timeout = jiffies + msecs_to_jiffies(100); + while (regmap_read(dev->regmap, AL5_MCU_STA, &status) == 0 && + status != AL5_MCU_STA_SLEEP) { + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + cpu_relax(); + } + + return 0; +} + +static int allegro_mcu_start(struct allegro_dev *dev) +{ + unsigned long timeout; + unsigned int status; + int err; + + err = regmap_write(dev->regmap, AL5_MCU_WAKEUP, BIT(0)); + if (err) + return err; + + timeout = jiffies + msecs_to_jiffies(100); + while (regmap_read(dev->regmap, AL5_MCU_STA, &status) == 0 && + status == AL5_MCU_STA_SLEEP) { + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + cpu_relax(); + } + + err = regmap_write(dev->regmap, AL5_MCU_WAKEUP, 0); + if (err) + return err; + + return 0; +} + +static int allegro_mcu_reset(struct allegro_dev *dev) +{ + int err; + + err = regmap_write(dev->regmap, + AL5_MCU_RESET_MODE, AL5_MCU_RESET_MODE_SLEEP); + if (err < 0) + return err; + + err = regmap_write(dev->regmap, AL5_MCU_RESET, AL5_MCU_RESET_SOFT); + if (err < 0) + return err; + + return allegro_mcu_wait_for_sleep(dev); +} + +static void allegro_destroy_channel(struct allegro_channel *channel) +{ + struct allegro_dev *dev = channel->dev; + unsigned long timeout; + + if (channel_exists(channel)) { + reinit_completion(&channel->completion); + allegro_mcu_send_destroy_channel(dev, channel); + timeout = wait_for_completion_timeout(&channel->completion, + msecs_to_jiffies(5000)); + if (timeout == 0) + v4l2_warn(&dev->v4l2_dev, + "channel %d: timeout while destroying\n", + channel->mcu_channel_id); + + channel->mcu_channel_id = -1; + } + + destroy_intermediate_buffers(channel); + destroy_reference_buffers(channel); + + v4l2_ctrl_grab(channel->mpeg_video_h264_profile, false); + v4l2_ctrl_grab(channel->mpeg_video_h264_level, false); + v4l2_ctrl_grab(channel->mpeg_video_bitrate_mode, false); + v4l2_ctrl_grab(channel->mpeg_video_bitrate, false); + v4l2_ctrl_grab(channel->mpeg_video_bitrate_peak, false); + v4l2_ctrl_grab(channel->mpeg_video_cpb_size, false); + v4l2_ctrl_grab(channel->mpeg_video_gop_size, false); + + if (channel->user_id != -1) { + clear_bit(channel->user_id, &dev->channel_user_ids); + channel->user_id = -1; + } +} + +/* + * Create the MCU channel + * + * After the channel has been created, the picture size, format, colorspace + * and framerate are fixed. Also the codec, profile, bitrate, etc. cannot be + * changed anymore. + * + * The channel can be created only once. The MCU will accept source buffers + * and stream buffers only after a channel has been created. + */ +static int allegro_create_channel(struct allegro_channel *channel) +{ + struct allegro_dev *dev = channel->dev; + unsigned long timeout; + enum v4l2_mpeg_video_h264_level min_level; + + if (channel_exists(channel)) { + v4l2_warn(&dev->v4l2_dev, + "channel already exists\n"); + return 0; + } + + channel->user_id = allegro_next_user_id(dev); + if (channel->user_id < 0) { + v4l2_err(&dev->v4l2_dev, + "no free channels available\n"); + return -EBUSY; + } + set_bit(channel->user_id, &dev->channel_user_ids); + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "user %d: creating channel (%4.4s, %dx%d@%d)\n", + channel->user_id, + (char *)&channel->codec, channel->width, channel->height, 25); + + min_level = select_minimum_h264_level(channel->width, channel->height); + if (channel->level < min_level) { + v4l2_warn(&dev->v4l2_dev, + "user %d: selected Level %s too low: increasing to Level %s\n", + channel->user_id, + v4l2_ctrl_get_menu(V4L2_CID_MPEG_VIDEO_H264_LEVEL)[channel->level], + v4l2_ctrl_get_menu(V4L2_CID_MPEG_VIDEO_H264_LEVEL)[min_level]); + channel->level = min_level; + } + + v4l2_ctrl_grab(channel->mpeg_video_h264_profile, true); + v4l2_ctrl_grab(channel->mpeg_video_h264_level, true); + v4l2_ctrl_grab(channel->mpeg_video_bitrate_mode, true); + v4l2_ctrl_grab(channel->mpeg_video_bitrate, true); + v4l2_ctrl_grab(channel->mpeg_video_bitrate_peak, true); + v4l2_ctrl_grab(channel->mpeg_video_cpb_size, true); + v4l2_ctrl_grab(channel->mpeg_video_gop_size, true); + + reinit_completion(&channel->completion); + allegro_mcu_send_create_channel(dev, channel); + timeout = wait_for_completion_timeout(&channel->completion, + msecs_to_jiffies(5000)); + if (timeout == 0) + channel->error = -ETIMEDOUT; + if (channel->error) + goto err; + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "channel %d: accepting buffers\n", + channel->mcu_channel_id); + + return 0; + +err: + allegro_destroy_channel(channel); + + return channel->error; +} + +static void allegro_set_default_params(struct allegro_channel *channel) +{ + channel->width = ALLEGRO_WIDTH_DEFAULT; + channel->height = ALLEGRO_HEIGHT_DEFAULT; + channel->stride = round_up(channel->width, 32); + + channel->colorspace = V4L2_COLORSPACE_REC709; + channel->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + channel->quantization = V4L2_QUANTIZATION_DEFAULT; + channel->xfer_func = V4L2_XFER_FUNC_DEFAULT; + + channel->pixelformat = V4L2_PIX_FMT_NV12; + channel->sizeimage_raw = channel->stride * channel->height * 3 / 2; + + channel->codec = V4L2_PIX_FMT_H264; + channel->profile = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE; + channel->level = + select_minimum_h264_level(channel->width, channel->height); + channel->sizeimage_encoded = + estimate_stream_size(channel->width, channel->height); + + channel->bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_CBR; + channel->bitrate = maximum_bitrate(channel->level); + channel->bitrate_peak = maximum_bitrate(channel->level); + channel->cpb_size = maximum_cpb_size(channel->level); + channel->gop_size = ALLEGRO_GOP_SIZE_DEFAULT; +} + +static int allegro_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct allegro_channel *channel = vb2_get_drv_priv(vq); + struct allegro_dev *dev = channel->dev; + + v4l2_dbg(2, debug, &dev->v4l2_dev, + "%s: queue setup[%s]: nplanes = %d\n", + V4L2_TYPE_IS_OUTPUT(vq->type) ? "output" : "capture", + *nplanes == 0 ? "REQBUFS" : "CREATE_BUFS", *nplanes); + + if (*nplanes != 0) { + if (V4L2_TYPE_IS_OUTPUT(vq->type)) { + if (sizes[0] < channel->sizeimage_raw) + return -EINVAL; + } else { + if (sizes[0] < channel->sizeimage_encoded) + return -EINVAL; + } + } else { + *nplanes = 1; + if (V4L2_TYPE_IS_OUTPUT(vq->type)) + sizes[0] = channel->sizeimage_raw; + else + sizes[0] = channel->sizeimage_encoded; + } + + return 0; +} + +static int allegro_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct allegro_channel *channel = vb2_get_drv_priv(vb->vb2_queue); + struct allegro_dev *dev = channel->dev; + + if (allegro_get_state(channel) == ALLEGRO_STATE_DRAIN && + V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) + return -EBUSY; + + if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { + if (vbuf->field == V4L2_FIELD_ANY) + vbuf->field = V4L2_FIELD_NONE; + if (vbuf->field != V4L2_FIELD_NONE) { + v4l2_err(&dev->v4l2_dev, + "channel %d: unsupported field\n", + channel->mcu_channel_id); + return -EINVAL; + } + } + + return 0; +} + +static void allegro_buf_queue(struct vb2_buffer *vb) +{ + struct allegro_channel *channel = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + if (allegro_get_state(channel) == ALLEGRO_STATE_WAIT_FOR_BUFFER && + vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + allegro_channel_buf_done(channel, vbuf, VB2_BUF_STATE_DONE); + return; + } + + v4l2_m2m_buf_queue(channel->fh.m2m_ctx, vbuf); +} + +static int allegro_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct allegro_channel *channel = vb2_get_drv_priv(q); + struct allegro_dev *dev = channel->dev; + + v4l2_dbg(2, debug, &dev->v4l2_dev, + "%s: start streaming\n", + V4L2_TYPE_IS_OUTPUT(q->type) ? "output" : "capture"); + + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + channel->osequence = 0; + allegro_set_state(channel, ALLEGRO_STATE_ENCODING); + } else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + channel->csequence = 0; + } + + return 0; +} + +static void allegro_stop_streaming(struct vb2_queue *q) +{ + struct allegro_channel *channel = vb2_get_drv_priv(q); + struct allegro_dev *dev = channel->dev; + struct vb2_v4l2_buffer *buffer; + + v4l2_dbg(2, debug, &dev->v4l2_dev, + "%s: stop streaming\n", + V4L2_TYPE_IS_OUTPUT(q->type) ? "output" : "capture"); + + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + allegro_set_state(channel, ALLEGRO_STATE_STOPPED); + while ((buffer = v4l2_m2m_src_buf_remove(channel->fh.m2m_ctx))) + v4l2_m2m_buf_done(buffer, VB2_BUF_STATE_ERROR); + } else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + allegro_destroy_channel(channel); + while ((buffer = v4l2_m2m_dst_buf_remove(channel->fh.m2m_ctx))) + v4l2_m2m_buf_done(buffer, VB2_BUF_STATE_ERROR); + } +} + +static const struct vb2_ops allegro_queue_ops = { + .queue_setup = allegro_queue_setup, + .buf_prepare = allegro_buf_prepare, + .buf_queue = allegro_buf_queue, + .start_streaming = allegro_start_streaming, + .stop_streaming = allegro_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int allegro_queue_init(void *priv, + struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + int err; + struct allegro_channel *channel = priv; + + src_vq->dev = &channel->dev->plat_dev->dev; + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + src_vq->io_modes = VB2_DMABUF | VB2_MMAP; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->drv_priv = channel; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->ops = &allegro_queue_ops; + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + src_vq->lock = &channel->dev->lock; + err = vb2_queue_init(src_vq); + if (err) + return err; + + dst_vq->dev = &channel->dev->plat_dev->dev; + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dst_vq->io_modes = VB2_DMABUF | VB2_MMAP; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->drv_priv = channel; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->ops = &allegro_queue_ops; + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->lock = &channel->dev->lock; + err = vb2_queue_init(dst_vq); + if (err) + return err; + + return 0; +} + +static int allegro_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct allegro_channel *channel = container_of(ctrl->handler, + struct allegro_channel, + ctrl_handler); + struct allegro_dev *dev = channel->dev; + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "s_ctrl: %s = %d\n", v4l2_ctrl_get_name(ctrl->id), ctrl->val); + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + channel->level = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + channel->bitrate_mode = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + channel->bitrate = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: + channel->bitrate_peak = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE: + channel->cpb_size = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + channel->gop_size = ctrl->val; + break; + } + + return 0; +} + +static const struct v4l2_ctrl_ops allegro_ctrl_ops = { + .s_ctrl = allegro_s_ctrl, +}; + +static int allegro_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct allegro_dev *dev = video_get_drvdata(vdev); + struct allegro_channel *channel = NULL; + struct v4l2_ctrl_handler *handler; + u64 mask; + + channel = kzalloc(sizeof(*channel), GFP_KERNEL); + if (!channel) + return -ENOMEM; + + v4l2_fh_init(&channel->fh, vdev); + file->private_data = &channel->fh; + v4l2_fh_add(&channel->fh); + + init_completion(&channel->completion); + + channel->dev = dev; + + allegro_set_default_params(channel); + + handler = &channel->ctrl_handler; + v4l2_ctrl_handler_init(handler, 0); + channel->mpeg_video_h264_profile = v4l2_ctrl_new_std_menu(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_PROFILE, + V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, 0x0, + V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE); + mask = 1 << V4L2_MPEG_VIDEO_H264_LEVEL_1B; + channel->mpeg_video_h264_level = v4l2_ctrl_new_std_menu(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_LEVEL, + V4L2_MPEG_VIDEO_H264_LEVEL_5_1, mask, + V4L2_MPEG_VIDEO_H264_LEVEL_5_1); + channel->mpeg_video_bitrate_mode = v4l2_ctrl_new_std_menu(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE_MODE, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0, + channel->bitrate_mode); + channel->mpeg_video_bitrate = v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE, + 0, maximum_bitrate(V4L2_MPEG_VIDEO_H264_LEVEL_5_1), + 1, channel->bitrate); + channel->mpeg_video_bitrate_peak = v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, + 0, maximum_bitrate(V4L2_MPEG_VIDEO_H264_LEVEL_5_1), + 1, channel->bitrate_peak); + channel->mpeg_video_cpb_size = v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE, + 0, maximum_cpb_size(V4L2_MPEG_VIDEO_H264_LEVEL_5_1), + 1, channel->cpb_size); + channel->mpeg_video_gop_size = v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MPEG_VIDEO_GOP_SIZE, + 0, ALLEGRO_GOP_SIZE_MAX, + 1, channel->gop_size); + v4l2_ctrl_new_std(handler, + &allegro_ctrl_ops, + V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, + 1, 32, + 1, 1); + channel->fh.ctrl_handler = handler; + + channel->mcu_channel_id = -1; + channel->user_id = -1; + + INIT_LIST_HEAD(&channel->buffers_reference); + INIT_LIST_HEAD(&channel->buffers_intermediate); + + list_add(&channel->list, &dev->channels); + + channel->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, channel, + allegro_queue_init); + + return 0; +} + +static int allegro_release(struct file *file) +{ + struct allegro_channel *channel = fh_to_channel(file->private_data); + + v4l2_m2m_ctx_release(channel->fh.m2m_ctx); + + list_del(&channel->list); + + v4l2_ctrl_handler_free(&channel->ctrl_handler); + + v4l2_fh_del(&channel->fh); + v4l2_fh_exit(&channel->fh); + + kfree(channel); + + return 0; +} + +static int allegro_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct video_device *vdev = video_devdata(file); + struct allegro_dev *dev = video_get_drvdata(vdev); + + strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); + strscpy(cap->card, "Allegro DVT Video Encoder", sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + dev_name(&dev->plat_dev->dev)); + + return 0; +} + +static int allegro_enum_fmt_vid(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + if (f->index) + return -EINVAL; + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + f->pixelformat = V4L2_PIX_FMT_NV12; + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + f->pixelformat = V4L2_PIX_FMT_H264; + break; + default: + return -EINVAL; + } + return 0; +} + +static int allegro_g_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct allegro_channel *channel = fh_to_channel(fh); + + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.width = channel->width; + f->fmt.pix.height = channel->height; + + f->fmt.pix.colorspace = channel->colorspace; + f->fmt.pix.ycbcr_enc = channel->ycbcr_enc; + f->fmt.pix.quantization = channel->quantization; + f->fmt.pix.xfer_func = channel->xfer_func; + + f->fmt.pix.pixelformat = channel->codec; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = channel->sizeimage_encoded; + + return 0; +} + +static int allegro_try_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + f->fmt.pix.field = V4L2_FIELD_NONE; + + f->fmt.pix.width = clamp_t(__u32, f->fmt.pix.width, + ALLEGRO_WIDTH_MIN, ALLEGRO_WIDTH_MAX); + f->fmt.pix.height = clamp_t(__u32, f->fmt.pix.height, + ALLEGRO_HEIGHT_MIN, ALLEGRO_HEIGHT_MAX); + + f->fmt.pix.pixelformat = V4L2_PIX_FMT_H264; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = + estimate_stream_size(f->fmt.pix.width, f->fmt.pix.height); + + return 0; +} + +static int allegro_g_fmt_vid_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct allegro_channel *channel = fh_to_channel(fh); + + f->fmt.pix.field = V4L2_FIELD_NONE; + + f->fmt.pix.width = channel->width; + f->fmt.pix.height = channel->height; + + f->fmt.pix.colorspace = channel->colorspace; + f->fmt.pix.ycbcr_enc = channel->ycbcr_enc; + f->fmt.pix.quantization = channel->quantization; + f->fmt.pix.xfer_func = channel->xfer_func; + + f->fmt.pix.pixelformat = channel->pixelformat; + f->fmt.pix.bytesperline = channel->stride; + f->fmt.pix.sizeimage = channel->sizeimage_raw; + + return 0; +} + +static int allegro_try_fmt_vid_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + f->fmt.pix.field = V4L2_FIELD_NONE; + + /* + * The firmware of the Allegro codec handles the padding internally + * and expects the visual frame size when configuring a channel. + * Therefore, unlike other encoder drivers, this driver does not round + * up the width and height to macroblock alignment and does not + * implement the selection api. + */ + f->fmt.pix.width = clamp_t(__u32, f->fmt.pix.width, + ALLEGRO_WIDTH_MIN, ALLEGRO_WIDTH_MAX); + f->fmt.pix.height = clamp_t(__u32, f->fmt.pix.height, + ALLEGRO_HEIGHT_MIN, ALLEGRO_HEIGHT_MAX); + + f->fmt.pix.pixelformat = V4L2_PIX_FMT_NV12; + f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 32); + f->fmt.pix.sizeimage = + f->fmt.pix.bytesperline * f->fmt.pix.height * 3 / 2; + + return 0; +} + +static int allegro_s_fmt_vid_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct allegro_channel *channel = fh_to_channel(fh); + int err; + + err = allegro_try_fmt_vid_out(file, fh, f); + if (err) + return err; + + channel->width = f->fmt.pix.width; + channel->height = f->fmt.pix.height; + channel->stride = f->fmt.pix.bytesperline; + channel->sizeimage_raw = f->fmt.pix.sizeimage; + + channel->colorspace = f->fmt.pix.colorspace; + channel->ycbcr_enc = f->fmt.pix.ycbcr_enc; + channel->quantization = f->fmt.pix.quantization; + channel->xfer_func = f->fmt.pix.xfer_func; + + channel->level = + select_minimum_h264_level(channel->width, channel->height); + channel->sizeimage_encoded = + estimate_stream_size(channel->width, channel->height); + + return 0; +} + +static int allegro_try_encoder_cmd(struct file *file, void *fh, + struct v4l2_encoder_cmd *cmd) +{ + switch (cmd->cmd) { + case V4L2_ENC_CMD_START: + cmd->flags = 0; + break; + case V4L2_ENC_CMD_STOP: + if (cmd->flags) + return -EINVAL; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int allegro_channel_cmd_stop(struct allegro_channel *channel) +{ + struct allegro_dev *dev = channel->dev; + struct vb2_v4l2_buffer *dst_buf; + + switch (allegro_get_state(channel)) { + case ALLEGRO_STATE_DRAIN: + case ALLEGRO_STATE_WAIT_FOR_BUFFER: + return -EBUSY; + case ALLEGRO_STATE_ENCODING: + allegro_set_state(channel, ALLEGRO_STATE_DRAIN); + break; + default: + return 0; + } + + /* If there are output buffers, they must be encoded */ + if (v4l2_m2m_num_src_bufs_ready(channel->fh.m2m_ctx) != 0) { + v4l2_dbg(1, debug, &dev->v4l2_dev, + "channel %d: CMD_STOP: continue encoding src buffers\n", + channel->mcu_channel_id); + return 0; + } + + /* If there are capture buffers, use it to signal EOS */ + dst_buf = v4l2_m2m_dst_buf_remove(channel->fh.m2m_ctx); + if (dst_buf) { + v4l2_dbg(1, debug, &dev->v4l2_dev, + "channel %d: CMD_STOP: signaling EOS\n", + channel->mcu_channel_id); + allegro_channel_buf_done(channel, dst_buf, VB2_BUF_STATE_DONE); + return 0; + } + + /* + * If there are no capture buffers, we need to wait for the next + * buffer to signal EOS. + */ + v4l2_dbg(1, debug, &dev->v4l2_dev, + "channel %d: CMD_STOP: wait for CAPTURE buffer to signal EOS\n", + channel->mcu_channel_id); + allegro_set_state(channel, ALLEGRO_STATE_WAIT_FOR_BUFFER); + + return 0; +} + +static int allegro_channel_cmd_start(struct allegro_channel *channel) +{ + switch (allegro_get_state(channel)) { + case ALLEGRO_STATE_DRAIN: + case ALLEGRO_STATE_WAIT_FOR_BUFFER: + return -EBUSY; + case ALLEGRO_STATE_STOPPED: + allegro_set_state(channel, ALLEGRO_STATE_ENCODING); + break; + default: + return 0; + } + + return 0; +} + +static int allegro_encoder_cmd(struct file *file, void *fh, + struct v4l2_encoder_cmd *cmd) +{ + struct allegro_channel *channel = fh_to_channel(fh); + int err; + + err = allegro_try_encoder_cmd(file, fh, cmd); + if (err) + return err; + + switch (cmd->cmd) { + case V4L2_ENC_CMD_STOP: + err = allegro_channel_cmd_stop(channel); + break; + case V4L2_ENC_CMD_START: + err = allegro_channel_cmd_start(channel); + break; + default: + err = -EINVAL; + break; + } + + return err; +} + +static int allegro_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + switch (fsize->pixel_format) { + case V4L2_PIX_FMT_H264: + case V4L2_PIX_FMT_NV12: + break; + default: + return -EINVAL; + } + + if (fsize->index) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; + fsize->stepwise.min_width = ALLEGRO_WIDTH_MIN; + fsize->stepwise.max_width = ALLEGRO_WIDTH_MAX; + fsize->stepwise.step_width = 1; + fsize->stepwise.min_height = ALLEGRO_HEIGHT_MIN; + fsize->stepwise.max_height = ALLEGRO_HEIGHT_MAX; + fsize->stepwise.step_height = 1; + + return 0; +} + +static int allegro_ioctl_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct v4l2_fh *fh = file->private_data; + struct allegro_channel *channel = fh_to_channel(fh); + int err; + + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + err = allegro_create_channel(channel); + if (err) + return err; + } + + return v4l2_m2m_streamon(file, fh->m2m_ctx, type); +} + +static int allegro_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_EOS: + return v4l2_event_subscribe(fh, sub, 0, NULL); + default: + return v4l2_ctrl_subscribe_event(fh, sub); + } +} + +static const struct v4l2_ioctl_ops allegro_ioctl_ops = { + .vidioc_querycap = allegro_querycap, + .vidioc_enum_fmt_vid_cap = allegro_enum_fmt_vid, + .vidioc_enum_fmt_vid_out = allegro_enum_fmt_vid, + .vidioc_g_fmt_vid_cap = allegro_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = allegro_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = allegro_try_fmt_vid_cap, + .vidioc_g_fmt_vid_out = allegro_g_fmt_vid_out, + .vidioc_try_fmt_vid_out = allegro_try_fmt_vid_out, + .vidioc_s_fmt_vid_out = allegro_s_fmt_vid_out, + + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .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_streamon = allegro_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + + .vidioc_try_encoder_cmd = allegro_try_encoder_cmd, + .vidioc_encoder_cmd = allegro_encoder_cmd, + .vidioc_enum_framesizes = allegro_enum_framesizes, + + .vidioc_subscribe_event = allegro_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static const struct v4l2_file_operations allegro_fops = { + .owner = THIS_MODULE, + .open = allegro_open, + .release = allegro_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +static int allegro_register_device(struct allegro_dev *dev) +{ + struct video_device *video_dev = &dev->video_dev; + + strscpy(video_dev->name, "allegro", sizeof(video_dev->name)); + video_dev->fops = &allegro_fops; + video_dev->ioctl_ops = &allegro_ioctl_ops; + video_dev->release = video_device_release_empty; + video_dev->lock = &dev->lock; + video_dev->v4l2_dev = &dev->v4l2_dev; + video_dev->vfl_dir = VFL_DIR_M2M; + video_dev->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; + video_set_drvdata(video_dev, dev); + + return video_register_device(video_dev, VFL_TYPE_GRABBER, 0); +} + +static void allegro_device_run(void *priv) +{ + struct allegro_channel *channel = priv; + struct allegro_dev *dev = channel->dev; + struct vb2_v4l2_buffer *src_buf; + struct vb2_v4l2_buffer *dst_buf; + dma_addr_t src_y; + dma_addr_t src_uv; + dma_addr_t dst_addr; + unsigned long dst_size; + + dst_buf = v4l2_m2m_next_dst_buf(channel->fh.m2m_ctx); + dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); + dst_size = vb2_plane_size(&dst_buf->vb2_buf, 0); + allegro_mcu_send_put_stream_buffer(dev, channel, dst_addr, dst_size); + + src_buf = v4l2_m2m_next_src_buf(channel->fh.m2m_ctx); + src_buf->sequence = channel->osequence++; + + src_y = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); + src_uv = src_y + (channel->stride * channel->height); + allegro_mcu_send_encode_frame(dev, channel, src_y, src_uv); +} + +static const struct v4l2_m2m_ops allegro_m2m_ops = { + .device_run = allegro_device_run, +}; + +static int allegro_mcu_hw_init(struct allegro_dev *dev, + const struct fw_info *info) +{ + int err; + + allegro_mbox_init(dev, &dev->mbox_command, + info->mailbox_cmd, info->mailbox_size); + allegro_mbox_init(dev, &dev->mbox_status, + info->mailbox_status, info->mailbox_size); + + allegro_mcu_enable_interrupts(dev); + + /* The mcu sends INIT after reset. */ + allegro_mcu_start(dev); + err = allegro_mcu_wait_for_init_timeout(dev, 5000); + if (err < 0) { + v4l2_err(&dev->v4l2_dev, + "mcu did not send INIT after reset\n"); + err = -EIO; + goto err_disable_interrupts; + } + + err = allegro_alloc_buffer(dev, &dev->suballocator, + info->suballocator_size); + if (err) { + v4l2_err(&dev->v4l2_dev, + "failed to allocate %zu bytes for suballocator\n", + info->suballocator_size); + goto err_reset_mcu; + } + + allegro_mcu_send_init(dev, dev->suballocator.paddr, + dev->suballocator.size); + err = allegro_mcu_wait_for_init_timeout(dev, 5000); + if (err < 0) { + v4l2_err(&dev->v4l2_dev, + "mcu failed to configure sub-allocator\n"); + err = -EIO; + goto err_free_suballocator; + } + + return 0; + +err_free_suballocator: + allegro_free_buffer(dev, &dev->suballocator); +err_reset_mcu: + allegro_mcu_reset(dev); +err_disable_interrupts: + allegro_mcu_disable_interrupts(dev); + + return err; +} + +static int allegro_mcu_hw_deinit(struct allegro_dev *dev) +{ + int err; + + err = allegro_mcu_reset(dev); + if (err) + v4l2_warn(&dev->v4l2_dev, + "mcu failed to enter sleep state\n"); + + err = allegro_mcu_disable_interrupts(dev); + if (err) + v4l2_warn(&dev->v4l2_dev, + "failed to disable interrupts\n"); + + allegro_free_buffer(dev, &dev->suballocator); + + return 0; +} + +static void allegro_fw_callback(const struct firmware *fw, void *context) +{ + struct allegro_dev *dev = context; + const char *fw_codec_name = "al5e.fw"; + const struct firmware *fw_codec; + int err; + const struct fw_info *info; + + if (!fw) + return; + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "requesting codec firmware '%s'\n", fw_codec_name); + err = request_firmware(&fw_codec, fw_codec_name, &dev->plat_dev->dev); + if (err) + goto err_release_firmware; + + info = allegro_get_firmware_info(dev, fw, fw_codec); + if (!info) { + v4l2_err(&dev->v4l2_dev, "firmware is not supported\n"); + goto err_release_firmware_codec; + } + + v4l2_info(&dev->v4l2_dev, + "using mcu firmware version '%s'\n", info->version); + + /* Ensure that the mcu is sleeping at the reset vector */ + err = allegro_mcu_reset(dev); + if (err) { + v4l2_err(&dev->v4l2_dev, "failed to reset mcu\n"); + goto err_release_firmware_codec; + } + + allegro_copy_firmware(dev, fw->data, fw->size); + allegro_copy_fw_codec(dev, fw_codec->data, fw_codec->size); + + err = allegro_mcu_hw_init(dev, info); + if (err) { + v4l2_err(&dev->v4l2_dev, "failed to initialize mcu\n"); + goto err_free_fw_codec; + } + + dev->m2m_dev = v4l2_m2m_init(&allegro_m2m_ops); + if (IS_ERR(dev->m2m_dev)) { + v4l2_err(&dev->v4l2_dev, "failed to init mem2mem device\n"); + goto err_mcu_hw_deinit; + } + + err = allegro_register_device(dev); + if (err) { + v4l2_err(&dev->v4l2_dev, "failed to register video device\n"); + goto err_m2m_release; + } + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "allegro codec registered as /dev/video%d\n", + dev->video_dev.num); + + release_firmware(fw_codec); + release_firmware(fw); + + return; + +err_m2m_release: + v4l2_m2m_release(dev->m2m_dev); + dev->m2m_dev = NULL; +err_mcu_hw_deinit: + allegro_mcu_hw_deinit(dev); +err_free_fw_codec: + allegro_free_fw_codec(dev); +err_release_firmware_codec: + release_firmware(fw_codec); +err_release_firmware: + release_firmware(fw); +} + +static int allegro_firmware_request_nowait(struct allegro_dev *dev) +{ + const char *fw = "al5e_b.fw"; + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "requesting firmware '%s'\n", fw); + return request_firmware_nowait(THIS_MODULE, true, fw, + &dev->plat_dev->dev, GFP_KERNEL, dev, + allegro_fw_callback); +} + +static int allegro_probe(struct platform_device *pdev) +{ + struct allegro_dev *dev; + struct resource *res, *sram_res; + int ret; + int irq; + void __iomem *regs, *sram_regs; + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + dev->plat_dev = pdev; + init_completion(&dev->init_complete); + INIT_LIST_HEAD(&dev->channels); + + mutex_init(&dev->lock); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); + if (!res) { + dev_err(&pdev->dev, + "regs resource missing from device tree\n"); + return -EINVAL; + } + regs = devm_ioremap_nocache(&pdev->dev, res->start, resource_size(res)); + if (IS_ERR(regs)) { + dev_err(&pdev->dev, "failed to map registers\n"); + return PTR_ERR(regs); + } + dev->regmap = devm_regmap_init_mmio(&pdev->dev, regs, + &allegro_regmap_config); + if (IS_ERR(dev->regmap)) { + dev_err(&pdev->dev, "failed to init regmap\n"); + return PTR_ERR(dev->regmap); + } + + sram_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram"); + if (!sram_res) { + dev_err(&pdev->dev, + "sram resource missing from device tree\n"); + return -EINVAL; + } + sram_regs = devm_ioremap_nocache(&pdev->dev, + sram_res->start, + resource_size(sram_res)); + if (IS_ERR(sram_regs)) { + dev_err(&pdev->dev, "failed to map sram\n"); + return PTR_ERR(sram_regs); + } + dev->sram = devm_regmap_init_mmio(&pdev->dev, sram_regs, + &allegro_sram_config); + if (IS_ERR(dev->sram)) { + dev_err(&pdev->dev, "failed to init sram\n"); + return PTR_ERR(dev->sram); + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get irq resource\n"); + return irq; + } + ret = devm_request_threaded_irq(&pdev->dev, irq, + allegro_hardirq, + allegro_irq_thread, + IRQF_SHARED, dev_name(&pdev->dev), dev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request irq: %d\n", ret); + return ret; + } + + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); + if (ret) + return ret; + + platform_set_drvdata(pdev, dev); + + ret = allegro_firmware_request_nowait(dev); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, + "failed to request firmware: %d\n", ret); + return ret; + } + + return 0; +} + +static int allegro_remove(struct platform_device *pdev) +{ + struct allegro_dev *dev = platform_get_drvdata(pdev); + + video_unregister_device(&dev->video_dev); + if (dev->m2m_dev) + v4l2_m2m_release(dev->m2m_dev); + allegro_mcu_hw_deinit(dev); + allegro_free_fw_codec(dev); + + v4l2_device_unregister(&dev->v4l2_dev); + + return 0; +} + +static const struct of_device_id allegro_dt_ids[] = { + { .compatible = "allegro,al5e-1.1" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, allegro_dt_ids); + +static struct platform_driver allegro_driver = { + .probe = allegro_probe, + .remove = allegro_remove, + .driver = { + .name = "allegro", + .of_match_table = of_match_ptr(allegro_dt_ids), + }, +}; + +module_platform_driver(allegro_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michael Tretter <kernel@pengutronix.de>"); +MODULE_DESCRIPTION("Allegro DVT encoder driver"); diff --git a/drivers/staging/media/allegro-dvt/nal-h264.c b/drivers/staging/media/allegro-dvt/nal-h264.c new file mode 100644 index 000000000000..4e14b77851e1 --- /dev/null +++ b/drivers/staging/media/allegro-dvt/nal-h264.c @@ -0,0 +1,1001 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Pengutronix, Michael Tretter <kernel@pengutronix.de> + * + * Convert NAL units between raw byte sequence payloads (RBSP) and C structs + * + * The conversion is defined in "ITU-T Rec. H.264 (04/2017) Advanced video + * coding for generic audiovisual services". Decoder drivers may use the + * parser to parse RBSP from encoded streams and configure the hardware, if + * the hardware is not able to parse RBSP itself. Encoder drivers may use the + * generator to generate the RBSP for SPS/PPS nal units and add them to the + * encoded stream if the hardware does not generate the units. + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/string.h> +#include <linux/v4l2-controls.h> + +#include <linux/device.h> +#include <linux/export.h> +#include <linux/log2.h> + +#include "nal-h264.h" + +/* + * See Rec. ITU-T H.264 (04/2017) Table 7-1 – NAL unit type codes, syntax + * element categories, and NAL unit type classes + */ +enum nal_unit_type { + SEQUENCE_PARAMETER_SET = 7, + PICTURE_PARAMETER_SET = 8, + FILLER_DATA = 12, +}; + +struct rbsp; + +struct nal_h264_ops { + int (*rbsp_bit)(struct rbsp *rbsp, int *val); + int (*rbsp_bits)(struct rbsp *rbsp, int n, unsigned int *val); + int (*rbsp_uev)(struct rbsp *rbsp, unsigned int *val); + int (*rbsp_sev)(struct rbsp *rbsp, int *val); +}; + +/** + * struct rbsp - State object for handling a raw byte sequence payload + * @data: pointer to the data of the rbsp + * @size: maximum size of the data of the rbsp + * @pos: current bit position inside the rbsp + * @num_consecutive_zeros: number of zeros before @pos + * @ops: per datatype functions for interacting with the rbsp + * @error: an error occurred while handling the rbsp + * + * This struct is passed around the various parsing functions and tracks the + * current position within the raw byte sequence payload. + * + * The @ops field allows to separate the operation, i.e., reading/writing a + * value from/to that rbsp, from the structure of the NAL unit. This allows to + * have a single function for iterating the NAL unit, while @ops has function + * pointers for handling each type in the rbsp. + */ +struct rbsp { + u8 *data; + size_t size; + unsigned int pos; + unsigned int num_consecutive_zeros; + struct nal_h264_ops *ops; + int error; +}; + +static void rbsp_init(struct rbsp *rbsp, void *addr, size_t size, + struct nal_h264_ops *ops) +{ + if (!rbsp) + return; + + rbsp->data = addr; + rbsp->size = size; + rbsp->pos = 0; + rbsp->ops = ops; + rbsp->error = 0; +} + +/** + * nal_h264_profile_from_v4l2() - Get profile_idc for v4l2 h264 profile + * @profile: the profile as &enum v4l2_mpeg_video_h264_profile + * + * Convert the &enum v4l2_mpeg_video_h264_profile to profile_idc as specified + * in Rec. ITU-T H.264 (04/2017) A.2. + * + * Return: the profile_idc for the passed level + */ +int nal_h264_profile_from_v4l2(enum v4l2_mpeg_video_h264_profile profile) +{ + switch (profile) { + case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: + return 66; + case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: + return 77; + case V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED: + return 88; + case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: + return 100; + default: + return -EINVAL; + } +} + +/** + * nal_h264_level_from_v4l2() - Get level_idc for v4l2 h264 level + * @level: the level as &enum v4l2_mpeg_video_h264_level + * + * Convert the &enum v4l2_mpeg_video_h264_level to level_idc as specified in + * Rec. ITU-T H.264 (04/2017) A.3.2. + * + * Return: the level_idc for the passed level + */ +int nal_h264_level_from_v4l2(enum v4l2_mpeg_video_h264_level level) +{ + switch (level) { + case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: + return 10; + case V4L2_MPEG_VIDEO_H264_LEVEL_1B: + return 9; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: + return 11; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: + return 12; + case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: + return 13; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: + return 20; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: + return 21; + case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: + return 22; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: + return 30; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: + return 31; + case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: + return 32; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: + return 40; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_1: + return 41; + case V4L2_MPEG_VIDEO_H264_LEVEL_4_2: + return 42; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: + return 50; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: + return 51; + default: + return -EINVAL; + } +} + +static int rbsp_read_bits(struct rbsp *rbsp, int n, unsigned int *value); +static int rbsp_write_bits(struct rbsp *rbsp, int n, unsigned int value); + +/* + * When reading or writing, the emulation_prevention_three_byte is detected + * only when the 2 one bits need to be inserted. Therefore, we are not + * actually adding the 0x3 byte, but the 2 one bits and the six 0 bits of the + * next byte. + */ +#define EMULATION_PREVENTION_THREE_BYTE (0x3 << 6) + +static int add_emulation_prevention_three_byte(struct rbsp *rbsp) +{ + rbsp->num_consecutive_zeros = 0; + rbsp_write_bits(rbsp, 8, EMULATION_PREVENTION_THREE_BYTE); + + return 0; +} + +static int discard_emulation_prevention_three_byte(struct rbsp *rbsp) +{ + unsigned int tmp = 0; + + rbsp->num_consecutive_zeros = 0; + rbsp_read_bits(rbsp, 8, &tmp); + if (tmp != EMULATION_PREVENTION_THREE_BYTE) + return -EINVAL; + + return 0; +} + +static inline int rbsp_read_bit(struct rbsp *rbsp) +{ + int shift; + int ofs; + int bit; + int err; + + if (rbsp->num_consecutive_zeros == 22) { + err = discard_emulation_prevention_three_byte(rbsp); + if (err) + return err; + } + + shift = 7 - (rbsp->pos % 8); + ofs = rbsp->pos / 8; + if (ofs >= rbsp->size) + return -EINVAL; + + bit = (rbsp->data[ofs] >> shift) & 1; + + rbsp->pos++; + + if (bit == 1 || + (rbsp->num_consecutive_zeros < 7 && (rbsp->pos % 8 == 0))) + rbsp->num_consecutive_zeros = 0; + else + rbsp->num_consecutive_zeros++; + + return bit; +} + +static inline int rbsp_write_bit(struct rbsp *rbsp, bool value) +{ + int shift; + int ofs; + + if (rbsp->num_consecutive_zeros == 22) + add_emulation_prevention_three_byte(rbsp); + + shift = 7 - (rbsp->pos % 8); + ofs = rbsp->pos / 8; + if (ofs >= rbsp->size) + return -EINVAL; + + rbsp->data[ofs] &= ~(1 << shift); + rbsp->data[ofs] |= value << shift; + + rbsp->pos++; + + if (value == 1 || + (rbsp->num_consecutive_zeros < 7 && (rbsp->pos % 8 == 0))) { + rbsp->num_consecutive_zeros = 0; + } else { + rbsp->num_consecutive_zeros++; + } + + return 0; +} + +static inline int rbsp_read_bits(struct rbsp *rbsp, int n, unsigned int *value) +{ + int i; + int bit; + unsigned int tmp = 0; + + if (n > 8 * sizeof(*value)) + return -EINVAL; + + for (i = n; i > 0; i--) { + bit = rbsp_read_bit(rbsp); + if (bit < 0) + return bit; + tmp |= bit << (i - 1); + } + + if (value) + *value = tmp; + + return 0; +} + +static int rbsp_write_bits(struct rbsp *rbsp, int n, unsigned int value) +{ + int ret; + + if (n > 8 * sizeof(value)) + return -EINVAL; + + while (n--) { + ret = rbsp_write_bit(rbsp, (value >> n) & 1); + if (ret) + return ret; + } + + return 0; +} + +static int rbsp_read_uev(struct rbsp *rbsp, unsigned int *value) +{ + int leading_zero_bits = 0; + unsigned int tmp = 0; + int ret; + + while ((ret = rbsp_read_bit(rbsp)) == 0) + leading_zero_bits++; + if (ret < 0) + return ret; + + if (leading_zero_bits > 0) { + ret = rbsp_read_bits(rbsp, leading_zero_bits, &tmp); + if (ret) + return ret; + } + + if (value) + *value = (1 << leading_zero_bits) - 1 + tmp; + + return 0; +} + +static int rbsp_write_uev(struct rbsp *rbsp, unsigned int *value) +{ + int ret; + int leading_zero_bits; + + if (!value) + return -EINVAL; + + leading_zero_bits = ilog2(*value + 1); + + ret = rbsp_write_bits(rbsp, leading_zero_bits, 0); + if (ret) + return ret; + + return rbsp_write_bits(rbsp, leading_zero_bits + 1, *value + 1); +} + +static int rbsp_read_sev(struct rbsp *rbsp, int *value) +{ + int ret; + unsigned int tmp; + + ret = rbsp_read_uev(rbsp, &tmp); + if (ret) + return ret; + + if (value) { + if (tmp & 1) + *value = (tmp + 1) / 2; + else + *value = -(tmp / 2); + } + + return 0; +} + +static int rbsp_write_sev(struct rbsp *rbsp, int *value) +{ + unsigned int tmp; + + if (!value) + return -EINVAL; + + if (*value > 0) + tmp = (2 * (*value)) | 1; + else + tmp = -2 * (*value); + + return rbsp_write_uev(rbsp, &tmp); +} + +static int __rbsp_write_bit(struct rbsp *rbsp, int *value) +{ + return rbsp_write_bit(rbsp, *value); +} + +static int __rbsp_write_bits(struct rbsp *rbsp, int n, unsigned int *value) +{ + return rbsp_write_bits(rbsp, n, *value); +} + +static struct nal_h264_ops write = { + .rbsp_bit = __rbsp_write_bit, + .rbsp_bits = __rbsp_write_bits, + .rbsp_uev = rbsp_write_uev, + .rbsp_sev = rbsp_write_sev, +}; + +static int __rbsp_read_bit(struct rbsp *rbsp, int *value) +{ + int tmp = rbsp_read_bit(rbsp); + + if (tmp < 0) + return tmp; + *value = tmp; + + return 0; +} + +static struct nal_h264_ops read = { + .rbsp_bit = __rbsp_read_bit, + .rbsp_bits = rbsp_read_bits, + .rbsp_uev = rbsp_read_uev, + .rbsp_sev = rbsp_read_sev, +}; + +static inline void rbsp_bit(struct rbsp *rbsp, int *value) +{ + if (rbsp->error) + return; + rbsp->error = rbsp->ops->rbsp_bit(rbsp, value); +} + +static inline void rbsp_bits(struct rbsp *rbsp, int n, int *value) +{ + if (rbsp->error) + return; + rbsp->error = rbsp->ops->rbsp_bits(rbsp, n, value); +} + +static inline void rbsp_uev(struct rbsp *rbsp, unsigned int *value) +{ + if (rbsp->error) + return; + rbsp->error = rbsp->ops->rbsp_uev(rbsp, value); +} + +static inline void rbsp_sev(struct rbsp *rbsp, int *value) +{ + if (rbsp->error) + return; + rbsp->error = rbsp->ops->rbsp_sev(rbsp, value); +} + +static void nal_h264_rbsp_trailing_bits(struct rbsp *rbsp) +{ + unsigned int rbsp_stop_one_bit = 1; + unsigned int rbsp_alignment_zero_bit = 0; + + rbsp_bit(rbsp, &rbsp_stop_one_bit); + rbsp_bits(rbsp, round_up(rbsp->pos, 8) - rbsp->pos, + &rbsp_alignment_zero_bit); +} + +static void nal_h264_write_start_code_prefix(struct rbsp *rbsp) +{ + u8 *p = rbsp->data + DIV_ROUND_UP(rbsp->pos, 8); + int i = 4; + + if (DIV_ROUND_UP(rbsp->pos, 8) + i > rbsp->size) { + rbsp->error = -EINVAL; + return; + } + + p[0] = 0x00; + p[1] = 0x00; + p[2] = 0x00; + p[3] = 0x01; + + rbsp->pos += i * 8; +} + +static void nal_h264_read_start_code_prefix(struct rbsp *rbsp) +{ + u8 *p = rbsp->data + DIV_ROUND_UP(rbsp->pos, 8); + int i = 4; + + if (DIV_ROUND_UP(rbsp->pos, 8) + i > rbsp->size) { + rbsp->error = -EINVAL; + return; + } + + if (p[0] != 0x00 || p[1] != 0x00 || p[2] != 0x00 || p[3] != 0x01) { + rbsp->error = -EINVAL; + return; + } + + rbsp->pos += i * 8; +} + +static void nal_h264_write_filler_data(struct rbsp *rbsp) +{ + u8 *p = rbsp->data + DIV_ROUND_UP(rbsp->pos, 8); + int i; + + /* Keep 1 byte extra for terminating the NAL unit */ + i = rbsp->size - DIV_ROUND_UP(rbsp->pos, 8) - 1; + memset(p, 0xff, i); + rbsp->pos += i * 8; +} + +static void nal_h264_read_filler_data(struct rbsp *rbsp) +{ + u8 *p = rbsp->data + DIV_ROUND_UP(rbsp->pos, 8); + + while (*p == 0xff) { + if (DIV_ROUND_UP(rbsp->pos, 8) > rbsp->size) { + rbsp->error = -EINVAL; + return; + } + + p++; + rbsp->pos += 8; + } +} + +static void nal_h264_rbsp_hrd_parameters(struct rbsp *rbsp, + struct nal_h264_hrd_parameters *hrd) +{ + unsigned int i; + + if (!hrd) { + rbsp->error = -EINVAL; + return; + } + + rbsp_uev(rbsp, &hrd->cpb_cnt_minus1); + rbsp_bits(rbsp, 4, &hrd->bit_rate_scale); + rbsp_bits(rbsp, 4, &hrd->cpb_size_scale); + + for (i = 0; i <= hrd->cpb_cnt_minus1; i++) { + rbsp_uev(rbsp, &hrd->bit_rate_value_minus1[i]); + rbsp_uev(rbsp, &hrd->cpb_size_value_minus1[i]); + rbsp_bit(rbsp, &hrd->cbr_flag[i]); + } + + rbsp_bits(rbsp, 5, &hrd->initial_cpb_removal_delay_length_minus1); + rbsp_bits(rbsp, 5, &hrd->cpb_removal_delay_length_minus1); + rbsp_bits(rbsp, 5, &hrd->dpb_output_delay_length_minus1); + rbsp_bits(rbsp, 5, &hrd->time_offset_length); +} + +static void nal_h264_rbsp_vui_parameters(struct rbsp *rbsp, + struct nal_h264_vui_parameters *vui) +{ + if (!vui) { + rbsp->error = -EINVAL; + return; + } + + rbsp_bit(rbsp, &vui->aspect_ratio_info_present_flag); + if (vui->aspect_ratio_info_present_flag) { + rbsp_bits(rbsp, 8, &vui->aspect_ratio_idc); + if (vui->aspect_ratio_idc == 255) { + rbsp_bits(rbsp, 16, &vui->sar_width); + rbsp_bits(rbsp, 16, &vui->sar_height); + } + } + + rbsp_bit(rbsp, &vui->overscan_info_present_flag); + if (vui->overscan_info_present_flag) + rbsp_bit(rbsp, &vui->overscan_appropriate_flag); + + rbsp_bit(rbsp, &vui->video_signal_type_present_flag); + if (vui->video_signal_type_present_flag) { + rbsp_bits(rbsp, 3, &vui->video_format); + rbsp_bit(rbsp, &vui->video_full_range_flag); + + rbsp_bit(rbsp, &vui->colour_description_present_flag); + if (vui->colour_description_present_flag) { + rbsp_bits(rbsp, 8, &vui->colour_primaries); + rbsp_bits(rbsp, 8, &vui->transfer_characteristics); + rbsp_bits(rbsp, 8, &vui->matrix_coefficients); + } + } + + rbsp_bit(rbsp, &vui->chroma_loc_info_present_flag); + if (vui->chroma_loc_info_present_flag) { + rbsp_uev(rbsp, &vui->chroma_sample_loc_type_top_field); + rbsp_uev(rbsp, &vui->chroma_sample_loc_type_bottom_field); + } + + rbsp_bit(rbsp, &vui->timing_info_present_flag); + if (vui->timing_info_present_flag) { + rbsp_bits(rbsp, 32, &vui->num_units_in_tick); + rbsp_bits(rbsp, 32, &vui->time_scale); + rbsp_bit(rbsp, &vui->fixed_frame_rate_flag); + } + + rbsp_bit(rbsp, &vui->nal_hrd_parameters_present_flag); + if (vui->nal_hrd_parameters_present_flag) + nal_h264_rbsp_hrd_parameters(rbsp, &vui->nal_hrd_parameters); + + rbsp_bit(rbsp, &vui->vcl_hrd_parameters_present_flag); + if (vui->vcl_hrd_parameters_present_flag) + nal_h264_rbsp_hrd_parameters(rbsp, &vui->vcl_hrd_parameters); + + if (vui->nal_hrd_parameters_present_flag || + vui->vcl_hrd_parameters_present_flag) + rbsp_bit(rbsp, &vui->low_delay_hrd_flag); + + rbsp_bit(rbsp, &vui->pic_struct_present_flag); + + rbsp_bit(rbsp, &vui->bitstream_restriction_flag); + if (vui->bitstream_restriction_flag) { + rbsp_bit(rbsp, &vui->motion_vectors_over_pic_boundaries_flag); + rbsp_uev(rbsp, &vui->max_bytes_per_pic_denom); + rbsp_uev(rbsp, &vui->max_bits_per_mb_denom); + rbsp_uev(rbsp, &vui->log2_max_mv_length_horizontal); + rbsp_uev(rbsp, &vui->log21_max_mv_length_vertical); + rbsp_uev(rbsp, &vui->max_num_reorder_frames); + rbsp_uev(rbsp, &vui->max_dec_frame_buffering); + } +} + +static void nal_h264_rbsp_sps(struct rbsp *rbsp, struct nal_h264_sps *sps) +{ + unsigned int i; + + if (!sps) { + rbsp->error = -EINVAL; + return; + } + + rbsp_bits(rbsp, 8, &sps->profile_idc); + rbsp_bit(rbsp, &sps->constraint_set0_flag); + rbsp_bit(rbsp, &sps->constraint_set1_flag); + rbsp_bit(rbsp, &sps->constraint_set2_flag); + rbsp_bit(rbsp, &sps->constraint_set3_flag); + rbsp_bit(rbsp, &sps->constraint_set4_flag); + rbsp_bit(rbsp, &sps->constraint_set5_flag); + rbsp_bits(rbsp, 2, &sps->reserved_zero_2bits); + rbsp_bits(rbsp, 8, &sps->level_idc); + + rbsp_uev(rbsp, &sps->seq_parameter_set_id); + + if (sps->profile_idc == 100 || sps->profile_idc == 110 || + sps->profile_idc == 122 || sps->profile_idc == 244 || + sps->profile_idc == 44 || sps->profile_idc == 83 || + sps->profile_idc == 86 || sps->profile_idc == 118 || + sps->profile_idc == 128 || sps->profile_idc == 138 || + sps->profile_idc == 139 || sps->profile_idc == 134 || + sps->profile_idc == 135) { + rbsp_uev(rbsp, &sps->chroma_format_idc); + + if (sps->chroma_format_idc == 3) + rbsp_bit(rbsp, &sps->separate_colour_plane_flag); + rbsp_uev(rbsp, &sps->bit_depth_luma_minus8); + rbsp_uev(rbsp, &sps->bit_depth_chroma_minus8); + rbsp_bit(rbsp, &sps->qpprime_y_zero_transform_bypass_flag); + rbsp_bit(rbsp, &sps->seq_scaling_matrix_present_flag); + if (sps->seq_scaling_matrix_present_flag) + rbsp->error = -EINVAL; + } + + rbsp_uev(rbsp, &sps->log2_max_frame_num_minus4); + + rbsp_uev(rbsp, &sps->pic_order_cnt_type); + switch (sps->pic_order_cnt_type) { + case 0: + rbsp_uev(rbsp, &sps->log2_max_pic_order_cnt_lsb_minus4); + break; + case 1: + rbsp_bit(rbsp, &sps->delta_pic_order_always_zero_flag); + rbsp_sev(rbsp, &sps->offset_for_non_ref_pic); + rbsp_sev(rbsp, &sps->offset_for_top_to_bottom_field); + + rbsp_uev(rbsp, &sps->num_ref_frames_in_pic_order_cnt_cycle); + for (i = 0; i < sps->num_ref_frames_in_pic_order_cnt_cycle; i++) + rbsp_sev(rbsp, &sps->offset_for_ref_frame[i]); + break; + default: + rbsp->error = -EINVAL; + break; + } + + rbsp_uev(rbsp, &sps->max_num_ref_frames); + rbsp_bit(rbsp, &sps->gaps_in_frame_num_value_allowed_flag); + rbsp_uev(rbsp, &sps->pic_width_in_mbs_minus1); + rbsp_uev(rbsp, &sps->pic_height_in_map_units_minus1); + + rbsp_bit(rbsp, &sps->frame_mbs_only_flag); + if (!sps->frame_mbs_only_flag) + rbsp_bit(rbsp, &sps->mb_adaptive_frame_field_flag); + + rbsp_bit(rbsp, &sps->direct_8x8_inference_flag); + + rbsp_bit(rbsp, &sps->frame_cropping_flag); + if (sps->frame_cropping_flag) { + rbsp_uev(rbsp, &sps->crop_left); + rbsp_uev(rbsp, &sps->crop_right); + rbsp_uev(rbsp, &sps->crop_top); + rbsp_uev(rbsp, &sps->crop_bottom); + } + + rbsp_bit(rbsp, &sps->vui_parameters_present_flag); + if (sps->vui_parameters_present_flag) + nal_h264_rbsp_vui_parameters(rbsp, &sps->vui); +} + +static void nal_h264_rbsp_pps(struct rbsp *rbsp, struct nal_h264_pps *pps) +{ + int i; + + rbsp_uev(rbsp, &pps->pic_parameter_set_id); + rbsp_uev(rbsp, &pps->seq_parameter_set_id); + rbsp_bit(rbsp, &pps->entropy_coding_mode_flag); + rbsp_bit(rbsp, &pps->bottom_field_pic_order_in_frame_present_flag); + rbsp_uev(rbsp, &pps->num_slice_groups_minus1); + if (pps->num_slice_groups_minus1 > 0) { + rbsp_uev(rbsp, &pps->slice_group_map_type); + switch (pps->slice_group_map_type) { + case 0: + for (i = 0; i < pps->num_slice_groups_minus1; i++) + rbsp_uev(rbsp, &pps->run_length_minus1[i]); + break; + case 2: + for (i = 0; i < pps->num_slice_groups_minus1; i++) { + rbsp_uev(rbsp, &pps->top_left[i]); + rbsp_uev(rbsp, &pps->bottom_right[i]); + } + break; + case 3: case 4: case 5: + rbsp_bit(rbsp, &pps->slice_group_change_direction_flag); + rbsp_uev(rbsp, &pps->slice_group_change_rate_minus1); + break; + case 6: + rbsp_uev(rbsp, &pps->pic_size_in_map_units_minus1); + for (i = 0; i < pps->pic_size_in_map_units_minus1; i++) + rbsp_bits(rbsp, + order_base_2(pps->num_slice_groups_minus1 + 1), + &pps->slice_group_id[i]); + break; + default: + break; + } + } + rbsp_uev(rbsp, &pps->num_ref_idx_l0_default_active_minus1); + rbsp_uev(rbsp, &pps->num_ref_idx_l1_default_active_minus1); + rbsp_bit(rbsp, &pps->weighted_pred_flag); + rbsp_bits(rbsp, 2, &pps->weighted_bipred_idc); + rbsp_sev(rbsp, &pps->pic_init_qp_minus26); + rbsp_sev(rbsp, &pps->pic_init_qs_minus26); + rbsp_sev(rbsp, &pps->chroma_qp_index_offset); + rbsp_bit(rbsp, &pps->deblocking_filter_control_present_flag); + rbsp_bit(rbsp, &pps->constrained_intra_pred_flag); + rbsp_bit(rbsp, &pps->redundant_pic_cnt_present_flag); + if (/* more_rbsp_data() */ false) { + rbsp_bit(rbsp, &pps->transform_8x8_mode_flag); + rbsp_bit(rbsp, &pps->pic_scaling_matrix_present_flag); + if (pps->pic_scaling_matrix_present_flag) + rbsp->error = -EINVAL; + rbsp_sev(rbsp, &pps->second_chroma_qp_index_offset); + } +} + +/** + * nal_h264_write_sps() - Write SPS NAL unit into RBSP format + * @dev: device pointer + * @dest: the buffer that is filled with RBSP data + * @n: maximum size of @dest in bytes + * @sps: &struct nal_h264_sps to convert to RBSP + * + * Convert @sps to RBSP data and write it into @dest. + * + * The size of the SPS NAL unit is not known in advance and this function will + * fail, if @dest does not hold sufficient space for the SPS NAL unit. + * + * Return: number of bytes written to @dest or negative error code + */ +ssize_t nal_h264_write_sps(const struct device *dev, + void *dest, size_t n, struct nal_h264_sps *sps) +{ + struct rbsp rbsp; + unsigned int forbidden_zero_bit = 0; + unsigned int nal_ref_idc = 0; + unsigned int nal_unit_type = SEQUENCE_PARAMETER_SET; + + if (!dest) + return -EINVAL; + + rbsp_init(&rbsp, dest, n, &write); + + nal_h264_write_start_code_prefix(&rbsp); + + rbsp_bit(&rbsp, &forbidden_zero_bit); + rbsp_bits(&rbsp, 2, &nal_ref_idc); + rbsp_bits(&rbsp, 5, &nal_unit_type); + + nal_h264_rbsp_sps(&rbsp, sps); + + nal_h264_rbsp_trailing_bits(&rbsp); + + if (rbsp.error) + return rbsp.error; + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_h264_write_sps); + +/** + * nal_h264_read_sps() - Read SPS NAL unit from RBSP format + * @dev: device pointer + * @sps: the &struct nal_h264_sps to fill from the RBSP data + * @src: the buffer that contains the RBSP data + * @n: size of @src in bytes + * + * Read RBSP data from @src and use it to fill @sps. + * + * Return: number of bytes read from @src or negative error code + */ +ssize_t nal_h264_read_sps(const struct device *dev, + struct nal_h264_sps *sps, void *src, size_t n) +{ + struct rbsp rbsp; + unsigned int forbidden_zero_bit; + unsigned int nal_ref_idc; + unsigned int nal_unit_type; + + if (!src) + return -EINVAL; + + rbsp_init(&rbsp, src, n, &read); + + nal_h264_read_start_code_prefix(&rbsp); + + rbsp_bit(&rbsp, &forbidden_zero_bit); + rbsp_bits(&rbsp, 2, &nal_ref_idc); + rbsp_bits(&rbsp, 5, &nal_unit_type); + + if (rbsp.error || + forbidden_zero_bit != 0 || + nal_ref_idc != 0 || + nal_unit_type != SEQUENCE_PARAMETER_SET) + return -EINVAL; + + nal_h264_rbsp_sps(&rbsp, sps); + + nal_h264_rbsp_trailing_bits(&rbsp); + + if (rbsp.error) + return rbsp.error; + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_h264_read_sps); + +/** + * nal_h264_write_pps() - Write PPS NAL unit into RBSP format + * @dev: device pointer + * @dest: the buffer that is filled with RBSP data + * @n: maximum size of @dest in bytes + * @pps: &struct nal_h264_pps to convert to RBSP + * + * Convert @pps to RBSP data and write it into @dest. + * + * The size of the PPS NAL unit is not known in advance and this function will + * fail, if @dest does not hold sufficient space for the PPS NAL unit. + * + * Return: number of bytes written to @dest or negative error code + */ +ssize_t nal_h264_write_pps(const struct device *dev, + void *dest, size_t n, struct nal_h264_pps *pps) +{ + struct rbsp rbsp; + unsigned int forbidden_zero_bit = 0; + unsigned int nal_ref_idc = 0; + unsigned int nal_unit_type = PICTURE_PARAMETER_SET; + + if (!dest) + return -EINVAL; + + rbsp_init(&rbsp, dest, n, &write); + + nal_h264_write_start_code_prefix(&rbsp); + + /* NAL unit header */ + rbsp_bit(&rbsp, &forbidden_zero_bit); + rbsp_bits(&rbsp, 2, &nal_ref_idc); + rbsp_bits(&rbsp, 5, &nal_unit_type); + + nal_h264_rbsp_pps(&rbsp, pps); + + nal_h264_rbsp_trailing_bits(&rbsp); + + if (rbsp.error) + return rbsp.error; + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_h264_write_pps); + +/** + * nal_h264_read_pps() - Read PPS NAL unit from RBSP format + * @dev: device pointer + * @pps: the &struct nal_h264_pps to fill from the RBSP data + * @src: the buffer that contains the RBSP data + * @n: size of @src in bytes + * + * Read RBSP data from @src and use it to fill @pps. + * + * Return: number of bytes read from @src or negative error code + */ +ssize_t nal_h264_read_pps(const struct device *dev, + struct nal_h264_pps *pps, void *src, size_t n) +{ + struct rbsp rbsp; + + if (!src) + return -EINVAL; + + rbsp_init(&rbsp, src, n, &read); + + nal_h264_read_start_code_prefix(&rbsp); + + /* NAL unit header */ + rbsp.pos += 8; + + nal_h264_rbsp_pps(&rbsp, pps); + + nal_h264_rbsp_trailing_bits(&rbsp); + + if (rbsp.error) + return rbsp.error; + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_h264_read_pps); + +/** + * nal_h264_write_filler() - Write filler data RBSP + * @dev: device pointer + * @dest: buffer to fill with filler data + * @n: size of the buffer to fill with filler data + * + * Write a filler data RBSP to @dest with a size of @n bytes and return the + * number of written filler data bytes. + * + * Use this function to generate dummy data in an RBSP data stream that can be + * safely ignored by h264 decoders. + * + * The RBSP format of the filler data is specified in Rec. ITU-T H.264 + * (04/2017) 7.3.2.7 Filler data RBSP syntax. + * + * Return: number of filler data bytes (including marker) or negative error + */ +ssize_t nal_h264_write_filler(const struct device *dev, void *dest, size_t n) +{ + struct rbsp rbsp; + unsigned int forbidden_zero_bit = 0; + unsigned int nal_ref_idc = 0; + unsigned int nal_unit_type = FILLER_DATA; + + if (!dest) + return -EINVAL; + + rbsp_init(&rbsp, dest, n, &write); + + nal_h264_write_start_code_prefix(&rbsp); + + rbsp_bit(&rbsp, &forbidden_zero_bit); + rbsp_bits(&rbsp, 2, &nal_ref_idc); + rbsp_bits(&rbsp, 5, &nal_unit_type); + + nal_h264_write_filler_data(&rbsp); + + nal_h264_rbsp_trailing_bits(&rbsp); + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_h264_write_filler); + +/** + * nal_h264_read_filler() - Read filler data RBSP + * @dev: device pointer + * @src: buffer with RBSP data that is read + * @n: maximum size of src that shall be read + * + * Read a filler data RBSP from @src up to a maximum size of @n bytes and + * return the size of the filler data in bytes including the marker. + * + * This function is used to parse filler data and skip the respective bytes in + * the RBSP data. + * + * The RBSP format of the filler data is specified in Rec. ITU-T H.264 + * (04/2017) 7.3.2.7 Filler data RBSP syntax. + * + * Return: number of filler data bytes (including marker) or negative error + */ +ssize_t nal_h264_read_filler(const struct device *dev, void *src, size_t n) +{ + struct rbsp rbsp; + unsigned int forbidden_zero_bit; + unsigned int nal_ref_idc; + unsigned int nal_unit_type; + + if (!src) + return -EINVAL; + + rbsp_init(&rbsp, src, n, &read); + + nal_h264_read_start_code_prefix(&rbsp); + + rbsp_bit(&rbsp, &forbidden_zero_bit); + rbsp_bits(&rbsp, 2, &nal_ref_idc); + rbsp_bits(&rbsp, 5, &nal_unit_type); + + if (rbsp.error) + return rbsp.error; + if (forbidden_zero_bit != 0 || + nal_ref_idc != 0 || + nal_unit_type != FILLER_DATA) + return -EINVAL; + + nal_h264_read_filler_data(&rbsp); + nal_h264_rbsp_trailing_bits(&rbsp); + + if (rbsp.error) + return rbsp.error; + + return DIV_ROUND_UP(rbsp.pos, 8); +} +EXPORT_SYMBOL_GPL(nal_h264_read_filler); diff --git a/drivers/staging/media/allegro-dvt/nal-h264.h b/drivers/staging/media/allegro-dvt/nal-h264.h new file mode 100644 index 000000000000..2ba7cbced7a5 --- /dev/null +++ b/drivers/staging/media/allegro-dvt/nal-h264.h @@ -0,0 +1,208 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2019 Pengutronix, Michael Tretter <kernel@pengutronix.de> + * + * Convert NAL units between raw byte sequence payloads (RBSP) and C structs. + */ + +#ifndef __NAL_H264_H__ +#define __NAL_H264_H__ + +#include <linux/kernel.h> +#include <linux/types.h> + +/** + * struct nal_h264_hdr_parameters - HDR parameters + * + * C struct representation of the sequence parameter set NAL unit as defined by + * Rec. ITU-T H.264 (04/2017) E.1.2 HRD parameters syntax. + */ +struct nal_h264_hrd_parameters { + unsigned int cpb_cnt_minus1; + unsigned int bit_rate_scale; + unsigned int cpb_size_scale; + struct { + int bit_rate_value_minus1[16]; + int cpb_size_value_minus1[16]; + unsigned int cbr_flag[16]; + }; + unsigned int initial_cpb_removal_delay_length_minus1; + unsigned int cpb_removal_delay_length_minus1; + unsigned int dpb_output_delay_length_minus1; + unsigned int time_offset_length; +}; + +/** + * struct nal_h264_vui_parameters - VUI parameters + * + * C struct representation of the VUI parameters as defined by Rec. ITU-T + * H.264 (04/2017) E.1.1 VUI parameters syntax. + */ +struct nal_h264_vui_parameters { + unsigned int aspect_ratio_info_present_flag; + struct { + unsigned int aspect_ratio_idc; + unsigned int sar_width; + unsigned int sar_height; + }; + unsigned int overscan_info_present_flag; + unsigned int overscan_appropriate_flag; + unsigned int video_signal_type_present_flag; + struct { + unsigned int video_format; + unsigned int video_full_range_flag; + unsigned int colour_description_present_flag; + struct { + unsigned int colour_primaries; + unsigned int transfer_characteristics; + unsigned int matrix_coefficients; + }; + }; + unsigned int chroma_loc_info_present_flag; + struct { + unsigned int chroma_sample_loc_type_top_field; + unsigned int chroma_sample_loc_type_bottom_field; + }; + unsigned int timing_info_present_flag; + struct { + unsigned int num_units_in_tick; + unsigned int time_scale; + unsigned int fixed_frame_rate_flag; + }; + unsigned int nal_hrd_parameters_present_flag; + struct nal_h264_hrd_parameters nal_hrd_parameters; + unsigned int vcl_hrd_parameters_present_flag; + struct nal_h264_hrd_parameters vcl_hrd_parameters; + unsigned int low_delay_hrd_flag; + unsigned int pic_struct_present_flag; + unsigned int bitstream_restriction_flag; + struct { + unsigned int motion_vectors_over_pic_boundaries_flag; + unsigned int max_bytes_per_pic_denom; + unsigned int max_bits_per_mb_denom; + unsigned int log2_max_mv_length_horizontal; + unsigned int log21_max_mv_length_vertical; + unsigned int max_num_reorder_frames; + unsigned int max_dec_frame_buffering; + }; +}; + +/** + * struct nal_h264_sps - Sequence parameter set + * + * C struct representation of the sequence parameter set NAL unit as defined by + * Rec. ITU-T H.264 (04/2017) 7.3.2.1.1 Sequence parameter set data syntax. + */ +struct nal_h264_sps { + unsigned int profile_idc; + unsigned int constraint_set0_flag; + unsigned int constraint_set1_flag; + unsigned int constraint_set2_flag; + unsigned int constraint_set3_flag; + unsigned int constraint_set4_flag; + unsigned int constraint_set5_flag; + unsigned int reserved_zero_2bits; + unsigned int level_idc; + unsigned int seq_parameter_set_id; + struct { + unsigned int chroma_format_idc; + unsigned int separate_colour_plane_flag; + unsigned int bit_depth_luma_minus8; + unsigned int bit_depth_chroma_minus8; + unsigned int qpprime_y_zero_transform_bypass_flag; + unsigned int seq_scaling_matrix_present_flag; + }; + unsigned int log2_max_frame_num_minus4; + unsigned int pic_order_cnt_type; + union { + unsigned int log2_max_pic_order_cnt_lsb_minus4; + struct { + unsigned int delta_pic_order_always_zero_flag; + int offset_for_non_ref_pic; + int offset_for_top_to_bottom_field; + unsigned int num_ref_frames_in_pic_order_cnt_cycle; + int offset_for_ref_frame[255]; + }; + }; + unsigned int max_num_ref_frames; + unsigned int gaps_in_frame_num_value_allowed_flag; + unsigned int pic_width_in_mbs_minus1; + unsigned int pic_height_in_map_units_minus1; + unsigned int frame_mbs_only_flag; + unsigned int mb_adaptive_frame_field_flag; + unsigned int direct_8x8_inference_flag; + unsigned int frame_cropping_flag; + struct { + unsigned int crop_left; + unsigned int crop_right; + unsigned int crop_top; + unsigned int crop_bottom; + }; + unsigned int vui_parameters_present_flag; + struct nal_h264_vui_parameters vui; +}; + +/** + * struct nal_h264_pps - Picture parameter set + * + * C struct representation of the picture parameter set NAL unit as defined by + * Rec. ITU-T H.264 (04/2017) 7.3.2.2 Picture parameter set RBSP syntax. + */ +struct nal_h264_pps { + unsigned int pic_parameter_set_id; + unsigned int seq_parameter_set_id; + unsigned int entropy_coding_mode_flag; + unsigned int bottom_field_pic_order_in_frame_present_flag; + unsigned int num_slice_groups_minus1; + unsigned int slice_group_map_type; + union { + unsigned int run_length_minus1[8]; + struct { + unsigned int top_left[8]; + unsigned int bottom_right[8]; + }; + struct { + unsigned int slice_group_change_direction_flag; + unsigned int slice_group_change_rate_minus1; + }; + struct { + unsigned int pic_size_in_map_units_minus1; + unsigned int slice_group_id[8]; + }; + }; + unsigned int num_ref_idx_l0_default_active_minus1; + unsigned int num_ref_idx_l1_default_active_minus1; + unsigned int weighted_pred_flag; + unsigned int weighted_bipred_idc; + int pic_init_qp_minus26; + int pic_init_qs_minus26; + int chroma_qp_index_offset; + unsigned int deblocking_filter_control_present_flag; + unsigned int constrained_intra_pred_flag; + unsigned int redundant_pic_cnt_present_flag; + struct { + unsigned int transform_8x8_mode_flag; + unsigned int pic_scaling_matrix_present_flag; + int second_chroma_qp_index_offset; + }; +}; + +int nal_h264_profile_from_v4l2(enum v4l2_mpeg_video_h264_profile profile); +int nal_h264_level_from_v4l2(enum v4l2_mpeg_video_h264_level level); + +ssize_t nal_h264_write_sps(const struct device *dev, + void *dest, size_t n, struct nal_h264_sps *sps); +ssize_t nal_h264_read_sps(const struct device *dev, + struct nal_h264_sps *sps, void *src, size_t n); +void nal_h264_print_sps(const struct device *dev, struct nal_h264_sps *sps); + +ssize_t nal_h264_write_pps(const struct device *dev, + void *dest, size_t n, struct nal_h264_pps *pps); +ssize_t nal_h264_read_pps(const struct device *dev, + struct nal_h264_pps *pps, void *src, size_t n); +void nal_h264_print_pps(const struct device *dev, struct nal_h264_pps *pps); + +ssize_t nal_h264_write_filler(const struct device *dev, void *dest, size_t n); +ssize_t nal_h264_read_filler(const struct device *dev, void *src, size_t n); + +#endif /* __NAL_H264_H__ */ diff --git a/drivers/staging/media/bcm2048/radio-bcm2048.c b/drivers/staging/media/bcm2048/radio-bcm2048.c index 09903ffb13ba..2c60a1fb6350 100644 --- a/drivers/staging/media/bcm2048/radio-bcm2048.c +++ b/drivers/staging/media/bcm2048/radio-bcm2048.c @@ -2310,11 +2310,6 @@ static int bcm2048_vidioc_querycap(struct file *file, void *priv, strscpy(capability->card, BCM2048_DRIVER_CARD, sizeof(capability->card)); snprintf(capability->bus_info, 32, "I2C: 0x%X", bdev->client->addr); - capability->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO | - V4L2_CAP_HW_FREQ_SEEK; - capability->capabilities = capability->device_caps | - V4L2_CAP_DEVICE_CAPS; - return 0; } @@ -2570,6 +2565,8 @@ static const struct video_device bcm2048_viddev_template = { .name = BCM2048_DRIVER_NAME, .release = video_device_release_empty, .ioctl_ops = &bcm2048_ioctl_ops, + .device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO | + V4L2_CAP_HW_FREQ_SEEK, }; /* diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe.c b/drivers/staging/media/davinci_vpfe/dm365_ipipe.c index 30e2edc0cec5..08c26f3c5282 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_ipipe.c +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe.c @@ -1772,7 +1772,7 @@ vpfe_ipipe_init(struct vpfe_ipipe_device *ipipe, struct platform_device *pdev) struct media_pad *pads = &ipipe->pads[0]; struct v4l2_subdev *sd = &ipipe->subdev; struct media_entity *me = &sd->entity; - struct resource *res, *memres; + struct resource *res, *res2, *memres; res = platform_get_resource(pdev, IORESOURCE_MEM, 4); if (!res) @@ -1786,11 +1786,11 @@ vpfe_ipipe_init(struct vpfe_ipipe_device *ipipe, struct platform_device *pdev) if (!ipipe->base_addr) goto error_release; - res = platform_get_resource(pdev, IORESOURCE_MEM, 6); - if (!res) + res2 = platform_get_resource(pdev, IORESOURCE_MEM, 6); + if (!res2) goto error_unmap; - ipipe->isp5_base_addr = ioremap_nocache(res->start, - resource_size(res)); + ipipe->isp5_base_addr = ioremap_nocache(res2->start, + resource_size(res2)); if (!ipipe->isp5_base_addr) goto error_unmap; diff --git a/drivers/staging/media/davinci_vpfe/dm365_isif.c b/drivers/staging/media/davinci_vpfe/dm365_isif.c index 46fd8184fc77..05a997f7aa5d 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_isif.c +++ b/drivers/staging/media/davinci_vpfe/dm365_isif.c @@ -816,7 +816,7 @@ isif_config_dfc(struct vpfe_isif_device *isif, struct vpfe_isif_dfc *vdfc) /* Correct whole line or partial */ if (vdfc->corr_whole_line) - val |= 1 << ISIF_VDFC_CORR_WHOLE_LN_SHIFT; + val |= BIT(ISIF_VDFC_CORR_WHOLE_LN_SHIFT); /* level shift value */ val |= (vdfc->def_level_shift & ISIF_VDFC_LEVEL_SHFT_MASK) << @@ -844,7 +844,7 @@ isif_config_dfc(struct vpfe_isif_device *isif, struct vpfe_isif_dfc *vdfc) val = isif_read(isif->isif_cfg.base_addr, DFCMEMCTL); /* set DFCMARST and set DFCMWR */ - val |= 1 << ISIF_DFCMEMCTL_DFCMARST_SHIFT; + val |= BIT(ISIF_DFCMEMCTL_DFCMARST_SHIFT); val |= 1; isif_write(isif->isif_cfg.base_addr, val, DFCMEMCTL); @@ -875,7 +875,7 @@ isif_config_dfc(struct vpfe_isif_device *isif, struct vpfe_isif_dfc *vdfc) } val = isif_read(isif->isif_cfg.base_addr, DFCMEMCTL); /* clear DFCMARST and set DFCMWR */ - val &= ~(1 << ISIF_DFCMEMCTL_DFCMARST_SHIFT); + val &= ~BIT(ISIF_DFCMEMCTL_DFCMARST_SHIFT); val |= 1; isif_write(isif->isif_cfg.base_addr, val, DFCMEMCTL); @@ -1135,7 +1135,7 @@ static int isif_config_raw(struct v4l2_subdev *sd, int mode) isif_write(isif->isif_cfg.base_addr, val, CGAMMAWD); /* Configure DPCM compression settings */ if (params->v4l2_pix_fmt == V4L2_PIX_FMT_SGRBG10DPCM8) { - val = 1 << ISIF_DPCM_EN_SHIFT; + val = BIT(ISIF_DPCM_EN_SHIFT); val |= (params->dpcm_predictor & ISIF_DPCM_PREDICTOR_MASK) << ISIF_DPCM_PREDICTOR_SHIFT; } diff --git a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c index 57b93605bc58..9dc28ffe38d5 100644 --- a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c +++ b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c @@ -158,7 +158,7 @@ static irqreturn_t vpfe_isr(int irq, void *dev_id) { struct vpfe_device *vpfe_dev = dev_id; - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_isr\n"); + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "%s\n", __func__); vpfe_isif_buffer_isr(&vpfe_dev->vpfe_isif); vpfe_resizer_buffer_isr(&vpfe_dev->vpfe_resizer); return IRQ_HANDLED; @@ -169,7 +169,7 @@ static irqreturn_t vpfe_vdint1_isr(int irq, void *dev_id) { struct vpfe_device *vpfe_dev = dev_id; - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_vdint1_isr\n"); + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "%s\n", __func__); vpfe_isif_vidint1_isr(&vpfe_dev->vpfe_isif); return IRQ_HANDLED; } @@ -179,7 +179,7 @@ static irqreturn_t vpfe_imp_dma_isr(int irq, void *dev_id) { struct vpfe_device *vpfe_dev = dev_id; - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_imp_dma_isr\n"); + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "%s\n", __func__); vpfe_ipipeif_ss_buffer_isr(&vpfe_dev->vpfe_ipipeif); vpfe_resizer_dma_isr(&vpfe_dev->vpfe_resizer); return IRQ_HANDLED; @@ -691,7 +691,7 @@ 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"); + v4l2_info(pdev->dev.driver, "%s\n", __func__); kzfree(vpfe_dev->sd); vpfe_detach_irq(vpfe_dev); diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.c b/drivers/staging/media/davinci_vpfe/vpfe_video.c index 510202a3b091..ab6bc452d9f6 100644 --- a/drivers/staging/media/davinci_vpfe/vpfe_video.c +++ b/drivers/staging/media/davinci_vpfe/vpfe_video.c @@ -419,6 +419,9 @@ static int vpfe_open(struct file *file) /* If decoder is not initialized. initialize it */ if (!video->initialized && vpfe_update_pipe_state(video)) { mutex_unlock(&video->lock); + v4l2_fh_del(&handle->vfh); + v4l2_fh_exit(&handle->vfh); + kfree(handle); return -ENODEV; } /* Increment device users counter */ @@ -609,10 +612,6 @@ static int vpfe_querycap(struct file *file, void *priv, v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querycap\n"); - if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - else - cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING | V4L2_CAP_DEVICE_CAPS; strscpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver)); @@ -1625,6 +1624,11 @@ int vpfe_video_register(struct vpfe_video_device *video, video->video_dev.v4l2_dev = vdev; + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + video->video_dev.device_caps = V4L2_CAP_VIDEO_CAPTURE; + else + video->video_dev.device_caps = V4L2_CAP_VIDEO_OUTPUT; + video->video_dev.device_caps |= V4L2_CAP_STREAMING; ret = video_register_device(&video->video_dev, VFL_TYPE_GRABBER, -1); if (ret < 0) pr_err("%s: could not register video device (%d)\n", diff --git a/drivers/staging/media/imx/Makefile b/drivers/staging/media/imx/Makefile index d2d909a36239..aa6c4b4ad37e 100644 --- a/drivers/staging/media/imx/Makefile +++ b/drivers/staging/media/imx/Makefile @@ -1,16 +1,16 @@ # SPDX-License-Identifier: GPL-2.0 -imx-media-objs := imx-media-dev.o imx-media-internal-sd.o imx-media-of.o -imx-media-objs += imx-media-dev-common.o -imx-media-common-objs := imx-media-utils.o imx-media-fim.o -imx-media-ic-objs := imx-ic-common.o imx-ic-prp.o imx-ic-prpencvf.o +imx6-media-objs := imx-media-dev.o imx-media-internal-sd.o \ + imx-ic-common.o imx-ic-prp.o imx-ic-prpencvf.o imx-media-vdic.o -obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx-media.o +imx-media-common-objs := imx-media-capture.o imx-media-dev-common.o \ + imx-media-of.o imx-media-utils.o + +imx6-media-csi-objs := imx-media-csi.o imx-media-fim.o + +obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx6-media.o obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx-media-common.o -obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx-media-capture.o -obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx-media-vdic.o -obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx-media-ic.o -obj-$(CONFIG_VIDEO_IMX_CSI) += imx-media-csi.o +obj-$(CONFIG_VIDEO_IMX_CSI) += imx6-media-csi.o obj-$(CONFIG_VIDEO_IMX_CSI) += imx6-mipi-csi2.o obj-$(CONFIG_VIDEO_IMX7_CSI) += imx7-media-csi.o diff --git a/drivers/staging/media/imx/imx-ic-common.c b/drivers/staging/media/imx/imx-ic-common.c index 18cd4cb92431..6df1ffb53895 100644 --- a/drivers/staging/media/imx/imx-ic-common.c +++ b/drivers/staging/media/imx/imx-ic-common.c @@ -4,8 +4,6 @@ * * Copyright (c) 2014-2016 Mentor Graphics Inc. */ -#include <linux/module.h> -#include <linux/platform_device.h> #include <media/v4l2-device.h> #include <media/v4l2-subdev.h> #include "imx-media.h" @@ -20,23 +18,23 @@ static struct imx_ic_ops *ic_ops[IC_NUM_OPS] = { [IC_TASK_VIEWFINDER] = &imx_ic_prpencvf_ops, }; -static int imx_ic_probe(struct platform_device *pdev) +struct v4l2_subdev *imx_media_ic_register(struct v4l2_device *v4l2_dev, + struct device *ipu_dev, + struct ipu_soc *ipu, + u32 grp_id) { - struct imx_media_ipu_internal_sd_pdata *pdata; struct imx_ic_priv *priv; int ret; - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(ipu_dev, sizeof(*priv), GFP_KERNEL); if (!priv) - return -ENOMEM; + return ERR_PTR(-ENOMEM); - platform_set_drvdata(pdev, &priv->sd); - priv->dev = &pdev->dev; + priv->ipu_dev = ipu_dev; + priv->ipu = ipu; - /* get our ipu_id, grp_id and IC task id */ - pdata = priv->dev->platform_data; - priv->ipu_id = pdata->ipu_id; - switch (pdata->grp_id) { + /* get our IC task id */ + switch (grp_id) { case IMX_MEDIA_GRP_ID_IPU_IC_PRP: priv->task_id = IC_TASK_PRP; break; @@ -47,7 +45,7 @@ static int imx_ic_probe(struct platform_device *pdev) priv->task_id = IC_TASK_VIEWFINDER; break; default: - return -EINVAL; + return ERR_PTR(-EINVAL); } v4l2_subdev_init(&priv->sd, ic_ops[priv->task_id]->subdev_ops); @@ -55,55 +53,35 @@ static int imx_ic_probe(struct platform_device *pdev) priv->sd.internal_ops = ic_ops[priv->task_id]->internal_ops; priv->sd.entity.ops = ic_ops[priv->task_id]->entity_ops; priv->sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER; - priv->sd.dev = &pdev->dev; - priv->sd.owner = THIS_MODULE; + priv->sd.owner = ipu_dev->driver->owner; priv->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; - priv->sd.grp_id = pdata->grp_id; - strscpy(priv->sd.name, pdata->sd_name, sizeof(priv->sd.name)); + priv->sd.grp_id = grp_id; + imx_media_grp_id_to_sd_name(priv->sd.name, sizeof(priv->sd.name), + priv->sd.grp_id, ipu_get_num(ipu)); ret = ic_ops[priv->task_id]->init(priv); if (ret) - return ret; + return ERR_PTR(ret); - ret = v4l2_async_register_subdev(&priv->sd); - if (ret) + ret = v4l2_device_register_subdev(v4l2_dev, &priv->sd); + if (ret) { ic_ops[priv->task_id]->remove(priv); + return ERR_PTR(ret); + } - return ret; + return &priv->sd; } -static int imx_ic_remove(struct platform_device *pdev) +int imx_media_ic_unregister(struct v4l2_subdev *sd) { - struct v4l2_subdev *sd = platform_get_drvdata(pdev); struct imx_ic_priv *priv = container_of(sd, struct imx_ic_priv, sd); v4l2_info(sd, "Removing\n"); ic_ops[priv->task_id]->remove(priv); - v4l2_async_unregister_subdev(sd); + v4l2_device_unregister_subdev(sd); media_entity_cleanup(&sd->entity); return 0; } - -static const struct platform_device_id imx_ic_ids[] = { - { .name = "imx-ipuv3-ic" }, - { }, -}; -MODULE_DEVICE_TABLE(platform, imx_ic_ids); - -static struct platform_driver imx_ic_driver = { - .probe = imx_ic_probe, - .remove = imx_ic_remove, - .id_table = imx_ic_ids, - .driver = { - .name = "imx-ipuv3-ic", - }, -}; -module_platform_driver(imx_ic_driver); - -MODULE_DESCRIPTION("i.MX IC subdev driver"); -MODULE_AUTHOR("Steve Longerbeam <steve_longerbeam@mentor.com>"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:imx-ipuv3-ic"); diff --git a/drivers/staging/media/imx/imx-ic-prp.c b/drivers/staging/media/imx/imx-ic-prp.c index 10ffe00f1a54..5b4af3cfe670 100644 --- a/drivers/staging/media/imx/imx-ic-prp.c +++ b/drivers/staging/media/imx/imx-ic-prp.c @@ -35,16 +35,12 @@ #define S_ALIGN 1 /* multiple of 2 */ struct prp_priv { - struct imx_media_dev *md; struct imx_ic_priv *ic_priv; struct media_pad pad[PRP_NUM_PADS]; /* lock to protect all members below */ struct mutex lock; - /* IPU units we require */ - struct ipu_soc *ipu; - struct v4l2_subdev *src_sd; struct v4l2_subdev *sink_sd_prpenc; struct v4l2_subdev *sink_sd_prpvf; @@ -62,7 +58,7 @@ static inline struct prp_priv *sd_to_priv(struct v4l2_subdev *sd) { struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd); - return ic_priv->prp_priv; + return ic_priv->task_priv; } static int prp_start(struct prp_priv *priv) @@ -70,12 +66,10 @@ static int prp_start(struct prp_priv *priv) struct imx_ic_priv *ic_priv = priv->ic_priv; bool src_is_vdic; - priv->ipu = priv->md->ipu[ic_priv->ipu_id]; - /* set IC to receive from CSI or VDI depending on source */ src_is_vdic = !!(priv->src_sd->grp_id & IMX_MEDIA_GRP_ID_IPU_VDIC); - ipu_set_ic_src_mux(priv->ipu, priv->csi_id, src_is_vdic); + ipu_set_ic_src_mux(ic_priv->ipu, priv->csi_id, src_is_vdic); return 0; } @@ -216,12 +210,12 @@ static int prp_link_setup(struct media_entity *entity, { struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd); - struct prp_priv *priv = ic_priv->prp_priv; + struct prp_priv *priv = ic_priv->task_priv; struct v4l2_subdev *remote_sd; int ret = 0; - dev_dbg(ic_priv->dev, "link setup %s -> %s", remote->entity->name, - local->entity->name); + dev_dbg(ic_priv->ipu_dev, "%s: link setup %s -> %s", + ic_priv->sd.name, remote->entity->name, local->entity->name); remote_sd = media_entity_to_v4l2_subdev(remote->entity); @@ -295,7 +289,7 @@ static int prp_link_validate(struct v4l2_subdev *sd, struct v4l2_subdev_format *sink_fmt) { struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd); - struct prp_priv *priv = ic_priv->prp_priv; + struct prp_priv *priv = ic_priv->task_priv; struct v4l2_subdev *csi; int ret; @@ -304,8 +298,8 @@ static int prp_link_validate(struct v4l2_subdev *sd, if (ret) return ret; - csi = imx_media_find_upstream_subdev(priv->md, &ic_priv->sd.entity, - IMX_MEDIA_GRP_ID_IPU_CSI); + csi = imx_media_pipeline_subdev(&ic_priv->sd.entity, + IMX_MEDIA_GRP_ID_IPU_CSI, true); if (IS_ERR(csi)) csi = NULL; @@ -351,7 +345,7 @@ out: static int prp_s_stream(struct v4l2_subdev *sd, int enable) { struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd); - struct prp_priv *priv = ic_priv->prp_priv; + struct prp_priv *priv = ic_priv->task_priv; int ret = 0; mutex_lock(&priv->lock); @@ -368,7 +362,8 @@ static int prp_s_stream(struct v4l2_subdev *sd, int enable) if (priv->stream_count != !enable) goto update_count; - dev_dbg(ic_priv->dev, "stream %s\n", enable ? "ON" : "OFF"); + dev_dbg(ic_priv->ipu_dev, "%s: stream %s\n", sd->name, + enable ? "ON" : "OFF"); if (enable) ret = prp_start(priv); @@ -440,9 +435,6 @@ static int prp_registered(struct v4l2_subdev *sd) int i, ret; u32 code; - /* get media device */ - priv->md = dev_get_drvdata(sd->v4l2_dev->dev); - for (i = 0; i < PRP_NUM_PADS; i++) { priv->pad[i].flags = (i == PRP_SINK_PAD) ? MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; @@ -494,12 +486,12 @@ static int prp_init(struct imx_ic_priv *ic_priv) { struct prp_priv *priv; - priv = devm_kzalloc(ic_priv->dev, sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(ic_priv->ipu_dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; mutex_init(&priv->lock); - ic_priv->prp_priv = priv; + ic_priv->task_priv = priv; priv->ic_priv = ic_priv; return 0; @@ -507,7 +499,7 @@ static int prp_init(struct imx_ic_priv *ic_priv) static void prp_remove(struct imx_ic_priv *ic_priv) { - struct prp_priv *priv = ic_priv->prp_priv; + struct prp_priv *priv = ic_priv->task_priv; mutex_destroy(&priv->lock); } diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c index 64037b0a8387..82bba68c554e 100644 --- a/drivers/staging/media/imx/imx-ic-prpencvf.c +++ b/drivers/staging/media/imx/imx-ic-prpencvf.c @@ -50,7 +50,6 @@ #define S_ALIGN 1 /* multiple of 2 */ struct prp_priv { - struct imx_media_dev *md; struct imx_ic_priv *ic_priv; struct media_pad pad[PRPENCVF_NUM_PADS]; /* the video device at output pad */ @@ -60,7 +59,6 @@ struct prp_priv { struct mutex lock; /* IPU units we require */ - struct ipu_soc *ipu; struct ipu_ic *ic; struct ipuv3_channel *out_ch; struct ipuv3_channel *rot_in_ch; @@ -156,9 +154,7 @@ static int prp_get_ipu_resources(struct prp_priv *priv) struct ipuv3_channel *out_ch, *rot_in_ch, *rot_out_ch; int ret, task = ic_priv->task_id; - priv->ipu = priv->md->ipu[ic_priv->ipu_id]; - - ic = ipu_ic_get(priv->ipu, task); + ic = ipu_ic_get(ic_priv->ipu, task); if (IS_ERR(ic)) { v4l2_err(&ic_priv->sd, "failed to get IC\n"); ret = PTR_ERR(ic); @@ -166,7 +162,7 @@ static int prp_get_ipu_resources(struct prp_priv *priv) } priv->ic = ic; - out_ch = ipu_idmac_get(priv->ipu, prp_channel[task].out_ch); + out_ch = ipu_idmac_get(ic_priv->ipu, prp_channel[task].out_ch); if (IS_ERR(out_ch)) { v4l2_err(&ic_priv->sd, "could not get IDMAC channel %u\n", prp_channel[task].out_ch); @@ -175,7 +171,7 @@ static int prp_get_ipu_resources(struct prp_priv *priv) } priv->out_ch = out_ch; - rot_in_ch = ipu_idmac_get(priv->ipu, prp_channel[task].rot_in_ch); + rot_in_ch = ipu_idmac_get(ic_priv->ipu, prp_channel[task].rot_in_ch); if (IS_ERR(rot_in_ch)) { v4l2_err(&ic_priv->sd, "could not get IDMAC channel %u\n", prp_channel[task].rot_in_ch); @@ -184,7 +180,7 @@ static int prp_get_ipu_resources(struct prp_priv *priv) } priv->rot_in_ch = rot_in_ch; - rot_out_ch = ipu_idmac_get(priv->ipu, prp_channel[task].rot_out_ch); + rot_out_ch = ipu_idmac_get(ic_priv->ipu, prp_channel[task].rot_out_ch); if (IS_ERR(rot_out_ch)) { v4l2_err(&ic_priv->sd, "could not get IDMAC channel %u\n", prp_channel[task].rot_out_ch); @@ -464,13 +460,13 @@ static int prp_setup_rotation(struct prp_priv *priv) incc = priv->cc[PRPENCVF_SINK_PAD]; outcc = vdev->cc; - ret = imx_media_alloc_dma_buf(priv->md, &priv->rot_buf[0], + ret = imx_media_alloc_dma_buf(ic_priv->ipu_dev, &priv->rot_buf[0], outfmt->sizeimage); if (ret) { v4l2_err(&ic_priv->sd, "failed to alloc rot_buf[0], %d\n", ret); return ret; } - ret = imx_media_alloc_dma_buf(priv->md, &priv->rot_buf[1], + ret = imx_media_alloc_dma_buf(ic_priv->ipu_dev, &priv->rot_buf[1], outfmt->sizeimage); if (ret) { v4l2_err(&ic_priv->sd, "failed to alloc rot_buf[1], %d\n", ret); @@ -543,14 +539,16 @@ static int prp_setup_rotation(struct prp_priv *priv) unsetup_vb2: prp_unsetup_vb2_buf(priv, VB2_BUF_STATE_QUEUED); free_rot1: - imx_media_free_dma_buf(priv->md, &priv->rot_buf[1]); + imx_media_free_dma_buf(ic_priv->ipu_dev, &priv->rot_buf[1]); free_rot0: - imx_media_free_dma_buf(priv->md, &priv->rot_buf[0]); + imx_media_free_dma_buf(ic_priv->ipu_dev, &priv->rot_buf[0]); return ret; } static void prp_unsetup_rotation(struct prp_priv *priv) { + struct imx_ic_priv *ic_priv = priv->ic_priv; + ipu_ic_task_disable(priv->ic); ipu_idmac_disable_channel(priv->out_ch); @@ -561,8 +559,8 @@ static void prp_unsetup_rotation(struct prp_priv *priv) ipu_ic_disable(priv->ic); - imx_media_free_dma_buf(priv->md, &priv->rot_buf[0]); - imx_media_free_dma_buf(priv->md, &priv->rot_buf[1]); + imx_media_free_dma_buf(ic_priv->ipu_dev, &priv->rot_buf[0]); + imx_media_free_dma_buf(ic_priv->ipu_dev, &priv->rot_buf[1]); } static int prp_setup_norotation(struct prp_priv *priv) @@ -602,7 +600,7 @@ static int prp_setup_norotation(struct prp_priv *priv) ipu_cpmem_dump(priv->out_ch); ipu_ic_dump(priv->ic); - ipu_dump(priv->ipu); + ipu_dump(ic_priv->ipu); ipu_ic_enable(priv->ic); @@ -654,7 +652,7 @@ static int prp_start(struct prp_priv *priv) outfmt = &vdev->fmt.fmt.pix; - ret = imx_media_alloc_dma_buf(priv->md, &priv->underrun_buf, + ret = imx_media_alloc_dma_buf(ic_priv->ipu_dev, &priv->underrun_buf, outfmt->sizeimage); if (ret) goto out_put_ipu; @@ -674,10 +672,10 @@ static int prp_start(struct prp_priv *priv) if (ret) goto out_free_underrun; - priv->nfb4eof_irq = ipu_idmac_channel_irq(priv->ipu, + priv->nfb4eof_irq = ipu_idmac_channel_irq(ic_priv->ipu, priv->out_ch, IPU_IRQ_NFB4EOF); - ret = devm_request_irq(ic_priv->dev, priv->nfb4eof_irq, + ret = devm_request_irq(ic_priv->ipu_dev, priv->nfb4eof_irq, prp_nfb4eof_interrupt, 0, "imx-ic-prp-nfb4eof", priv); if (ret) { @@ -688,12 +686,12 @@ static int prp_start(struct prp_priv *priv) if (ipu_rot_mode_is_irt(priv->rot_mode)) priv->eof_irq = ipu_idmac_channel_irq( - priv->ipu, priv->rot_out_ch, IPU_IRQ_EOF); + ic_priv->ipu, priv->rot_out_ch, IPU_IRQ_EOF); else priv->eof_irq = ipu_idmac_channel_irq( - priv->ipu, priv->out_ch, IPU_IRQ_EOF); + ic_priv->ipu, priv->out_ch, IPU_IRQ_EOF); - ret = devm_request_irq(ic_priv->dev, priv->eof_irq, + ret = devm_request_irq(ic_priv->ipu_dev, priv->eof_irq, prp_eof_interrupt, 0, "imx-ic-prp-eof", priv); if (ret) { @@ -718,13 +716,13 @@ static int prp_start(struct prp_priv *priv) return 0; out_free_eof_irq: - devm_free_irq(ic_priv->dev, priv->eof_irq, priv); + devm_free_irq(ic_priv->ipu_dev, priv->eof_irq, priv); out_free_nfb4eof_irq: - devm_free_irq(ic_priv->dev, priv->nfb4eof_irq, priv); + devm_free_irq(ic_priv->ipu_dev, priv->nfb4eof_irq, priv); out_unsetup: prp_unsetup(priv, VB2_BUF_STATE_QUEUED); out_free_underrun: - imx_media_free_dma_buf(priv->md, &priv->underrun_buf); + imx_media_free_dma_buf(ic_priv->ipu_dev, &priv->underrun_buf); out_put_ipu: prp_put_ipu_resources(priv); return ret; @@ -756,12 +754,12 @@ static void prp_stop(struct prp_priv *priv) v4l2_warn(&ic_priv->sd, "upstream stream off failed: %d\n", ret); - devm_free_irq(ic_priv->dev, priv->eof_irq, priv); - devm_free_irq(ic_priv->dev, priv->nfb4eof_irq, priv); + devm_free_irq(ic_priv->ipu_dev, priv->eof_irq, priv); + devm_free_irq(ic_priv->ipu_dev, priv->nfb4eof_irq, priv); prp_unsetup(priv, VB2_BUF_STATE_ERROR); - imx_media_free_dma_buf(priv->md, &priv->underrun_buf); + imx_media_free_dma_buf(ic_priv->ipu_dev, &priv->underrun_buf); /* cancel the EOF timeout timer */ del_timer_sync(&priv->eof_timeout_timer); @@ -904,11 +902,8 @@ static int prp_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_format *sdformat) { struct prp_priv *priv = sd_to_priv(sd); - struct imx_media_video_dev *vdev = priv->vdev; const struct imx_media_pixfmt *cc; - struct v4l2_pix_format vdev_fmt; struct v4l2_mbus_framefmt *fmt; - struct v4l2_rect vdev_compose; int ret = 0; if (sdformat->pad >= PRPENCVF_NUM_PADS) @@ -944,19 +939,9 @@ static int prp_set_fmt(struct v4l2_subdev *sd, priv->cc[PRPENCVF_SRC_PAD] = outcc; } - if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY) - goto out; - - priv->cc[sdformat->pad] = cc; + if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE) + priv->cc[sdformat->pad] = cc; - /* propagate output pad format to capture device */ - imx_media_mbus_fmt_to_pix_fmt(&vdev_fmt, &vdev_compose, - &priv->format_mbus[PRPENCVF_SRC_PAD], - priv->cc[PRPENCVF_SRC_PAD]); - mutex_unlock(&priv->lock); - imx_media_capture_device_set_format(vdev, &vdev_fmt, &vdev_compose); - - return 0; out: mutex_unlock(&priv->lock); return ret; @@ -1011,8 +996,8 @@ static int prp_link_setup(struct media_entity *entity, struct v4l2_subdev *remote_sd; int ret = 0; - dev_dbg(ic_priv->dev, "link setup %s -> %s", remote->entity->name, - local->entity->name); + dev_dbg(ic_priv->ipu_dev, "%s: link setup %s -> %s", + ic_priv->sd.name, remote->entity->name, local->entity->name); mutex_lock(&priv->lock); @@ -1178,7 +1163,8 @@ static int prp_s_stream(struct v4l2_subdev *sd, int enable) if (priv->stream_count != !enable) goto update_count; - dev_dbg(ic_priv->dev, "stream %s\n", enable ? "ON" : "OFF"); + dev_dbg(ic_priv->ipu_dev, "%s: stream %s\n", sd->name, + enable ? "ON" : "OFF"); if (enable) ret = prp_start(priv); @@ -1241,9 +1227,6 @@ static int prp_registered(struct v4l2_subdev *sd) int i, ret; u32 code; - /* get media device */ - priv->md = dev_get_drvdata(sd->v4l2_dev->dev); - for (i = 0; i < PRPENCVF_NUM_PADS; i++) { priv->pad[i].flags = (i == PRPENCVF_SINK_PAD) ? MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; @@ -1266,14 +1249,10 @@ static int prp_registered(struct v4l2_subdev *sd) if (ret) return ret; - ret = imx_media_capture_device_register(priv->md, priv->vdev); + ret = imx_media_capture_device_register(priv->vdev); if (ret) return ret; - ret = imx_media_add_video_device(priv->md, priv->vdev); - if (ret) - goto unreg; - ret = prp_init_controls(priv); if (ret) goto unreg; @@ -1325,7 +1304,7 @@ static int prp_init(struct imx_ic_priv *ic_priv) { struct prp_priv *priv; - priv = devm_kzalloc(ic_priv->dev, sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(ic_priv->ipu_dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -1335,7 +1314,8 @@ static int prp_init(struct imx_ic_priv *ic_priv) spin_lock_init(&priv->irqlock); timer_setup(&priv->eof_timeout_timer, prp_eof_timeout, 0); - priv->vdev = imx_media_capture_device_init(&ic_priv->sd, + priv->vdev = imx_media_capture_device_init(ic_priv->ipu_dev, + &ic_priv->sd, PRPENCVF_SRC_PAD); if (IS_ERR(priv->vdev)) return PTR_ERR(priv->vdev); diff --git a/drivers/staging/media/imx/imx-ic.h b/drivers/staging/media/imx/imx-ic.h index 0dbcf2a7ab5f..587c191c3eab 100644 --- a/drivers/staging/media/imx/imx-ic.h +++ b/drivers/staging/media/imx/imx-ic.h @@ -10,11 +10,10 @@ #include <media/v4l2-subdev.h> struct imx_ic_priv { - struct device *dev; + struct device *ipu_dev; + struct ipu_soc *ipu; struct v4l2_subdev sd; - int ipu_id; int task_id; - void *prp_priv; void *task_priv; }; @@ -29,6 +28,5 @@ struct imx_ic_ops { extern struct imx_ic_ops imx_ic_prp_ops; extern struct imx_ic_ops imx_ic_prpencvf_ops; -extern struct imx_ic_ops imx_ic_pp_ops; #endif diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index 9430c835c434..b33a07bc9105 100644 --- a/drivers/staging/media/imx/imx-media-capture.c +++ b/drivers/staging/media/imx/imx-media-capture.c @@ -202,6 +202,7 @@ static int capture_g_fmt_vid_cap(struct file *file, void *fh, static int __capture_try_fmt_vid_cap(struct capture_priv *priv, struct v4l2_subdev_format *fmt_src, struct v4l2_format *f, + const struct imx_media_pixfmt **retcc, struct v4l2_rect *compose) { const struct imx_media_pixfmt *cc, *cc_src; @@ -242,8 +243,17 @@ static int __capture_try_fmt_vid_cap(struct capture_priv *priv, } } - imx_media_mbus_fmt_to_pix_fmt(&f->fmt.pix, compose, - &fmt_src->format, cc); + imx_media_mbus_fmt_to_pix_fmt(&f->fmt.pix, &fmt_src->format, cc); + + if (retcc) + *retcc = cc; + + if (compose) { + compose->left = 0; + compose->top = 0; + compose->width = fmt_src->format.width; + compose->height = fmt_src->format.height; + } return 0; } @@ -261,7 +271,7 @@ static int capture_try_fmt_vid_cap(struct file *file, void *fh, if (ret) return ret; - return __capture_try_fmt_vid_cap(priv, &fmt_src, f, NULL); + return __capture_try_fmt_vid_cap(priv, &fmt_src, f, NULL, NULL); } static int capture_s_fmt_vid_cap(struct file *file, void *fh, @@ -269,7 +279,6 @@ static int capture_s_fmt_vid_cap(struct file *file, void *fh, { struct capture_priv *priv = video_drvdata(file); struct v4l2_subdev_format fmt_src; - struct v4l2_rect compose; int ret; if (vb2_is_busy(&priv->q)) { @@ -283,14 +292,12 @@ static int capture_s_fmt_vid_cap(struct file *file, void *fh, if (ret) return ret; - ret = __capture_try_fmt_vid_cap(priv, &fmt_src, f, &compose); + ret = __capture_try_fmt_vid_cap(priv, &fmt_src, f, &priv->vdev.cc, + &priv->vdev.compose); if (ret) return ret; priv->vdev.fmt.fmt.pix = f->fmt.pix; - priv->vdev.cc = imx_media_find_format(f->fmt.pix.pixelformat, - CS_SEL_ANY, true); - priv->vdev.compose = compose; return 0; } @@ -520,6 +527,33 @@ static void capture_buf_queue(struct vb2_buffer *vb) spin_unlock_irqrestore(&priv->q_lock, flags); } +static int capture_validate_fmt(struct capture_priv *priv) +{ + struct v4l2_subdev_format fmt_src; + const struct imx_media_pixfmt *cc; + struct v4l2_rect compose; + struct v4l2_format f; + int ret; + + fmt_src.pad = priv->src_sd_pad; + fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(priv->src_sd, pad, get_fmt, NULL, &fmt_src); + if (ret) + return ret; + + v4l2_fill_pix_format(&f.fmt.pix, &fmt_src.format); + + ret = __capture_try_fmt_vid_cap(priv, &fmt_src, &f, &cc, &compose); + if (ret) + return ret; + + return (priv->vdev.fmt.fmt.pix.width != f.fmt.pix.width || + priv->vdev.fmt.fmt.pix.height != f.fmt.pix.height || + priv->vdev.cc->cs != cc->cs || + priv->vdev.compose.width != compose.width || + priv->vdev.compose.height != compose.height) ? -EINVAL : 0; +} + static int capture_start_streaming(struct vb2_queue *vq, unsigned int count) { struct capture_priv *priv = vb2_get_drv_priv(vq); @@ -527,6 +561,12 @@ static int capture_start_streaming(struct vb2_queue *vq, unsigned int count) unsigned long flags; int ret; + ret = capture_validate_fmt(priv); + if (ret) { + v4l2_err(priv->src_sd, "capture format not valid\n"); + goto return_bufs; + } + ret = imx_media_pipeline_set_stream(priv->md, &priv->src_sd->entity, true); if (ret) { @@ -614,7 +654,6 @@ static int capture_release(struct file *file) struct capture_priv *priv = video_drvdata(file); struct video_device *vfd = priv->vdev.vfd; struct vb2_queue *vq = &priv->q; - int ret = 0; mutex_lock(&priv->mutex); @@ -627,7 +666,7 @@ static int capture_release(struct file *file) v4l2_fh_release(file); mutex_unlock(&priv->mutex); - return ret; + return 0; } static const struct v4l2_file_operations capture_fops = { @@ -649,21 +688,6 @@ static struct video_device capture_videodev = { .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING, }; -void imx_media_capture_device_set_format(struct imx_media_video_dev *vdev, - const struct v4l2_pix_format *pix, - const struct v4l2_rect *compose) -{ - struct capture_priv *priv = to_capture_priv(vdev); - - mutex_lock(&priv->mutex); - priv->vdev.fmt.fmt.pix = *pix; - priv->vdev.cc = imx_media_find_format(pix->pixelformat, CS_SEL_ANY, - true); - priv->vdev.compose = *compose; - mutex_unlock(&priv->mutex); -} -EXPORT_SYMBOL_GPL(imx_media_capture_device_set_format); - struct imx_media_buffer * imx_media_capture_device_next_buf(struct imx_media_video_dev *vdev) { @@ -701,19 +725,20 @@ void imx_media_capture_device_error(struct imx_media_video_dev *vdev) } EXPORT_SYMBOL_GPL(imx_media_capture_device_error); -int imx_media_capture_device_register(struct imx_media_dev *md, - struct imx_media_video_dev *vdev) +int imx_media_capture_device_register(struct imx_media_video_dev *vdev) { struct capture_priv *priv = to_capture_priv(vdev); struct v4l2_subdev *sd = priv->src_sd; + struct v4l2_device *v4l2_dev = sd->v4l2_dev; struct video_device *vfd = vdev->vfd; struct vb2_queue *vq = &priv->q; struct v4l2_subdev_format fmt_src; int ret; - priv->md = md; + /* get media device */ + priv->md = container_of(v4l2_dev->mdev, struct imx_media_dev, md); - vfd->v4l2_dev = sd->v4l2_dev; + vfd->v4l2_dev = v4l2_dev; ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); if (ret) { @@ -765,8 +790,10 @@ int imx_media_capture_device_register(struct imx_media_dev *md, } vdev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - imx_media_mbus_fmt_to_pix_fmt(&vdev->fmt.fmt.pix, &vdev->compose, + imx_media_mbus_fmt_to_pix_fmt(&vdev->fmt.fmt.pix, &fmt_src.format, NULL); + vdev->compose.width = fmt_src.format.width; + vdev->compose.height = fmt_src.format.height; vdev->cc = imx_media_find_format(vdev->fmt.fmt.pix.pixelformat, CS_SEL_ANY, false); @@ -775,6 +802,9 @@ int imx_media_capture_device_register(struct imx_media_dev *md, vfd->ctrl_handler = &priv->ctrl_hdlr; + /* add vdev to the video device list */ + imx_media_add_video_device(priv->md, vdev); + return 0; unreg: video_unregister_device(vfd); @@ -799,18 +829,19 @@ void imx_media_capture_device_unregister(struct imx_media_video_dev *vdev) EXPORT_SYMBOL_GPL(imx_media_capture_device_unregister); struct imx_media_video_dev * -imx_media_capture_device_init(struct v4l2_subdev *src_sd, int pad) +imx_media_capture_device_init(struct device *dev, struct v4l2_subdev *src_sd, + int pad) { struct capture_priv *priv; struct video_device *vfd; - priv = devm_kzalloc(src_sd->dev, sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return ERR_PTR(-ENOMEM); priv->src_sd = src_sd; priv->src_sd_pad = pad; - priv->dev = src_sd->dev; + priv->dev = dev; mutex_init(&priv->mutex); spin_lock_init(&priv->q_lock); diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 1d248aca40a9..0eeb0db6d83f 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -56,7 +56,6 @@ struct csi_skip_desc { struct csi_priv { struct device *dev; struct ipu_soc *ipu; - struct imx_media_dev *md; struct v4l2_subdev sd; struct media_pad pad[CSI_NUM_PADS]; /* the video device at IDMAC output pad */ @@ -178,8 +177,8 @@ static int csi_get_upstream_endpoint(struct csi_priv *priv, * CSI-2 receiver if it is in the path, otherwise stay * with video mux. */ - sd = imx_media_find_upstream_subdev(priv->md, src, - IMX_MEDIA_GRP_ID_CSI2); + sd = imx_media_pipeline_subdev(src, IMX_MEDIA_GRP_ID_CSI2, + true); if (!IS_ERR(sd)) src = &sd->entity; } @@ -193,9 +192,9 @@ static int csi_get_upstream_endpoint(struct csi_priv *priv, src = &priv->sd.entity; /* get source pad of entity directly upstream from src */ - pad = imx_media_find_upstream_pad(priv->md, src, 0); - if (IS_ERR(pad)) - return PTR_ERR(pad); + pad = imx_media_pipeline_pad(src, 0, 0, true); + if (!pad) + return -ENODEV; sd = media_entity_to_v4l2_subdev(pad->entity); @@ -608,7 +607,7 @@ static int csi_idmac_start(struct csi_priv *priv) outfmt = &vdev->fmt.fmt.pix; - ret = imx_media_alloc_dma_buf(priv->md, &priv->underrun_buf, + ret = imx_media_alloc_dma_buf(priv->dev, &priv->underrun_buf, outfmt->sizeimage); if (ret) goto out_put_ipu; @@ -662,7 +661,7 @@ out_free_nfb4eof_irq: out_unsetup: csi_idmac_unsetup(priv, VB2_BUF_STATE_QUEUED); out_free_dma_buf: - imx_media_free_dma_buf(priv->md, &priv->underrun_buf); + imx_media_free_dma_buf(priv->dev, &priv->underrun_buf); out_put_ipu: csi_idmac_put_ipu_resources(priv); return ret; @@ -694,7 +693,7 @@ static void csi_idmac_stop(struct csi_priv *priv) csi_idmac_unsetup(priv, VB2_BUF_STATE_ERROR); - imx_media_free_dma_buf(priv->md, &priv->underrun_buf); + imx_media_free_dma_buf(priv->dev, &priv->underrun_buf); /* cancel the EOF timeout timer */ del_timer_sync(&priv->eof_timeout_timer); @@ -1134,8 +1133,7 @@ static int csi_link_validate(struct v4l2_subdev *sd, */ #if 0 mutex_unlock(&priv->lock); - vc_num = imx_media_find_mipi_csi2_channel(priv->md, - &priv->sd.entity); + vc_num = imx_media_find_mipi_csi2_channel(&priv->sd.entity); if (vc_num < 0) return vc_num; mutex_lock(&priv->lock); @@ -1502,13 +1500,10 @@ static int csi_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_format *sdformat) { struct csi_priv *priv = v4l2_get_subdevdata(sd); - struct imx_media_video_dev *vdev = priv->vdev; struct v4l2_fwnode_endpoint upstream_ep = { .bus_type = 0 }; const struct imx_media_pixfmt *cc; - struct v4l2_pix_format vdev_fmt; struct v4l2_mbus_framefmt *fmt; struct v4l2_rect *crop, *compose; - struct v4l2_rect vdev_compose; int ret; if (sdformat->pad >= CSI_NUM_PADS) @@ -1558,19 +1553,9 @@ static int csi_set_fmt(struct v4l2_subdev *sd, } } - if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY) - goto out; - - priv->cc[sdformat->pad] = cc; - - /* propagate IDMAC output pad format to capture device */ - imx_media_mbus_fmt_to_pix_fmt(&vdev_fmt, &vdev_compose, - &priv->format_mbus[CSI_SRC_PAD_IDMAC], - priv->cc[CSI_SRC_PAD_IDMAC]); - mutex_unlock(&priv->lock); - imx_media_capture_device_set_format(vdev, &vdev_fmt, &vdev_compose); + if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE) + priv->cc[sdformat->pad] = cc; - return 0; out: mutex_unlock(&priv->lock); return ret; @@ -1762,9 +1747,6 @@ static int csi_registered(struct v4l2_subdev *sd) int i, ret; u32 code; - /* get media device */ - priv->md = dev_get_drvdata(sd->v4l2_dev->dev); - /* get handle to IPU CSI */ csi = ipu_csi_get(priv->ipu, priv->csi_id); if (IS_ERR(csi)) { @@ -1812,17 +1794,12 @@ static int csi_registered(struct v4l2_subdev *sd) if (ret) goto free_fim; - ret = imx_media_capture_device_register(priv->md, priv->vdev); + ret = imx_media_capture_device_register(priv->vdev); if (ret) goto free_fim; - ret = imx_media_add_video_device(priv->md, priv->vdev); - if (ret) - goto unreg; - return 0; -unreg: - imx_media_capture_device_unregister(priv->vdev); + free_fim: if (priv->fim) imx_media_fim_free(priv->fim); @@ -1983,7 +1960,7 @@ static int imx_csi_probe(struct platform_device *pdev) imx_media_grp_id_to_sd_name(priv->sd.name, sizeof(priv->sd.name), priv->sd.grp_id, ipu_get_num(priv->ipu)); - priv->vdev = imx_media_capture_device_init(&priv->sd, + priv->vdev = imx_media_capture_device_init(priv->sd.dev, &priv->sd, CSI_SRC_PAD_IDMAC); if (IS_ERR(priv->vdev)) return PTR_ERR(priv->vdev); diff --git a/drivers/staging/media/imx/imx-media-dev-common.c b/drivers/staging/media/imx/imx-media-dev-common.c index 6cd93419b81d..66b505f7e8df 100644 --- a/drivers/staging/media/imx/imx-media-dev-common.c +++ b/drivers/staging/media/imx/imx-media-dev-common.c @@ -8,9 +8,341 @@ #include <linux/of_graph.h> #include <linux/of_platform.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-event.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-mc.h> #include "imx-media.h" -static const struct v4l2_async_notifier_operations imx_media_subdev_ops = { +static inline struct imx_media_dev *notifier2dev(struct v4l2_async_notifier *n) +{ + return container_of(n, struct imx_media_dev, notifier); +} + +/* async subdev bound notifier */ +static int imx_media_subdev_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_subdev *asd) +{ + v4l2_info(sd->v4l2_dev, "subdev %s bound\n", sd->name); + + return 0; +} + +/* + * Create the media links for all subdevs that registered. + * Called after all async subdevs have bound. + */ +static int imx_media_create_links(struct v4l2_async_notifier *notifier) +{ + struct imx_media_dev *imxmd = notifier2dev(notifier); + struct v4l2_subdev *sd; + + list_for_each_entry(sd, &imxmd->v4l2_dev.subdevs, list) { + switch (sd->grp_id) { + case IMX_MEDIA_GRP_ID_IPU_VDIC: + case IMX_MEDIA_GRP_ID_IPU_IC_PRP: + case IMX_MEDIA_GRP_ID_IPU_IC_PRPENC: + case IMX_MEDIA_GRP_ID_IPU_IC_PRPVF: + /* + * links have already been created for the + * sync-registered subdevs. + */ + break; + case IMX_MEDIA_GRP_ID_IPU_CSI0: + case IMX_MEDIA_GRP_ID_IPU_CSI1: + case IMX_MEDIA_GRP_ID_CSI: + imx_media_create_csi_of_links(imxmd, sd); + break; + default: + /* + * if this subdev has fwnode links, create media + * links for them. + */ + imx_media_create_of_links(imxmd, sd); + break; + } + } + + return 0; +} + +/* + * adds given video device to given imx-media source pad vdev list. + * Continues upstream from the pad entity's sink pads. + */ +static int imx_media_add_vdev_to_pad(struct imx_media_dev *imxmd, + struct imx_media_video_dev *vdev, + struct media_pad *srcpad) +{ + struct media_entity *entity = srcpad->entity; + struct imx_media_pad_vdev *pad_vdev; + struct list_head *pad_vdev_list; + struct media_link *link; + struct v4l2_subdev *sd; + int i, ret; + + /* skip this entity if not a v4l2_subdev */ + if (!is_media_entity_v4l2_subdev(entity)) + return 0; + + sd = media_entity_to_v4l2_subdev(entity); + + pad_vdev_list = to_pad_vdev_list(sd, srcpad->index); + if (!pad_vdev_list) { + v4l2_warn(&imxmd->v4l2_dev, "%s:%u has no vdev list!\n", + entity->name, srcpad->index); + /* + * shouldn't happen, but no reason to fail driver load, + * just skip this entity. + */ + return 0; + } + + /* just return if we've been here before */ + list_for_each_entry(pad_vdev, pad_vdev_list, list) { + if (pad_vdev->vdev == vdev) + return 0; + } + + dev_dbg(imxmd->md.dev, "adding %s to pad %s:%u\n", + vdev->vfd->entity.name, entity->name, srcpad->index); + + pad_vdev = devm_kzalloc(imxmd->md.dev, sizeof(*pad_vdev), GFP_KERNEL); + if (!pad_vdev) + return -ENOMEM; + + /* attach this vdev to this pad */ + pad_vdev->vdev = vdev; + list_add_tail(&pad_vdev->list, pad_vdev_list); + + /* move upstream from this entity's sink pads */ + for (i = 0; i < entity->num_pads; i++) { + struct media_pad *pad = &entity->pads[i]; + + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + continue; + + list_for_each_entry(link, &entity->links, list) { + if (link->sink != pad) + continue; + ret = imx_media_add_vdev_to_pad(imxmd, vdev, + link->source); + if (ret) + return ret; + } + } + + return 0; +} + +/* + * For every subdevice, allocate an array of list_head's, one list_head + * for each pad, to hold the list of video devices reachable from that + * pad. + */ +static int imx_media_alloc_pad_vdev_lists(struct imx_media_dev *imxmd) +{ + struct list_head *vdev_lists; + struct media_entity *entity; + struct v4l2_subdev *sd; + int i; + + list_for_each_entry(sd, &imxmd->v4l2_dev.subdevs, list) { + entity = &sd->entity; + vdev_lists = devm_kcalloc(imxmd->md.dev, + entity->num_pads, sizeof(*vdev_lists), + GFP_KERNEL); + if (!vdev_lists) + return -ENOMEM; + + /* attach to the subdev's host private pointer */ + sd->host_priv = vdev_lists; + + for (i = 0; i < entity->num_pads; i++) + INIT_LIST_HEAD(to_pad_vdev_list(sd, i)); + } + + return 0; +} + +/* form the vdev lists in all imx-media source pads */ +static int imx_media_create_pad_vdev_lists(struct imx_media_dev *imxmd) +{ + struct imx_media_video_dev *vdev; + struct media_link *link; + int ret; + + ret = imx_media_alloc_pad_vdev_lists(imxmd); + if (ret) + return ret; + + list_for_each_entry(vdev, &imxmd->vdev_list, list) { + link = list_first_entry(&vdev->vfd->entity.links, + struct media_link, list); + ret = imx_media_add_vdev_to_pad(imxmd, vdev, link->source); + if (ret) + return ret; + } + + return 0; +} + +/* async subdev complete notifier */ +int imx_media_probe_complete(struct v4l2_async_notifier *notifier) +{ + struct imx_media_dev *imxmd = notifier2dev(notifier); + int ret; + + mutex_lock(&imxmd->mutex); + + ret = imx_media_create_links(notifier); + if (ret) + goto unlock; + + ret = imx_media_create_pad_vdev_lists(imxmd); + if (ret) + goto unlock; + + ret = v4l2_device_register_subdev_nodes(&imxmd->v4l2_dev); +unlock: + mutex_unlock(&imxmd->mutex); + if (ret) + return ret; + + return media_device_register(&imxmd->md); +} +EXPORT_SYMBOL_GPL(imx_media_probe_complete); + +/* + * adds controls to a video device from an entity subdevice. + * Continues upstream from the entity's sink pads. + */ +static int imx_media_inherit_controls(struct imx_media_dev *imxmd, + struct video_device *vfd, + struct media_entity *entity) +{ + int i, ret = 0; + + if (is_media_entity_v4l2_subdev(entity)) { + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + + dev_dbg(imxmd->md.dev, + "adding controls to %s from %s\n", + vfd->entity.name, sd->entity.name); + + ret = v4l2_ctrl_add_handler(vfd->ctrl_handler, + sd->ctrl_handler, + NULL, true); + if (ret) + return ret; + } + + /* move upstream */ + for (i = 0; i < entity->num_pads; i++) { + struct media_pad *pad, *spad = &entity->pads[i]; + + if (!(spad->flags & MEDIA_PAD_FL_SINK)) + continue; + + pad = media_entity_remote_pad(spad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + continue; + + ret = imx_media_inherit_controls(imxmd, vfd, pad->entity); + if (ret) + break; + } + + return ret; +} + +static int imx_media_link_notify(struct media_link *link, u32 flags, + unsigned int notification) +{ + struct imx_media_dev *imxmd = container_of(link->graph_obj.mdev, + struct imx_media_dev, md); + struct media_entity *source = link->source->entity; + struct imx_media_pad_vdev *pad_vdev; + struct list_head *pad_vdev_list; + struct video_device *vfd; + struct v4l2_subdev *sd; + int pad_idx, ret; + + ret = v4l2_pipeline_link_notify(link, flags, notification); + if (ret) + return ret; + + /* don't bother if source is not a subdev */ + if (!is_media_entity_v4l2_subdev(source)) + return 0; + + sd = media_entity_to_v4l2_subdev(source); + pad_idx = link->source->index; + + pad_vdev_list = to_pad_vdev_list(sd, pad_idx); + if (!pad_vdev_list) { + /* nothing to do if source sd has no pad vdev list */ + return 0; + } + + /* + * Before disabling a link, reset controls for all video + * devices reachable from this link. + * + * After enabling a link, refresh controls for all video + * devices reachable from this link. + */ + if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH && + !(flags & MEDIA_LNK_FL_ENABLED)) { + list_for_each_entry(pad_vdev, pad_vdev_list, list) { + vfd = pad_vdev->vdev->vfd; + dev_dbg(imxmd->md.dev, + "reset controls for %s\n", + vfd->entity.name); + v4l2_ctrl_handler_free(vfd->ctrl_handler); + v4l2_ctrl_handler_init(vfd->ctrl_handler, 0); + } + } else if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH && + (link->flags & MEDIA_LNK_FL_ENABLED)) { + list_for_each_entry(pad_vdev, pad_vdev_list, list) { + vfd = pad_vdev->vdev->vfd; + dev_dbg(imxmd->md.dev, + "refresh controls for %s\n", + vfd->entity.name); + ret = imx_media_inherit_controls(imxmd, vfd, + &vfd->entity); + if (ret) + break; + } + } + + return ret; +} + +static void imx_media_notify(struct v4l2_subdev *sd, unsigned int notification, + void *arg) +{ + struct media_entity *entity = &sd->entity; + int i; + + if (notification != V4L2_DEVICE_NOTIFY_EVENT) + return; + + for (i = 0; i < entity->num_pads; i++) { + struct media_pad *pad = &entity->pads[i]; + struct imx_media_pad_vdev *pad_vdev; + struct list_head *pad_vdev_list; + + pad_vdev_list = to_pad_vdev_list(sd, pad->index); + if (!pad_vdev_list) + continue; + list_for_each_entry(pad_vdev, pad_vdev_list, list) + v4l2_event_queue(pad_vdev->vdev->vfd, arg); + } +} + +static const struct v4l2_async_notifier_operations imx_media_notifier_ops = { .bound = imx_media_subdev_bound, .complete = imx_media_probe_complete, }; @@ -19,7 +351,8 @@ static const struct media_device_ops imx_media_md_ops = { .link_notify = imx_media_link_notify, }; -struct imx_media_dev *imx_media_dev_init(struct device *dev) +struct imx_media_dev *imx_media_dev_init(struct device *dev, + const struct media_device_ops *ops) { struct imx_media_dev *imxmd; int ret; @@ -31,7 +364,7 @@ struct imx_media_dev *imx_media_dev_init(struct device *dev) dev_set_drvdata(dev, imxmd); strscpy(imxmd->md.model, "imx-media", sizeof(imxmd->md.model)); - imxmd->md.ops = &imx_media_md_ops; + imxmd->md.ops = ops ? ops : &imx_media_md_ops; imxmd->md.dev = dev; mutex_init(&imxmd->mutex); @@ -50,8 +383,6 @@ struct imx_media_dev *imx_media_dev_init(struct device *dev) goto cleanup; } - dev_set_drvdata(imxmd->v4l2_dev.dev, imxmd); - INIT_LIST_HEAD(&imxmd->vdev_list); v4l2_async_notifier_init(&imxmd->notifier); @@ -65,7 +396,8 @@ cleanup: } EXPORT_SYMBOL_GPL(imx_media_dev_init); -int imx_media_dev_notifier_register(struct imx_media_dev *imxmd) +int imx_media_dev_notifier_register(struct imx_media_dev *imxmd, + const struct v4l2_async_notifier_operations *ops) { int ret; @@ -76,7 +408,7 @@ int imx_media_dev_notifier_register(struct imx_media_dev *imxmd) } /* prepare the async subdev notifier and register it */ - imxmd->notifier.ops = &imx_media_subdev_ops; + imxmd->notifier.ops = ops ? ops : &imx_media_notifier_ops; ret = v4l2_async_notifier_register(&imxmd->v4l2_dev, &imxmd->notifier); if (ret) { diff --git a/drivers/staging/media/imx/imx-media-dev.c b/drivers/staging/media/imx/imx-media-dev.c index 6be95584006d..6ac371f6e971 100644 --- a/drivers/staging/media/imx/imx-media-dev.c +++ b/drivers/staging/media/imx/imx-media-dev.c @@ -2,24 +2,13 @@ /* * V4L2 Media Controller Driver for Freescale i.MX5/6 SOC * - * Copyright (c) 2016 Mentor Graphics Inc. + * Copyright (c) 2016-2019 Mentor Graphics Inc. */ -#include <linux/delay.h> #include <linux/fs.h> #include <linux/module.h> -#include <linux/of_graph.h> -#include <linux/of_platform.h> -#include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/spinlock.h> -#include <linux/timer.h> -#include <media/v4l2-ctrls.h> +#include <media/v4l2-async.h> #include <media/v4l2-event.h> -#include <media/v4l2-ioctl.h> -#include <media/v4l2-mc.h> -#include <video/imx-ipu-v3.h> #include <media/imx.h> #include "imx-media.h" @@ -28,433 +17,31 @@ static inline struct imx_media_dev *notifier2dev(struct v4l2_async_notifier *n) return container_of(n, struct imx_media_dev, notifier); } -/* - * Adds a subdev to the root notifier's async subdev list. If fwnode is - * non-NULL, adds the async as a V4L2_ASYNC_MATCH_FWNODE match type, - * otherwise as a V4L2_ASYNC_MATCH_DEVNAME match type using the dev_name - * of the given platform_device. This is called during driver load when - * forming the async subdev list. - */ -int imx_media_add_async_subdev(struct imx_media_dev *imxmd, - struct fwnode_handle *fwnode, - struct platform_device *pdev) -{ - struct device_node *np = to_of_node(fwnode); - struct imx_media_async_subdev *imxasd; - struct v4l2_async_subdev *asd; - const char *devname = NULL; - int ret; - - if (fwnode) { - asd = v4l2_async_notifier_add_fwnode_subdev(&imxmd->notifier, - fwnode, - sizeof(*imxasd)); - } else { - devname = dev_name(&pdev->dev); - asd = v4l2_async_notifier_add_devname_subdev(&imxmd->notifier, - devname, - sizeof(*imxasd)); - } - - if (IS_ERR(asd)) { - ret = PTR_ERR(asd); - if (ret == -EEXIST) { - if (np) - dev_dbg(imxmd->md.dev, "%s: already added %pOFn\n", - __func__, np); - else - dev_dbg(imxmd->md.dev, "%s: already added %s\n", - __func__, devname); - } - return ret; - } - - imxasd = to_imx_media_asd(asd); - - if (devname) - imxasd->pdev = pdev; - - if (np) - dev_dbg(imxmd->md.dev, "%s: added %pOFn, match type FWNODE\n", - __func__, np); - else - dev_dbg(imxmd->md.dev, "%s: added %s, match type DEVNAME\n", - __func__, devname); - - return 0; -} - -/* - * get IPU from this CSI and add it to the list of IPUs - * the media driver will control. - */ -static int imx_media_get_ipu(struct imx_media_dev *imxmd, - struct v4l2_subdev *csi_sd) -{ - struct ipu_soc *ipu; - int ipu_id; - - ipu = dev_get_drvdata(csi_sd->dev->parent); - if (!ipu) { - v4l2_err(&imxmd->v4l2_dev, - "CSI %s has no parent IPU!\n", csi_sd->name); - return -ENODEV; - } - - ipu_id = ipu_get_num(ipu); - if (ipu_id > 1) { - v4l2_err(&imxmd->v4l2_dev, "invalid IPU id %d!\n", ipu_id); - return -ENODEV; - } - - if (!imxmd->ipu[ipu_id]) - imxmd->ipu[ipu_id] = ipu; - - return 0; -} - /* async subdev bound notifier */ -int imx_media_subdev_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd) +static int imx_media_subdev_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_subdev *asd) { struct imx_media_dev *imxmd = notifier2dev(notifier); - int ret = 0; - - mutex_lock(&imxmd->mutex); + int ret; if (sd->grp_id & IMX_MEDIA_GRP_ID_IPU_CSI) { - ret = imx_media_get_ipu(imxmd, sd); + /* register the IPU internal subdevs */ + ret = imx_media_register_ipu_internal_subdevs(imxmd, sd); if (ret) - goto out; + return ret; } v4l2_info(&imxmd->v4l2_dev, "subdev %s bound\n", sd->name); -out: - mutex_unlock(&imxmd->mutex); - return ret; -} - -/* - * Create the media links for all subdevs that registered. - * Called after all async subdevs have bound. - */ -static int imx_media_create_links(struct v4l2_async_notifier *notifier) -{ - struct imx_media_dev *imxmd = notifier2dev(notifier); - struct v4l2_subdev *sd; - int ret; - - list_for_each_entry(sd, &imxmd->v4l2_dev.subdevs, list) { - switch (sd->grp_id) { - case IMX_MEDIA_GRP_ID_IPU_VDIC: - case IMX_MEDIA_GRP_ID_IPU_IC_PRP: - case IMX_MEDIA_GRP_ID_IPU_IC_PRPENC: - case IMX_MEDIA_GRP_ID_IPU_IC_PRPVF: - case IMX_MEDIA_GRP_ID_IPU_CSI0: - case IMX_MEDIA_GRP_ID_IPU_CSI1: - ret = imx_media_create_ipu_internal_links(imxmd, sd); - if (ret) - return ret; - /* - * the CSIs straddle between the external and the IPU - * internal entities, so create the external links - * to the CSI sink pads. - */ - if (sd->grp_id & IMX_MEDIA_GRP_ID_IPU_CSI) - imx_media_create_csi_of_links(imxmd, sd); - break; - case IMX_MEDIA_GRP_ID_CSI: - imx_media_create_csi_of_links(imxmd, sd); - - break; - default: - /* - * if this subdev has fwnode links, create media - * links for them. - */ - imx_media_create_of_links(imxmd, sd); - break; - } - } - - return 0; -} - -/* - * adds given video device to given imx-media source pad vdev list. - * Continues upstream from the pad entity's sink pads. - */ -static int imx_media_add_vdev_to_pad(struct imx_media_dev *imxmd, - struct imx_media_video_dev *vdev, - struct media_pad *srcpad) -{ - struct media_entity *entity = srcpad->entity; - struct imx_media_pad_vdev *pad_vdev; - struct list_head *pad_vdev_list; - struct media_link *link; - struct v4l2_subdev *sd; - int i, ret; - - /* skip this entity if not a v4l2_subdev */ - if (!is_media_entity_v4l2_subdev(entity)) - return 0; - - sd = media_entity_to_v4l2_subdev(entity); - - pad_vdev_list = to_pad_vdev_list(sd, srcpad->index); - if (!pad_vdev_list) { - v4l2_warn(&imxmd->v4l2_dev, "%s:%u has no vdev list!\n", - entity->name, srcpad->index); - /* - * shouldn't happen, but no reason to fail driver load, - * just skip this entity. - */ - return 0; - } - - /* just return if we've been here before */ - list_for_each_entry(pad_vdev, pad_vdev_list, list) { - if (pad_vdev->vdev == vdev) - return 0; - } - - dev_dbg(imxmd->md.dev, "adding %s to pad %s:%u\n", - vdev->vfd->entity.name, entity->name, srcpad->index); - - pad_vdev = devm_kzalloc(imxmd->md.dev, sizeof(*pad_vdev), GFP_KERNEL); - if (!pad_vdev) - return -ENOMEM; - - /* attach this vdev to this pad */ - pad_vdev->vdev = vdev; - list_add_tail(&pad_vdev->list, pad_vdev_list); - - /* move upstream from this entity's sink pads */ - for (i = 0; i < entity->num_pads; i++) { - struct media_pad *pad = &entity->pads[i]; - - if (!(pad->flags & MEDIA_PAD_FL_SINK)) - continue; - - list_for_each_entry(link, &entity->links, list) { - if (link->sink != pad) - continue; - ret = imx_media_add_vdev_to_pad(imxmd, vdev, - link->source); - if (ret) - return ret; - } - } - - return 0; -} - -/* - * For every subdevice, allocate an array of list_head's, one list_head - * for each pad, to hold the list of video devices reachable from that - * pad. - */ -static int imx_media_alloc_pad_vdev_lists(struct imx_media_dev *imxmd) -{ - struct list_head *vdev_lists; - struct media_entity *entity; - struct v4l2_subdev *sd; - int i; - - list_for_each_entry(sd, &imxmd->v4l2_dev.subdevs, list) { - entity = &sd->entity; - vdev_lists = devm_kcalloc(imxmd->md.dev, - entity->num_pads, sizeof(*vdev_lists), - GFP_KERNEL); - if (!vdev_lists) - return -ENOMEM; - - /* attach to the subdev's host private pointer */ - sd->host_priv = vdev_lists; - - for (i = 0; i < entity->num_pads; i++) - INIT_LIST_HEAD(to_pad_vdev_list(sd, i)); - } - - return 0; -} - -/* form the vdev lists in all imx-media source pads */ -static int imx_media_create_pad_vdev_lists(struct imx_media_dev *imxmd) -{ - struct imx_media_video_dev *vdev; - struct media_link *link; - int ret; - - ret = imx_media_alloc_pad_vdev_lists(imxmd); - if (ret) - return ret; - - list_for_each_entry(vdev, &imxmd->vdev_list, list) { - link = list_first_entry(&vdev->vfd->entity.links, - struct media_link, list); - ret = imx_media_add_vdev_to_pad(imxmd, vdev, link->source); - if (ret) - return ret; - } return 0; } /* async subdev complete notifier */ -int imx_media_probe_complete(struct v4l2_async_notifier *notifier) -{ - struct imx_media_dev *imxmd = notifier2dev(notifier); - int ret; - - mutex_lock(&imxmd->mutex); - - ret = imx_media_create_links(notifier); - if (ret) - goto unlock; - - ret = imx_media_create_pad_vdev_lists(imxmd); - if (ret) - goto unlock; - - ret = v4l2_device_register_subdev_nodes(&imxmd->v4l2_dev); -unlock: - mutex_unlock(&imxmd->mutex); - if (ret) - return ret; - - return media_device_register(&imxmd->md); -} - -/* - * adds controls to a video device from an entity subdevice. - * Continues upstream from the entity's sink pads. - */ -static int imx_media_inherit_controls(struct imx_media_dev *imxmd, - struct video_device *vfd, - struct media_entity *entity) -{ - int i, ret = 0; - - if (is_media_entity_v4l2_subdev(entity)) { - struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); - - dev_dbg(imxmd->md.dev, - "adding controls to %s from %s\n", - vfd->entity.name, sd->entity.name); - - ret = v4l2_ctrl_add_handler(vfd->ctrl_handler, - sd->ctrl_handler, - NULL, true); - if (ret) - return ret; - } - - /* move upstream */ - for (i = 0; i < entity->num_pads; i++) { - struct media_pad *pad, *spad = &entity->pads[i]; - - if (!(spad->flags & MEDIA_PAD_FL_SINK)) - continue; - - pad = media_entity_remote_pad(spad); - if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) - continue; - - ret = imx_media_inherit_controls(imxmd, vfd, pad->entity); - if (ret) - break; - } - - return ret; -} - -int imx_media_link_notify(struct media_link *link, u32 flags, - unsigned int notification) -{ - struct media_entity *source = link->source->entity; - struct imx_media_pad_vdev *pad_vdev; - struct list_head *pad_vdev_list; - struct imx_media_dev *imxmd; - struct video_device *vfd; - struct v4l2_subdev *sd; - int pad_idx, ret; - - ret = v4l2_pipeline_link_notify(link, flags, notification); - if (ret) - return ret; - - /* don't bother if source is not a subdev */ - if (!is_media_entity_v4l2_subdev(source)) - return 0; - - sd = media_entity_to_v4l2_subdev(source); - pad_idx = link->source->index; - - imxmd = dev_get_drvdata(sd->v4l2_dev->dev); - - pad_vdev_list = to_pad_vdev_list(sd, pad_idx); - if (!pad_vdev_list) { - /* shouldn't happen, but no reason to fail link setup */ - return 0; - } - - /* - * Before disabling a link, reset controls for all video - * devices reachable from this link. - * - * After enabling a link, refresh controls for all video - * devices reachable from this link. - */ - if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH && - !(flags & MEDIA_LNK_FL_ENABLED)) { - list_for_each_entry(pad_vdev, pad_vdev_list, list) { - vfd = pad_vdev->vdev->vfd; - dev_dbg(imxmd->md.dev, - "reset controls for %s\n", - vfd->entity.name); - v4l2_ctrl_handler_free(vfd->ctrl_handler); - v4l2_ctrl_handler_init(vfd->ctrl_handler, 0); - } - } else if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH && - (link->flags & MEDIA_LNK_FL_ENABLED)) { - list_for_each_entry(pad_vdev, pad_vdev_list, list) { - vfd = pad_vdev->vdev->vfd; - dev_dbg(imxmd->md.dev, - "refresh controls for %s\n", - vfd->entity.name); - ret = imx_media_inherit_controls(imxmd, vfd, - &vfd->entity); - if (ret) - break; - } - } - - return ret; -} - -void imx_media_notify(struct v4l2_subdev *sd, unsigned int notification, - void *arg) -{ - struct media_entity *entity = &sd->entity; - int i; - - if (notification != V4L2_DEVICE_NOTIFY_EVENT) - return; - - for (i = 0; i < entity->num_pads; i++) { - struct media_pad *pad = &entity->pads[i]; - struct imx_media_pad_vdev *pad_vdev; - struct list_head *pad_vdev_list; - - pad_vdev_list = to_pad_vdev_list(sd, pad->index); - if (!pad_vdev_list) - continue; - list_for_each_entry(pad_vdev, pad_vdev_list, list) - v4l2_event_queue(pad_vdev->vdev->vfd, arg); - } -} +static const struct v4l2_async_notifier_operations imx_media_notifier_ops = { + .bound = imx_media_subdev_bound, + .complete = imx_media_probe_complete, +}; static int imx_media_probe(struct platform_device *pdev) { @@ -463,7 +50,7 @@ static int imx_media_probe(struct platform_device *pdev) struct imx_media_dev *imxmd; int ret; - imxmd = imx_media_dev_init(dev); + imxmd = imx_media_dev_init(dev, NULL); if (IS_ERR(imxmd)) return PTR_ERR(imxmd); @@ -474,14 +61,12 @@ static int imx_media_probe(struct platform_device *pdev) goto cleanup; } - ret = imx_media_dev_notifier_register(imxmd); + ret = imx_media_dev_notifier_register(imxmd, &imx_media_notifier_ops); if (ret) - goto del_int; + goto cleanup; return 0; -del_int: - imx_media_remove_ipu_internal_subdevs(imxmd); cleanup: v4l2_async_notifier_cleanup(&imxmd->notifier); v4l2_device_unregister(&imxmd->v4l2_dev); @@ -498,7 +83,7 @@ static int imx_media_remove(struct platform_device *pdev) v4l2_info(&imxmd->v4l2_dev, "Removing imx-media\n"); v4l2_async_notifier_unregister(&imxmd->notifier); - imx_media_remove_ipu_internal_subdevs(imxmd); + imx_media_unregister_ipu_internal_subdevs(imxmd); v4l2_async_notifier_cleanup(&imxmd->notifier); media_device_unregister(&imxmd->md); v4l2_device_unregister(&imxmd->v4l2_dev); diff --git a/drivers/staging/media/imx/imx-media-fim.c b/drivers/staging/media/imx/imx-media-fim.c index 2ab64bc30f5c..3a9182933508 100644 --- a/drivers/staging/media/imx/imx-media-fim.c +++ b/drivers/staging/media/imx/imx-media-fim.c @@ -37,8 +37,6 @@ enum { #define FIM_CL_TOLERANCE_MAX_DEF 0 /* no max tolerance (unbounded) */ struct imx_media_fim { - struct imx_media_dev *md; - /* the owning subdev of this fim instance */ struct v4l2_subdev *sd; @@ -416,7 +414,6 @@ void imx_media_fim_eof_monitor(struct imx_media_fim *fim, ktime_t timestamp) spin_unlock_irqrestore(&fim->lock, flags); } -EXPORT_SYMBOL_GPL(imx_media_fim_eof_monitor); /* Called by the subdev in its s_stream callback */ int imx_media_fim_set_stream(struct imx_media_fim *fim, @@ -453,7 +450,6 @@ out: v4l2_ctrl_unlock(fim->ctrl[FIM_CL_ENABLE]); return ret; } -EXPORT_SYMBOL_GPL(imx_media_fim_set_stream); int imx_media_fim_add_controls(struct imx_media_fim *fim) { @@ -461,7 +457,6 @@ int imx_media_fim_add_controls(struct imx_media_fim *fim) return v4l2_ctrl_add_handler(fim->sd->ctrl_handler, &fim->ctrl_handler, NULL, false); } -EXPORT_SYMBOL_GPL(imx_media_fim_add_controls); /* Called by the subdev in its subdev registered callback */ struct imx_media_fim *imx_media_fim_init(struct v4l2_subdev *sd) @@ -473,8 +468,6 @@ struct imx_media_fim *imx_media_fim_init(struct v4l2_subdev *sd) if (!fim) return ERR_PTR(-ENOMEM); - /* get media device */ - fim->md = dev_get_drvdata(sd->v4l2_dev->dev); fim->sd = sd; spin_lock_init(&fim->lock); @@ -485,10 +478,8 @@ struct imx_media_fim *imx_media_fim_init(struct v4l2_subdev *sd) return fim; } -EXPORT_SYMBOL_GPL(imx_media_fim_init); void imx_media_fim_free(struct imx_media_fim *fim) { v4l2_ctrl_handler_free(&fim->ctrl_handler); } -EXPORT_SYMBOL_GPL(imx_media_fim_free); diff --git a/drivers/staging/media/imx/imx-media-internal-sd.c b/drivers/staging/media/imx/imx-media-internal-sd.c index df49ebfbe98a..cb1e4cdd5079 100644 --- a/drivers/staging/media/imx/imx-media-internal-sd.c +++ b/drivers/staging/media/imx/imx-media-internal-sd.c @@ -9,208 +9,138 @@ #include <linux/platform_device.h> #include "imx-media.h" -enum isd_enum { - isd_csi0 = 0, - isd_csi1, - isd_vdic, - isd_ic_prp, - isd_ic_prpenc, - isd_ic_prpvf, - num_isd, -}; - -static const struct internal_subdev_id { - enum isd_enum index; - const char *name; - u32 grp_id; -} isd_id[num_isd] = { - [isd_csi0] = { - .index = isd_csi0, - .grp_id = IMX_MEDIA_GRP_ID_IPU_CSI0, - .name = "imx-ipuv3-csi", - }, - [isd_csi1] = { - .index = isd_csi1, - .grp_id = IMX_MEDIA_GRP_ID_IPU_CSI1, - .name = "imx-ipuv3-csi", - }, - [isd_vdic] = { - .index = isd_vdic, - .grp_id = IMX_MEDIA_GRP_ID_IPU_VDIC, - .name = "imx-ipuv3-vdic", - }, - [isd_ic_prp] = { - .index = isd_ic_prp, - .grp_id = IMX_MEDIA_GRP_ID_IPU_IC_PRP, - .name = "imx-ipuv3-ic", - }, - [isd_ic_prpenc] = { - .index = isd_ic_prpenc, - .grp_id = IMX_MEDIA_GRP_ID_IPU_IC_PRPENC, - .name = "imx-ipuv3-ic", - }, - [isd_ic_prpvf] = { - .index = isd_ic_prpvf, - .grp_id = IMX_MEDIA_GRP_ID_IPU_IC_PRPVF, - .name = "imx-ipuv3-ic", - }, -}; +/* max pads per internal-sd */ +#define MAX_INTERNAL_PADS 8 +/* max links per internal-sd pad */ +#define MAX_INTERNAL_LINKS 8 struct internal_subdev; struct internal_link { - const struct internal_subdev *remote; + int remote; int local_pad; int remote_pad; }; -/* max pads per internal-sd */ -#define MAX_INTERNAL_PADS 8 -/* max links per internal-sd pad */ -#define MAX_INTERNAL_LINKS 8 - struct internal_pad { + int num_links; struct internal_link link[MAX_INTERNAL_LINKS]; }; -static const struct internal_subdev { - const struct internal_subdev_id *id; +struct internal_subdev { + u32 grp_id; struct internal_pad pad[MAX_INTERNAL_PADS]; -} int_subdev[num_isd] = { - [isd_csi0] = { - .id = &isd_id[isd_csi0], + + struct v4l2_subdev * (*sync_register)(struct v4l2_device *v4l2_dev, + struct device *ipu_dev, + struct ipu_soc *ipu, + u32 grp_id); + int (*sync_unregister)(struct v4l2_subdev *sd); +}; + +static const struct internal_subdev int_subdev[NUM_IPU_SUBDEVS] = { + [IPU_CSI0] = { + .grp_id = IMX_MEDIA_GRP_ID_IPU_CSI0, .pad[CSI_SRC_PAD_DIRECT] = { + .num_links = 2, .link = { { .local_pad = CSI_SRC_PAD_DIRECT, - .remote = &int_subdev[isd_ic_prp], + .remote = IPU_IC_PRP, .remote_pad = PRP_SINK_PAD, }, { .local_pad = CSI_SRC_PAD_DIRECT, - .remote = &int_subdev[isd_vdic], + .remote = IPU_VDIC, .remote_pad = VDIC_SINK_PAD_DIRECT, }, }, }, }, - [isd_csi1] = { - .id = &isd_id[isd_csi1], + [IPU_CSI1] = { + .grp_id = IMX_MEDIA_GRP_ID_IPU_CSI1, .pad[CSI_SRC_PAD_DIRECT] = { + .num_links = 2, .link = { { .local_pad = CSI_SRC_PAD_DIRECT, - .remote = &int_subdev[isd_ic_prp], + .remote = IPU_IC_PRP, .remote_pad = PRP_SINK_PAD, }, { .local_pad = CSI_SRC_PAD_DIRECT, - .remote = &int_subdev[isd_vdic], + .remote = IPU_VDIC, .remote_pad = VDIC_SINK_PAD_DIRECT, }, }, }, }, - [isd_vdic] = { - .id = &isd_id[isd_vdic], + [IPU_VDIC] = { + .grp_id = IMX_MEDIA_GRP_ID_IPU_VDIC, + .sync_register = imx_media_vdic_register, + .sync_unregister = imx_media_vdic_unregister, .pad[VDIC_SRC_PAD_DIRECT] = { + .num_links = 1, .link = { { .local_pad = VDIC_SRC_PAD_DIRECT, - .remote = &int_subdev[isd_ic_prp], + .remote = IPU_IC_PRP, .remote_pad = PRP_SINK_PAD, }, }, }, }, - [isd_ic_prp] = { - .id = &isd_id[isd_ic_prp], + [IPU_IC_PRP] = { + .grp_id = IMX_MEDIA_GRP_ID_IPU_IC_PRP, + .sync_register = imx_media_ic_register, + .sync_unregister = imx_media_ic_unregister, .pad[PRP_SRC_PAD_PRPENC] = { + .num_links = 1, .link = { { .local_pad = PRP_SRC_PAD_PRPENC, - .remote = &int_subdev[isd_ic_prpenc], - .remote_pad = 0, + .remote = IPU_IC_PRPENC, + .remote_pad = PRPENCVF_SINK_PAD, }, }, }, .pad[PRP_SRC_PAD_PRPVF] = { + .num_links = 1, .link = { { .local_pad = PRP_SRC_PAD_PRPVF, - .remote = &int_subdev[isd_ic_prpvf], - .remote_pad = 0, + .remote = IPU_IC_PRPVF, + .remote_pad = PRPENCVF_SINK_PAD, }, }, }, }, - [isd_ic_prpenc] = { - .id = &isd_id[isd_ic_prpenc], + [IPU_IC_PRPENC] = { + .grp_id = IMX_MEDIA_GRP_ID_IPU_IC_PRPENC, + .sync_register = imx_media_ic_register, + .sync_unregister = imx_media_ic_unregister, }, - [isd_ic_prpvf] = { - .id = &isd_id[isd_ic_prpvf], + [IPU_IC_PRPVF] = { + .grp_id = IMX_MEDIA_GRP_ID_IPU_IC_PRPVF, + .sync_register = imx_media_ic_register, + .sync_unregister = imx_media_ic_unregister, }, }; -/* form a device name given an internal subdev and ipu id */ -static inline void isd_to_devname(char *devname, int sz, - const struct internal_subdev *isd, - int ipu_id) -{ - int pdev_id = ipu_id * num_isd + isd->id->index; - - snprintf(devname, sz, "%s.%d", isd->id->name, pdev_id); -} - -static const struct internal_subdev *find_intsd_by_grp_id(u32 grp_id) -{ - enum isd_enum i; - - for (i = 0; i < num_isd; i++) { - const struct internal_subdev *isd = &int_subdev[i]; - - if (isd->id->grp_id == grp_id) - return isd; - } - - return NULL; -} - -static struct v4l2_subdev *find_sink(struct imx_media_dev *imxmd, - struct v4l2_subdev *src, - const struct internal_link *link) -{ - char sink_devname[32]; - int ipu_id; - - /* - * retrieve IPU id from subdev name, note: can't get this from - * struct imx_media_ipu_internal_sd_pdata because if src is - * a CSI, it has different struct ipu_client_platformdata which - * does not contain IPU id. - */ - if (sscanf(src->name, "ipu%d", &ipu_id) != 1) - return NULL; - - isd_to_devname(sink_devname, sizeof(sink_devname), - link->remote, ipu_id - 1); - - return imx_media_find_subdev_by_devname(imxmd, sink_devname); -} - -static int create_ipu_internal_link(struct imx_media_dev *imxmd, - struct v4l2_subdev *src, - const struct internal_link *link) +static int create_internal_link(struct imx_media_dev *imxmd, + struct v4l2_subdev *src, + struct v4l2_subdev *sink, + const struct internal_link *link) { - struct v4l2_subdev *sink; int ret; - sink = find_sink(imxmd, src, link); - if (!sink) - return -ENODEV; + /* skip if this link already created */ + if (media_entity_find_link(&src->entity.pads[link->local_pad], + &sink->entity.pads[link->remote_pad])) + return 0; v4l2_info(&imxmd->v4l2_dev, "%s:%d -> %s:%d\n", src->name, link->local_pad, @@ -219,25 +149,21 @@ static int create_ipu_internal_link(struct imx_media_dev *imxmd, ret = media_create_pad_link(&src->entity, link->local_pad, &sink->entity, link->remote_pad, 0); if (ret) - v4l2_err(&imxmd->v4l2_dev, - "create_pad_link failed: %d\n", ret); + v4l2_err(&imxmd->v4l2_dev, "%s failed: %d\n", __func__, ret); return ret; } -int imx_media_create_ipu_internal_links(struct imx_media_dev *imxmd, - struct v4l2_subdev *sd) +static int create_ipu_internal_links(struct imx_media_dev *imxmd, + const struct internal_subdev *intsd, + struct v4l2_subdev *sd, + int ipu_id) { - const struct internal_subdev *intsd; const struct internal_pad *intpad; const struct internal_link *link; struct media_pad *pad; int i, j, ret; - intsd = find_intsd_by_grp_id(sd->grp_id); - if (!intsd) - return -ENODEV; - /* create the source->sink links */ for (i = 0; i < sd->entity.num_pads; i++) { intpad = &intsd->pad[i]; @@ -246,13 +172,13 @@ int imx_media_create_ipu_internal_links(struct imx_media_dev *imxmd, if (!(pad->flags & MEDIA_PAD_FL_SOURCE)) continue; - for (j = 0; ; j++) { - link = &intpad->link[j]; + for (j = 0; j < intpad->num_links; j++) { + struct v4l2_subdev *sink; - if (!link->remote) - break; + link = &intpad->link[j]; + sink = imxmd->sync_sd[ipu_id][link->remote]; - ret = create_ipu_internal_link(imxmd, sd, link); + ret = create_internal_link(imxmd, sd, sink, link); if (ret) return ret; } @@ -261,85 +187,116 @@ int imx_media_create_ipu_internal_links(struct imx_media_dev *imxmd, return 0; } -/* register an internal subdev as a platform device */ -static int add_internal_subdev(struct imx_media_dev *imxmd, - const struct internal_subdev *isd, - int ipu_id) +int imx_media_register_ipu_internal_subdevs(struct imx_media_dev *imxmd, + struct v4l2_subdev *csi) { - struct imx_media_ipu_internal_sd_pdata pdata; - struct platform_device_info pdevinfo = {}; - struct platform_device *pdev; + struct device *ipu_dev = csi->dev->parent; + const struct internal_subdev *intsd; + struct v4l2_subdev *sd; + struct ipu_soc *ipu; + int i, ipu_id, ret; - pdata.grp_id = isd->id->grp_id; + ipu = dev_get_drvdata(ipu_dev); + if (!ipu) { + v4l2_err(&imxmd->v4l2_dev, "invalid IPU device!\n"); + return -ENODEV; + } - /* the id of IPU this subdev will control */ - pdata.ipu_id = ipu_id; + ipu_id = ipu_get_num(ipu); + if (ipu_id > 1) { + v4l2_err(&imxmd->v4l2_dev, "invalid IPU id %d!\n", ipu_id); + return -ENODEV; + } - /* create subdev name */ - imx_media_grp_id_to_sd_name(pdata.sd_name, sizeof(pdata.sd_name), - pdata.grp_id, ipu_id); + mutex_lock(&imxmd->mutex); - pdevinfo.name = isd->id->name; - pdevinfo.id = ipu_id * num_isd + isd->id->index; - pdevinfo.parent = imxmd->md.dev; - pdevinfo.data = &pdata; - pdevinfo.size_data = sizeof(pdata); - pdevinfo.dma_mask = DMA_BIT_MASK(32); + /* register the synchronous subdevs */ + for (i = 0; i < NUM_IPU_SUBDEVS; i++) { + intsd = &int_subdev[i]; - pdev = platform_device_register_full(&pdevinfo); - if (IS_ERR(pdev)) - return PTR_ERR(pdev); + sd = imxmd->sync_sd[ipu_id][i]; - return imx_media_add_async_subdev(imxmd, NULL, pdev); -} + /* + * skip if this sync subdev already registered or its + * not a sync subdev (one of the CSIs) + */ + if (sd || !intsd->sync_register) + continue; -/* adds the internal subdevs in one ipu */ -int imx_media_add_ipu_internal_subdevs(struct imx_media_dev *imxmd, - int ipu_id) -{ - enum isd_enum i; - int ret; + mutex_unlock(&imxmd->mutex); + sd = intsd->sync_register(&imxmd->v4l2_dev, ipu_dev, ipu, + intsd->grp_id); + mutex_lock(&imxmd->mutex); + if (IS_ERR(sd)) { + ret = PTR_ERR(sd); + goto err_unwind; + } - for (i = 0; i < num_isd; i++) { - const struct internal_subdev *isd = &int_subdev[i]; + imxmd->sync_sd[ipu_id][i] = sd; + } - /* - * the CSIs are represented in the device-tree, so those - * devices are already added to the async subdev list by - * of_parse_subdev(). - */ - switch (isd->id->grp_id) { - case IMX_MEDIA_GRP_ID_IPU_CSI0: - case IMX_MEDIA_GRP_ID_IPU_CSI1: - ret = 0; - break; - default: - ret = add_internal_subdev(imxmd, isd, ipu_id); - break; + /* + * all the sync subdevs are registered, create the media links + * between them. + */ + for (i = 0; i < NUM_IPU_SUBDEVS; i++) { + intsd = &int_subdev[i]; + + if (intsd->grp_id == csi->grp_id) { + sd = csi; + } else { + sd = imxmd->sync_sd[ipu_id][i]; + if (!sd) + continue; } - if (ret) - goto remove; + ret = create_ipu_internal_links(imxmd, intsd, sd, ipu_id); + if (ret) { + mutex_unlock(&imxmd->mutex); + imx_media_unregister_ipu_internal_subdevs(imxmd); + return ret; + } } + mutex_unlock(&imxmd->mutex); return 0; -remove: - imx_media_remove_ipu_internal_subdevs(imxmd); +err_unwind: + while (--i >= 0) { + intsd = &int_subdev[i]; + sd = imxmd->sync_sd[ipu_id][i]; + if (!sd || !intsd->sync_unregister) + continue; + mutex_unlock(&imxmd->mutex); + intsd->sync_unregister(sd); + mutex_lock(&imxmd->mutex); + } + + mutex_unlock(&imxmd->mutex); return ret; } -void imx_media_remove_ipu_internal_subdevs(struct imx_media_dev *imxmd) +void imx_media_unregister_ipu_internal_subdevs(struct imx_media_dev *imxmd) { - struct imx_media_async_subdev *imxasd; - struct v4l2_async_subdev *asd; + const struct internal_subdev *intsd; + struct v4l2_subdev *sd; + int i, j; - list_for_each_entry(asd, &imxmd->notifier.asd_list, asd_list) { - imxasd = to_imx_media_asd(asd); + mutex_lock(&imxmd->mutex); - if (!imxasd->pdev) - continue; + for (i = 0; i < 2; i++) { + for (j = 0; j < NUM_IPU_SUBDEVS; j++) { + intsd = &int_subdev[j]; + sd = imxmd->sync_sd[i][j]; + + if (!sd || !intsd->sync_unregister) + continue; - platform_device_unregister(imxasd->pdev); + mutex_unlock(&imxmd->mutex); + intsd->sync_unregister(sd); + mutex_lock(&imxmd->mutex); + } } + + mutex_unlock(&imxmd->mutex); } diff --git a/drivers/staging/media/imx/imx-media-of.c b/drivers/staging/media/imx/imx-media-of.c index 990e82aa8e42..2d3efd2a6dde 100644 --- a/drivers/staging/media/imx/imx-media-of.c +++ b/drivers/staging/media/imx/imx-media-of.c @@ -19,6 +19,9 @@ int imx_media_of_add_csi(struct imx_media_dev *imxmd, struct device_node *csi_np) { + struct v4l2_async_subdev *asd; + int ret = 0; + if (!of_device_is_available(csi_np)) { dev_dbg(imxmd->md.dev, "%s: %pOFn not enabled\n", __func__, csi_np); @@ -26,18 +29,25 @@ int imx_media_of_add_csi(struct imx_media_dev *imxmd, } /* add CSI fwnode to async notifier */ - return imx_media_add_async_subdev(imxmd, of_fwnode_handle(csi_np), - NULL); + asd = v4l2_async_notifier_add_fwnode_subdev(&imxmd->notifier, + of_fwnode_handle(csi_np), + sizeof(*asd)); + if (IS_ERR(asd)) { + ret = PTR_ERR(asd); + if (ret == -EEXIST) + dev_dbg(imxmd->md.dev, "%s: already added %pOFn\n", + __func__, csi_np); + } + + return ret; } EXPORT_SYMBOL_GPL(imx_media_of_add_csi); int imx_media_add_of_subdevs(struct imx_media_dev *imxmd, struct device_node *np) { - bool ipu_found[2] = {false, false}; struct device_node *csi_np; int i, ret; - u32 ipu_id; for (i = 0; ; i++) { csi_np = of_parse_phandle(np, "ports", i); @@ -55,34 +65,15 @@ int imx_media_add_of_subdevs(struct imx_media_dev *imxmd, /* other error, can't continue */ goto err_out; } - - ret = of_alias_get_id(csi_np->parent, "ipu"); - if (ret < 0) - goto err_out; - if (ret > 1) { - ret = -EINVAL; - goto err_out; - } - - ipu_id = ret; - - if (!ipu_found[ipu_id]) { - ret = imx_media_add_ipu_internal_subdevs(imxmd, - ipu_id); - if (ret) - goto err_out; - } - - ipu_found[ipu_id] = true; } return 0; err_out: - imx_media_remove_ipu_internal_subdevs(imxmd); of_node_put(csi_np); return ret; } +EXPORT_SYMBOL_GPL(imx_media_add_of_subdevs); /* * Create a single media link to/from sd using a fwnode link. @@ -152,6 +143,7 @@ int imx_media_create_of_links(struct imx_media_dev *imxmd, return 0; } +EXPORT_SYMBOL_GPL(imx_media_create_of_links); /* * Create media links to the given CSI subdevice's sink pads, @@ -195,3 +187,4 @@ int imx_media_create_csi_of_links(struct imx_media_dev *imxmd, return 0; } +EXPORT_SYMBOL_GPL(imx_media_create_csi_of_links); diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c index b41842dba5ec..b5b8a3b7730a 100644 --- a/drivers/staging/media/imx/imx-media-utils.c +++ b/drivers/staging/media/imx/imx-media-utils.c @@ -573,8 +573,7 @@ void imx_media_fill_default_mbus_fields(struct v4l2_mbus_framefmt *tryfmt, EXPORT_SYMBOL_GPL(imx_media_fill_default_mbus_fields); int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix, - struct v4l2_rect *compose, - const struct v4l2_mbus_framefmt *mbus, + struct v4l2_mbus_framefmt *mbus, const struct imx_media_pixfmt *cc) { u32 width; @@ -621,17 +620,6 @@ int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix, pix->sizeimage = cc->planar ? ((stride * pix->height * cc->bpp) >> 3) : stride * pix->height; - /* - * set capture compose rectangle, which is fixed to the - * source subdevice mbus format. - */ - if (compose) { - compose->left = 0; - compose->top = 0; - compose->width = mbus->width; - compose->height = mbus->height; - } - return 0; } EXPORT_SYMBOL_GPL(imx_media_mbus_fmt_to_pix_fmt); @@ -643,11 +631,13 @@ int imx_media_mbus_fmt_to_ipu_image(struct ipu_image *image, memset(image, 0, sizeof(*image)); - ret = imx_media_mbus_fmt_to_pix_fmt(&image->pix, &image->rect, - mbus, NULL); + ret = imx_media_mbus_fmt_to_pix_fmt(&image->pix, mbus, NULL); if (ret) return ret; + image->rect.width = mbus->width; + image->rect.height = mbus->height; + return 0; } EXPORT_SYMBOL_GPL(imx_media_mbus_fmt_to_ipu_image); @@ -675,29 +665,28 @@ int imx_media_ipu_image_to_mbus_fmt(struct v4l2_mbus_framefmt *mbus, } EXPORT_SYMBOL_GPL(imx_media_ipu_image_to_mbus_fmt); -void imx_media_free_dma_buf(struct imx_media_dev *imxmd, +void imx_media_free_dma_buf(struct device *dev, struct imx_media_dma_buf *buf) { if (buf->virt) - dma_free_coherent(imxmd->md.dev, buf->len, - buf->virt, buf->phys); + dma_free_coherent(dev, buf->len, buf->virt, buf->phys); buf->virt = NULL; buf->phys = 0; } EXPORT_SYMBOL_GPL(imx_media_free_dma_buf); -int imx_media_alloc_dma_buf(struct imx_media_dev *imxmd, +int imx_media_alloc_dma_buf(struct device *dev, struct imx_media_dma_buf *buf, int size) { - imx_media_free_dma_buf(imxmd, buf); + imx_media_free_dma_buf(dev, buf); buf->len = PAGE_ALIGN(size); - buf->virt = dma_alloc_coherent(imxmd->md.dev, buf->len, &buf->phys, + buf->virt = dma_alloc_coherent(dev, buf->len, &buf->phys, GFP_DMA | GFP_KERNEL); if (!buf->virt) { - dev_err(imxmd->md.dev, "failed to alloc dma buffer\n"); + dev_err(dev, "%s: failed\n", __func__); return -ENOMEM; } @@ -764,35 +753,37 @@ imx_media_find_subdev_by_devname(struct imx_media_dev *imxmd, EXPORT_SYMBOL_GPL(imx_media_find_subdev_by_devname); /* - * Adds a video device to the master video device list. This is called by - * an async subdev that owns a video device when it is registered. + * Adds a video device to the master video device list. This is called + * when a video device is registered. */ -int imx_media_add_video_device(struct imx_media_dev *imxmd, - struct imx_media_video_dev *vdev) +void imx_media_add_video_device(struct imx_media_dev *imxmd, + struct imx_media_video_dev *vdev) { mutex_lock(&imxmd->mutex); list_add_tail(&vdev->list, &imxmd->vdev_list); mutex_unlock(&imxmd->mutex); - return 0; } EXPORT_SYMBOL_GPL(imx_media_add_video_device); /* - * Search upstream/downstream for a subdevice in the current pipeline - * with given grp_id, starting from start_entity. Returns the subdev's - * source/sink pad that it was reached from. If grp_id is zero, just - * returns the nearest source/sink pad to start_entity. Must be called - * with mdev->graph_mutex held. + * Search upstream/downstream for a subdevice or video device pad in the + * current pipeline, starting from start_entity. Returns the device's + * source/sink pad that it was reached from. Must be called with + * mdev->graph_mutex held. + * + * If grp_id != 0, finds a subdevice's pad of given grp_id. + * Else If buftype != 0, finds a video device's pad of given buffer type. + * Else, returns the nearest source/sink pad to start_entity. */ -static struct media_pad * -find_pipeline_pad(struct imx_media_dev *imxmd, - struct media_entity *start_entity, - u32 grp_id, bool upstream) +struct media_pad * +imx_media_pipeline_pad(struct media_entity *start_entity, u32 grp_id, + enum v4l2_buf_type buftype, bool upstream) { struct media_entity *me = start_entity; struct media_pad *pad = NULL; + struct video_device *vfd; struct v4l2_subdev *sd; int i; @@ -804,16 +795,27 @@ find_pipeline_pad(struct imx_media_dev *imxmd, continue; pad = media_entity_remote_pad(spad); - if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + if (!pad) continue; - if (grp_id != 0) { - sd = media_entity_to_v4l2_subdev(pad->entity); - if (sd->grp_id & grp_id) - return pad; + if (grp_id) { + if (is_media_entity_v4l2_subdev(pad->entity)) { + sd = media_entity_to_v4l2_subdev(pad->entity); + if (sd->grp_id & grp_id) + return pad; + } + + return imx_media_pipeline_pad(pad->entity, grp_id, + buftype, upstream); + } else if (buftype) { + if (is_media_entity_v4l2_video_device(pad->entity)) { + vfd = media_entity_to_video_device(pad->entity); + if (buftype == vfd->queue->type) + return pad; + } - return find_pipeline_pad(imxmd, pad->entity, - grp_id, upstream); + return imx_media_pipeline_pad(pad->entity, grp_id, + buftype, upstream); } else { return pad; } @@ -821,28 +823,33 @@ find_pipeline_pad(struct imx_media_dev *imxmd, return NULL; } +EXPORT_SYMBOL_GPL(imx_media_pipeline_pad); /* - * Search upstream for a subdev in the current pipeline with - * given grp_id. Must be called with mdev->graph_mutex held. + * Search upstream/downstream for a subdev or video device in the current + * pipeline. Must be called with mdev->graph_mutex held. */ -static struct v4l2_subdev * -find_upstream_subdev(struct imx_media_dev *imxmd, - struct media_entity *start_entity, - u32 grp_id) +static struct media_entity * +find_pipeline_entity(struct media_entity *start, u32 grp_id, + enum v4l2_buf_type buftype, bool upstream) { + struct media_pad *pad = NULL; + struct video_device *vfd; struct v4l2_subdev *sd; - struct media_pad *pad; - if (is_media_entity_v4l2_subdev(start_entity)) { - sd = media_entity_to_v4l2_subdev(start_entity); + if (grp_id && is_media_entity_v4l2_subdev(start)) { + sd = media_entity_to_v4l2_subdev(start); if (sd->grp_id & grp_id) - return sd; + return &sd->entity; + } else if (buftype && is_media_entity_v4l2_video_device(start)) { + vfd = media_entity_to_video_device(pad->entity); + if (buftype == vfd->queue->type) + return &vfd->entity; } - pad = find_pipeline_pad(imxmd, start_entity, grp_id, true); + pad = imx_media_pipeline_pad(start, grp_id, buftype, upstream); - return pad ? media_entity_to_v4l2_subdev(pad->entity) : NULL; + return pad ? pad->entity : NULL; } /* @@ -850,62 +857,57 @@ find_upstream_subdev(struct imx_media_dev *imxmd, * start entity in the current pipeline. * Must be called with mdev->graph_mutex held. */ -int imx_media_find_mipi_csi2_channel(struct imx_media_dev *imxmd, - struct media_entity *start_entity) +int imx_media_pipeline_csi2_channel(struct media_entity *start_entity) { struct media_pad *pad; int ret = -EPIPE; - pad = find_pipeline_pad(imxmd, start_entity, IMX_MEDIA_GRP_ID_CSI2, - true); - if (pad) { + pad = imx_media_pipeline_pad(start_entity, IMX_MEDIA_GRP_ID_CSI2, + 0, true); + if (pad) ret = pad->index - 1; - dev_dbg(imxmd->md.dev, "found vc%d from %s\n", - ret, start_entity->name); - } return ret; } -EXPORT_SYMBOL_GPL(imx_media_find_mipi_csi2_channel); +EXPORT_SYMBOL_GPL(imx_media_pipeline_csi2_channel); /* - * Find a source pad reached upstream from the given start entity in - * the current pipeline. Must be called with mdev->graph_mutex held. + * Find a subdev reached upstream from the given start entity in + * the current pipeline. + * Must be called with mdev->graph_mutex held. */ -struct media_pad * -imx_media_find_upstream_pad(struct imx_media_dev *imxmd, - struct media_entity *start_entity, - u32 grp_id) +struct v4l2_subdev * +imx_media_pipeline_subdev(struct media_entity *start_entity, u32 grp_id, + bool upstream) { - struct media_pad *pad; + struct media_entity *me; - pad = find_pipeline_pad(imxmd, start_entity, grp_id, true); - if (!pad) + me = find_pipeline_entity(start_entity, grp_id, 0, upstream); + if (!me) return ERR_PTR(-ENODEV); - return pad; + return media_entity_to_v4l2_subdev(me); } -EXPORT_SYMBOL_GPL(imx_media_find_upstream_pad); +EXPORT_SYMBOL_GPL(imx_media_pipeline_subdev); /* * Find a subdev reached upstream from the given start entity in * the current pipeline. * Must be called with mdev->graph_mutex held. */ -struct v4l2_subdev * -imx_media_find_upstream_subdev(struct imx_media_dev *imxmd, - struct media_entity *start_entity, - u32 grp_id) +struct video_device * +imx_media_pipeline_video_device(struct media_entity *start_entity, + enum v4l2_buf_type buftype, bool upstream) { - struct v4l2_subdev *sd; + struct media_entity *me; - sd = find_upstream_subdev(imxmd, start_entity, grp_id); - if (!sd) + me = find_pipeline_entity(start_entity, 0, buftype, upstream); + if (!me) return ERR_PTR(-ENODEV); - return sd; + return media_entity_to_video_device(me); } -EXPORT_SYMBOL_GPL(imx_media_find_upstream_subdev); +EXPORT_SYMBOL_GPL(imx_media_pipeline_video_device); /* * Turn current pipeline streaming on/off starting from entity. diff --git a/drivers/staging/media/imx/imx-media-vdic.c b/drivers/staging/media/imx/imx-media-vdic.c index 4487374c9435..4d90eecb04a2 100644 --- a/drivers/staging/media/imx/imx-media-vdic.c +++ b/drivers/staging/media/imx/imx-media-vdic.c @@ -4,13 +4,6 @@ * * Copyright (c) 2017 Mentor Graphics Inc. */ -#include <linux/delay.h> -#include <linux/interrupt.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/timer.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> @@ -65,12 +58,11 @@ struct vdic_pipeline_ops { #define S_ALIGN 1 /* multiple of 2 */ struct vdic_priv { - struct device *dev; - struct ipu_soc *ipu; - struct imx_media_dev *md; + struct device *ipu_dev; + struct ipu_soc *ipu; + struct v4l2_subdev sd; struct media_pad pad[VDIC_NUM_PADS]; - int ipu_id; /* lock to protect all members below */ struct mutex lock; @@ -145,8 +137,6 @@ static int vdic_get_ipu_resources(struct vdic_priv *priv) struct ipuv3_channel *ch; struct ipu_vdi *vdi; - priv->ipu = priv->md->ipu[priv->ipu_id]; - vdi = ipu_vdi_get(priv->ipu); if (IS_ERR(vdi)) { v4l2_err(&priv->sd, "failed to get VDIC\n"); @@ -511,7 +501,8 @@ static int vdic_s_stream(struct v4l2_subdev *sd, int enable) if (priv->stream_count != !enable) goto update_count; - dev_dbg(priv->dev, "stream %s\n", enable ? "ON" : "OFF"); + dev_dbg(priv->ipu_dev, "%s: stream %s\n", sd->name, + enable ? "ON" : "OFF"); if (enable) ret = vdic_start(priv); @@ -686,8 +677,8 @@ static int vdic_link_setup(struct media_entity *entity, struct v4l2_subdev *remote_sd; int ret = 0; - dev_dbg(priv->dev, "link setup %s -> %s", remote->entity->name, - local->entity->name); + dev_dbg(priv->ipu_dev, "%s: link setup %s -> %s", + sd->name, remote->entity->name, local->entity->name); mutex_lock(&priv->lock); @@ -860,9 +851,6 @@ static int vdic_registered(struct v4l2_subdev *sd) int i, ret; u32 code; - /* get media device */ - priv->md = dev_get_drvdata(sd->v4l2_dev->dev); - for (i = 0; i < VDIC_NUM_PADS; i++) { priv->pad[i].flags = (i == VDIC_SRC_PAD_DIRECT) ? MEDIA_PAD_FL_SOURCE : MEDIA_PAD_FL_SINK; @@ -934,77 +922,53 @@ static const struct v4l2_subdev_internal_ops vdic_internal_ops = { .unregistered = vdic_unregistered, }; -static int imx_vdic_probe(struct platform_device *pdev) +struct v4l2_subdev *imx_media_vdic_register(struct v4l2_device *v4l2_dev, + struct device *ipu_dev, + struct ipu_soc *ipu, + u32 grp_id) { - struct imx_media_ipu_internal_sd_pdata *pdata; struct vdic_priv *priv; int ret; - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(ipu_dev, sizeof(*priv), GFP_KERNEL); if (!priv) - return -ENOMEM; + return ERR_PTR(-ENOMEM); - platform_set_drvdata(pdev, &priv->sd); - priv->dev = &pdev->dev; - - pdata = priv->dev->platform_data; - priv->ipu_id = pdata->ipu_id; + priv->ipu_dev = ipu_dev; + priv->ipu = ipu; v4l2_subdev_init(&priv->sd, &vdic_subdev_ops); v4l2_set_subdevdata(&priv->sd, priv); priv->sd.internal_ops = &vdic_internal_ops; priv->sd.entity.ops = &vdic_entity_ops; priv->sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; - priv->sd.dev = &pdev->dev; - priv->sd.owner = THIS_MODULE; + priv->sd.owner = ipu_dev->driver->owner; priv->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; - /* get our group id */ - priv->sd.grp_id = pdata->grp_id; - strscpy(priv->sd.name, pdata->sd_name, sizeof(priv->sd.name)); + priv->sd.grp_id = grp_id; + imx_media_grp_id_to_sd_name(priv->sd.name, sizeof(priv->sd.name), + priv->sd.grp_id, ipu_get_num(ipu)); mutex_init(&priv->lock); - ret = v4l2_async_register_subdev(&priv->sd); + ret = v4l2_device_register_subdev(v4l2_dev, &priv->sd); if (ret) goto free; - return 0; + return &priv->sd; free: mutex_destroy(&priv->lock); - return ret; + return ERR_PTR(ret); } -static int imx_vdic_remove(struct platform_device *pdev) +int imx_media_vdic_unregister(struct v4l2_subdev *sd) { - struct v4l2_subdev *sd = platform_get_drvdata(pdev); struct vdic_priv *priv = v4l2_get_subdevdata(sd); v4l2_info(sd, "Removing\n"); - v4l2_async_unregister_subdev(sd); + v4l2_device_unregister_subdev(sd); mutex_destroy(&priv->lock); media_entity_cleanup(&sd->entity); return 0; } - -static const struct platform_device_id imx_vdic_ids[] = { - { .name = "imx-ipuv3-vdic" }, - { }, -}; -MODULE_DEVICE_TABLE(platform, imx_vdic_ids); - -static struct platform_driver imx_vdic_driver = { - .probe = imx_vdic_probe, - .remove = imx_vdic_remove, - .id_table = imx_vdic_ids, - .driver = { - .name = "imx-ipuv3-vdic", - }, -}; -module_platform_driver(imx_vdic_driver); - -MODULE_DESCRIPTION("i.MX VDIC subdev driver"); -MODULE_AUTHOR("Steve Longerbeam <steve_longerbeam@mentor.com>"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:imx-ipuv3-vdic"); diff --git a/drivers/staging/media/imx/imx-media.h b/drivers/staging/media/imx/imx-media.h index 6587aa49e005..8a60bdafe2da 100644 --- a/drivers/staging/media/imx/imx-media.h +++ b/drivers/staging/media/imx/imx-media.h @@ -16,6 +16,19 @@ #include <video/imx-ipu-v3.h> /* + * Enumeration of the IPU internal sub-devices + */ +enum { + IPU_CSI0 = 0, + IPU_CSI1, + IPU_VDIC, + IPU_IC_PRP, + IPU_IC_PRPENC, + IPU_IC_PRPVF, + NUM_IPU_SUBDEVS, +}; + +/* * Pad definitions for the subdevs with multiple source or * sink pads */ @@ -111,25 +124,6 @@ struct imx_media_pad_vdev { struct list_head list; }; -struct imx_media_ipu_internal_sd_pdata { - char sd_name[V4L2_SUBDEV_NAME_SIZE]; - u32 grp_id; - int ipu_id; -}; - -struct imx_media_async_subdev { - /* the base asd - must be first in this struct */ - struct v4l2_async_subdev asd; - /* the platform device of IPU-internal subdevs */ - struct platform_device *pdev; -}; - -static inline struct imx_media_async_subdev * -to_imx_media_asd(struct v4l2_async_subdev *asd) -{ - return container_of(asd, struct imx_media_async_subdev, asd); -} - struct imx_media_dev { struct media_device md; struct v4l2_device v4l2_dev; @@ -142,11 +136,11 @@ struct imx_media_dev { /* master video device list */ struct list_head vdev_list; - /* IPUs this media driver control, valid after subdevs bound */ - struct ipu_soc *ipu[2]; - /* for async subdev registration */ struct v4l2_async_notifier notifier; + + /* the IPU internal subdev's registered synchronously */ + struct v4l2_subdev *sync_sd[2][NUM_IPU_SUBDEVS]; }; enum codespace_sel { @@ -176,8 +170,7 @@ void imx_media_fill_default_mbus_fields(struct v4l2_mbus_framefmt *tryfmt, struct v4l2_mbus_framefmt *fmt, bool ic_route); int imx_media_mbus_fmt_to_pix_fmt(struct v4l2_pix_format *pix, - struct v4l2_rect *compose, - const struct v4l2_mbus_framefmt *mbus, + struct v4l2_mbus_framefmt *mbus, const struct imx_media_pixfmt *cc); int imx_media_mbus_fmt_to_ipu_image(struct ipu_image *image, struct v4l2_mbus_framefmt *mbus); @@ -191,18 +184,18 @@ imx_media_find_subdev_by_fwnode(struct imx_media_dev *imxmd, struct v4l2_subdev * imx_media_find_subdev_by_devname(struct imx_media_dev *imxmd, const char *devname); -int imx_media_add_video_device(struct imx_media_dev *imxmd, - struct imx_media_video_dev *vdev); -int imx_media_find_mipi_csi2_channel(struct imx_media_dev *imxmd, - struct media_entity *start_entity); +void imx_media_add_video_device(struct imx_media_dev *imxmd, + struct imx_media_video_dev *vdev); +int imx_media_pipeline_csi2_channel(struct media_entity *start_entity); struct media_pad * -imx_media_find_upstream_pad(struct imx_media_dev *imxmd, - struct media_entity *start_entity, - u32 grp_id); +imx_media_pipeline_pad(struct media_entity *start_entity, u32 grp_id, + enum v4l2_buf_type buftype, bool upstream); struct v4l2_subdev * -imx_media_find_upstream_subdev(struct imx_media_dev *imxmd, - struct media_entity *start_entity, - u32 grp_id); +imx_media_pipeline_subdev(struct media_entity *start_entity, u32 grp_id, + bool upstream); +struct video_device * +imx_media_pipeline_video_device(struct media_entity *start_entity, + enum v4l2_buf_type buftype, bool upstream); struct imx_media_dma_buf { void *virt; @@ -210,9 +203,9 @@ struct imx_media_dma_buf { unsigned long len; }; -void imx_media_free_dma_buf(struct imx_media_dev *imxmd, +void imx_media_free_dma_buf(struct device *dev, struct imx_media_dma_buf *buf); -int imx_media_alloc_dma_buf(struct imx_media_dev *imxmd, +int imx_media_alloc_dma_buf(struct device *dev, struct imx_media_dma_buf *buf, int size); @@ -220,22 +213,12 @@ int imx_media_pipeline_set_stream(struct imx_media_dev *imxmd, struct media_entity *entity, bool on); -/* imx-media-dev.c */ -int imx_media_add_async_subdev(struct imx_media_dev *imxmd, - struct fwnode_handle *fwnode, - struct platform_device *pdev); - -int imx_media_subdev_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *sd, - struct v4l2_async_subdev *asd); -int imx_media_link_notify(struct media_link *link, u32 flags, - unsigned int notification); -void imx_media_notify(struct v4l2_subdev *sd, unsigned int notification, - void *arg); +/* imx-media-dev-common.c */ int imx_media_probe_complete(struct v4l2_async_notifier *notifier); - -struct imx_media_dev *imx_media_dev_init(struct device *dev); -int imx_media_dev_notifier_register(struct imx_media_dev *imxmd); +struct imx_media_dev *imx_media_dev_init(struct device *dev, + const struct media_device_ops *ops); +int imx_media_dev_notifier_register(struct imx_media_dev *imxmd, + const struct v4l2_async_notifier_operations *ops); /* imx-media-fim.c */ struct imx_media_fim; @@ -248,11 +231,9 @@ struct imx_media_fim *imx_media_fim_init(struct v4l2_subdev *sd); void imx_media_fim_free(struct imx_media_fim *fim); /* imx-media-internal-sd.c */ -int imx_media_add_ipu_internal_subdevs(struct imx_media_dev *imxmd, - int ipu_id); -int imx_media_create_ipu_internal_links(struct imx_media_dev *imxmd, - struct v4l2_subdev *sd); -void imx_media_remove_ipu_internal_subdevs(struct imx_media_dev *imxmd); +int imx_media_register_ipu_internal_subdevs(struct imx_media_dev *imxmd, + struct v4l2_subdev *csi); +void imx_media_unregister_ipu_internal_subdevs(struct imx_media_dev *imxmd); /* imx-media-of.c */ int imx_media_add_of_subdevs(struct imx_media_dev *dev, @@ -264,18 +245,29 @@ int imx_media_create_csi_of_links(struct imx_media_dev *imxmd, int imx_media_of_add_csi(struct imx_media_dev *imxmd, struct device_node *csi_np); +/* imx-media-vdic.c */ +struct v4l2_subdev *imx_media_vdic_register(struct v4l2_device *v4l2_dev, + struct device *ipu_dev, + struct ipu_soc *ipu, + u32 grp_id); +int imx_media_vdic_unregister(struct v4l2_subdev *sd); + +/* imx-ic-common.c */ +struct v4l2_subdev *imx_media_ic_register(struct v4l2_device *v4l2_dev, + struct device *ipu_dev, + struct ipu_soc *ipu, + u32 grp_id); +int imx_media_ic_unregister(struct v4l2_subdev *sd); + /* imx-media-capture.c */ struct imx_media_video_dev * -imx_media_capture_device_init(struct v4l2_subdev *src_sd, int pad); +imx_media_capture_device_init(struct device *dev, struct v4l2_subdev *src_sd, + int pad); void imx_media_capture_device_remove(struct imx_media_video_dev *vdev); -int imx_media_capture_device_register(struct imx_media_dev *md, - struct imx_media_video_dev *vdev); +int imx_media_capture_device_register(struct imx_media_video_dev *vdev); void imx_media_capture_device_unregister(struct imx_media_video_dev *vdev); struct imx_media_buffer * imx_media_capture_device_next_buf(struct imx_media_video_dev *vdev); -void imx_media_capture_device_set_format(struct imx_media_video_dev *vdev, - const struct v4l2_pix_format *pix, - const struct v4l2_rect *compose); void imx_media_capture_device_error(struct imx_media_video_dev *vdev); /* subdev group ids */ diff --git a/drivers/staging/media/imx/imx7-media-csi.c b/drivers/staging/media/imx/imx7-media-csi.c index a708a0340eb1..9101566f3f67 100644 --- a/drivers/staging/media/imx/imx7-media-csi.c +++ b/drivers/staging/media/imx/imx7-media-csi.c @@ -152,8 +152,6 @@ #define CSI_CSICR18 0x48 #define CSI_CSICR19 0x4c -static const char * const imx7_csi_clk_id[] = {"axi", "dcic", "mclk"}; - struct imx7_csi { struct device *dev; struct v4l2_subdev sd; @@ -180,9 +178,7 @@ struct imx7_csi { void __iomem *regbase; int irq; - - int num_clks; - struct clk_bulk_data *clks; + struct clk *mclk; /* active vb2 buffers to send to video dev sink */ struct imx_media_buffer *active_vb2_buf[2]; @@ -199,23 +195,15 @@ struct imx7_csi { struct completion last_eof_completion; }; -#define imx7_csi_reg_read(_csi, _offset) \ - __raw_readl((_csi)->regbase + (_offset)) -#define imx7_csi_reg_write(_csi, _val, _offset) \ - __raw_writel(_val, (_csi)->regbase + (_offset)) - -static void imx7_csi_clk_enable(struct imx7_csi *csi) +static u32 imx7_csi_reg_read(struct imx7_csi *csi, unsigned int offset) { - int ret; - - ret = clk_bulk_prepare_enable(csi->num_clks, csi->clks); - if (ret < 0) - dev_err(csi->dev, "failed to enable clocks\n"); + return readl(csi->regbase + offset); } -static void imx7_csi_clk_disable(struct imx7_csi *csi) +static void imx7_csi_reg_write(struct imx7_csi *csi, unsigned int value, + unsigned int offset) { - clk_bulk_disable_unprepare(csi->num_clks, csi->clks); + writel(value, csi->regbase + offset); } static void imx7_csi_hw_reset(struct imx7_csi *csi) @@ -229,9 +217,9 @@ static void imx7_csi_hw_reset(struct imx7_csi *csi) imx7_csi_reg_write(csi, CSICR3_RESET_VAL, CSI_CSICR3); } -static unsigned long imx7_csi_irq_clear(struct imx7_csi *csi) +static u32 imx7_csi_irq_clear(struct imx7_csi *csi) { - unsigned long isr; + u32 isr; isr = imx7_csi_reg_read(csi, CSI_CSISR); imx7_csi_reg_write(csi, isr, CSI_CSISR); @@ -257,7 +245,7 @@ static void imx7_csi_init_interface(struct imx7_csi *csi) static void imx7_csi_hw_enable_irq(struct imx7_csi *csi) { - unsigned long cr1 = imx7_csi_reg_read(csi, CSI_CSICR1); + u32 cr1 = imx7_csi_reg_read(csi, CSI_CSICR1); cr1 |= BIT_SOF_INTEN; cr1 |= BIT_RFF_OR_INT; @@ -273,7 +261,7 @@ static void imx7_csi_hw_enable_irq(struct imx7_csi *csi) static void imx7_csi_hw_disable_irq(struct imx7_csi *csi) { - unsigned long cr1 = imx7_csi_reg_read(csi, CSI_CSICR1); + u32 cr1 = imx7_csi_reg_read(csi, CSI_CSICR1); cr1 &= ~BIT_SOF_INTEN; cr1 &= ~BIT_RFF_OR_INT; @@ -286,7 +274,7 @@ static void imx7_csi_hw_disable_irq(struct imx7_csi *csi) static void imx7_csi_hw_enable(struct imx7_csi *csi) { - unsigned long cr = imx7_csi_reg_read(csi, CSI_CSICR18); + u32 cr = imx7_csi_reg_read(csi, CSI_CSICR18); cr |= BIT_CSI_HW_ENABLE; @@ -295,7 +283,7 @@ static void imx7_csi_hw_enable(struct imx7_csi *csi) static void imx7_csi_hw_disable(struct imx7_csi *csi) { - unsigned long cr = imx7_csi_reg_read(csi, CSI_CSICR18); + u32 cr = imx7_csi_reg_read(csi, CSI_CSICR18); cr &= ~BIT_CSI_HW_ENABLE; @@ -304,7 +292,7 @@ static void imx7_csi_hw_disable(struct imx7_csi *csi) static void imx7_csi_dma_reflash(struct imx7_csi *csi) { - unsigned long cr3 = imx7_csi_reg_read(csi, CSI_CSICR18); + u32 cr3 = imx7_csi_reg_read(csi, CSI_CSICR18); cr3 = imx7_csi_reg_read(csi, CSI_CSICR3); cr3 |= BIT_DMA_REFLASH_RFF; @@ -313,7 +301,7 @@ static void imx7_csi_dma_reflash(struct imx7_csi *csi) static void imx7_csi_rx_fifo_clear(struct imx7_csi *csi) { - unsigned long cr1; + u32 cr1; cr1 = imx7_csi_reg_read(csi, CSI_CSICR1); imx7_csi_reg_write(csi, cr1 & ~BIT_FCC, CSI_CSICR1); @@ -331,7 +319,7 @@ static void imx7_csi_buf_stride_set(struct imx7_csi *csi, u32 stride) static void imx7_csi_deinterlace_enable(struct imx7_csi *csi, bool enable) { - unsigned long cr18 = imx7_csi_reg_read(csi, CSI_CSICR18); + u32 cr18 = imx7_csi_reg_read(csi, CSI_CSICR18); if (enable) cr18 |= BIT_DEINTERLACE_EN; @@ -343,8 +331,8 @@ static void imx7_csi_deinterlace_enable(struct imx7_csi *csi, bool enable) static void imx7_csi_dmareq_rff_enable(struct imx7_csi *csi) { - unsigned long cr3 = imx7_csi_reg_read(csi, CSI_CSICR3); - unsigned long cr2 = imx7_csi_reg_read(csi, CSI_CSICR2); + u32 cr3 = imx7_csi_reg_read(csi, CSI_CSICR3); + u32 cr2 = imx7_csi_reg_read(csi, CSI_CSICR2); /* Burst Type of DMA Transfer from RxFIFO. INCR16 */ cr2 |= 0xC0000000; @@ -360,7 +348,7 @@ static void imx7_csi_dmareq_rff_enable(struct imx7_csi *csi) static void imx7_csi_dmareq_rff_disable(struct imx7_csi *csi) { - unsigned long cr3 = imx7_csi_reg_read(csi, CSI_CSICR3); + u32 cr3 = imx7_csi_reg_read(csi, CSI_CSICR3); cr3 &= ~BIT_DMA_REQ_EN_RFF; cr3 &= ~BIT_HRESP_ERR_EN; @@ -408,17 +396,23 @@ static void imx7_csi_error_recovery(struct imx7_csi *csi) imx7_csi_hw_enable(csi); } -static void imx7_csi_init(struct imx7_csi *csi) +static int imx7_csi_init(struct imx7_csi *csi) { + int ret; + if (csi->is_init) - return; + return 0; - imx7_csi_clk_enable(csi); + ret = clk_prepare_enable(csi->mclk); + if (ret < 0) + return ret; imx7_csi_hw_reset(csi); imx7_csi_init_interface(csi); imx7_csi_dmareq_rff_enable(csi); csi->is_init = true; + + return 0; } static void imx7_csi_deinit(struct imx7_csi *csi) @@ -429,7 +423,7 @@ static void imx7_csi_deinit(struct imx7_csi *csi) imx7_csi_hw_reset(csi); imx7_csi_init_interface(csi); imx7_csi_dmareq_rff_disable(csi); - imx7_csi_clk_disable(csi); + clk_disable_unprepare(csi->mclk); csi->is_init = false; } @@ -450,9 +444,9 @@ static int imx7_csi_get_upstream_endpoint(struct imx7_csi *csi, skip_video_mux: /* get source pad of entity directly upstream from src */ - pad = imx_media_find_upstream_pad(csi->imxmd, src, 0); - if (IS_ERR(pad)) - return PTR_ERR(pad); + pad = imx_media_pipeline_pad(src, 0, 0, true); + if (!pad) + return -ENODEV; sd = media_entity_to_v4l2_subdev(pad->entity); @@ -531,7 +525,7 @@ static int imx7_csi_link_setup(struct media_entity *entity, init: if (csi->sink || csi->src_sd) - imx7_csi_init(csi); + ret = imx7_csi_init(csi); else imx7_csi_deinit(csi); @@ -653,7 +647,7 @@ static void imx7_csi_vb2_buf_done(struct imx7_csi *csi) static irqreturn_t imx7_csi_irq_handler(int irq, void *data) { struct imx7_csi *csi = data; - unsigned long status; + u32 status; spin_lock(&csi->irqlock); @@ -714,7 +708,7 @@ static int imx7_csi_dma_start(struct imx7_csi *csi) struct v4l2_pix_format *out_pix = &vdev->fmt.fmt.pix; int ret; - ret = imx_media_alloc_dma_buf(csi->imxmd, &csi->underrun_buf, + ret = imx_media_alloc_dma_buf(csi->dev, &csi->underrun_buf, out_pix->sizeimage); if (ret < 0) { v4l2_warn(&csi->sd, "consider increasing the CMA area\n"); @@ -754,7 +748,7 @@ static void imx7_csi_dma_stop(struct imx7_csi *csi) imx7_csi_dma_unsetup_vb2_buf(csi, VB2_BUF_STATE_ERROR); - imx_media_free_dma_buf(csi->imxmd, &csi->underrun_buf); + imx_media_free_dma_buf(csi->dev, &csi->underrun_buf); } static int imx7_csi_configure(struct imx7_csi *csi) @@ -811,7 +805,7 @@ static int imx7_csi_configure(struct imx7_csi *csi) return 0; } -static int imx7_csi_enable(struct imx7_csi *csi) +static void imx7_csi_enable(struct imx7_csi *csi) { imx7_csi_sw_reset(csi); @@ -819,10 +813,7 @@ static int imx7_csi_enable(struct imx7_csi *csi) imx7_csi_dmareq_rff_enable(csi); imx7_csi_hw_enable_irq(csi); imx7_csi_hw_enable(csi); - return 0; } - - return 0; } static void imx7_csi_disable(struct imx7_csi *csi) @@ -1021,7 +1012,6 @@ static int imx7_csi_try_fmt(struct imx7_csi *csi, break; default: return -EINVAL; - break; } return 0; } @@ -1031,11 +1021,8 @@ static int imx7_csi_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_format *sdformat) { struct imx7_csi *csi = v4l2_get_subdevdata(sd); - struct imx_media_video_dev *vdev = csi->vdev; const struct imx_media_pixfmt *outcc; struct v4l2_mbus_framefmt *outfmt; - struct v4l2_pix_format vdev_fmt; - struct v4l2_rect vdev_compose; const struct imx_media_pixfmt *cc; struct v4l2_mbus_framefmt *fmt; struct v4l2_subdev_format format; @@ -1080,19 +1067,8 @@ static int imx7_csi_set_fmt(struct v4l2_subdev *sd, csi->cc[IMX7_CSI_PAD_SRC] = outcc; } - if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY) - goto out_unlock; - - csi->cc[sdformat->pad] = cc; - - /* propagate output pad format to capture device */ - imx_media_mbus_fmt_to_pix_fmt(&vdev_fmt, &vdev_compose, - &csi->format_mbus[IMX7_CSI_PAD_SRC], - csi->cc[IMX7_CSI_PAD_SRC]); - mutex_unlock(&csi->lock); - imx_media_capture_device_set_format(vdev, &vdev_fmt, &vdev_compose); - - return 0; + if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE) + csi->cc[sdformat->pad] = cc; out_unlock: mutex_unlock(&csi->lock); @@ -1126,17 +1102,7 @@ static int imx7_csi_registered(struct v4l2_subdev *sd) if (ret < 0) return ret; - ret = imx_media_capture_device_register(csi->imxmd, csi->vdev); - if (ret < 0) - return ret; - - ret = imx_media_add_video_device(csi->imxmd, csi->vdev); - if (ret < 0) { - imx_media_capture_device_unregister(csi->vdev); - return ret; - } - - return 0; + return imx_media_capture_device_register(csi->vdev); } static void imx7_csi_unregistered(struct v4l2_subdev *sd) @@ -1200,31 +1166,12 @@ static int imx7_csi_parse_endpoint(struct device *dev, return fwnode_device_is_available(asd->match.fwnode) ? 0 : -EINVAL; } -static int imx7_csi_clocks_get(struct imx7_csi *csi) -{ - struct device *dev = csi->dev; - int i; - - csi->num_clks = ARRAY_SIZE(imx7_csi_clk_id); - csi->clks = devm_kcalloc(dev, csi->num_clks, sizeof(*csi->clks), - GFP_KERNEL); - - if (!csi->clks) - return -ENOMEM; - - for (i = 0; i < csi->num_clks; i++) - csi->clks[i].id = imx7_csi_clk_id[i]; - - return devm_clk_bulk_get(dev, csi->num_clks, csi->clks); -} - static int imx7_csi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *node = dev->of_node; struct imx_media_dev *imxmd; struct imx7_csi *csi; - struct resource *res; int ret; csi = devm_kzalloc(&pdev->dev, sizeof(*csi), GFP_KERNEL); @@ -1233,24 +1180,22 @@ static int imx7_csi_probe(struct platform_device *pdev) csi->dev = dev; - ret = imx7_csi_clocks_get(csi); - if (ret < 0) { - dev_err(dev, "Failed to get clocks"); - return -ENODEV; + csi->mclk = devm_clk_get(&pdev->dev, "mclk"); + if (IS_ERR(csi->mclk)) { + ret = PTR_ERR(csi->mclk); + dev_err(dev, "Failed to get mclk: %d", ret); + return ret; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); csi->irq = platform_get_irq(pdev, 0); - if (!res || csi->irq < 0) { + if (csi->irq < 0) { dev_err(dev, "Missing platform resources data\n"); - return -ENODEV; + return csi->irq; } - csi->regbase = devm_ioremap_resource(dev, res); - if (IS_ERR(csi->regbase)) { - dev_err(dev, "Failed platform resources map\n"); - return -ENODEV; - } + csi->regbase = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(csi->regbase)) + return PTR_ERR(csi->regbase); spin_lock_init(&csi->irqlock); mutex_init(&csi->lock); @@ -1260,12 +1205,11 @@ static int imx7_csi_probe(struct platform_device *pdev) (void *)csi); if (ret < 0) { dev_err(dev, "Request CSI IRQ failed.\n"); - ret = -ENODEV; goto destroy_mutex; } /* add media device */ - imxmd = imx_media_dev_init(dev); + imxmd = imx_media_dev_init(dev, NULL); if (IS_ERR(imxmd)) { ret = PTR_ERR(imxmd); goto destroy_mutex; @@ -1276,7 +1220,7 @@ static int imx7_csi_probe(struct platform_device *pdev) if (ret < 0 && ret != -ENODEV && ret != -EEXIST) goto cleanup; - ret = imx_media_dev_notifier_register(imxmd); + ret = imx_media_dev_notifier_register(imxmd, NULL); if (ret < 0) goto cleanup; @@ -1292,7 +1236,8 @@ static int imx7_csi_probe(struct platform_device *pdev) csi->sd.grp_id = IMX_MEDIA_GRP_ID_CSI; snprintf(csi->sd.name, sizeof(csi->sd.name), "csi"); - csi->vdev = imx_media_capture_device_init(&csi->sd, IMX7_CSI_PAD_SRC); + csi->vdev = imx_media_capture_device_init(csi->sd.dev, &csi->sd, + IMX7_CSI_PAD_SRC); if (IS_ERR(csi->vdev)) return PTR_ERR(csi->vdev); diff --git a/drivers/staging/media/imx/imx7-mipi-csis.c b/drivers/staging/media/imx/imx7-mipi-csis.c index 19455f425416..d1cdf011c8f1 100644 --- a/drivers/staging/media/imx/imx7-mipi-csis.c +++ b/drivers/staging/media/imx/imx7-mipi-csis.c @@ -456,13 +456,9 @@ static void mipi_csis_set_params(struct csi_state *state) MIPI_CSIS_CMN_CTRL_UPDATE_SHADOW_CTRL); } -static void mipi_csis_clk_enable(struct csi_state *state) +static int mipi_csis_clk_enable(struct csi_state *state) { - int ret; - - ret = clk_bulk_prepare_enable(state->num_clks, state->clks); - if (ret < 0) - dev_err(state->dev, "failed to enable clocks\n"); + return clk_bulk_prepare_enable(state->num_clks, state->clks); } static void mipi_csis_clk_disable(struct csi_state *state) @@ -784,6 +780,17 @@ static irqreturn_t mipi_csis_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } +static int mipi_csis_registered(struct v4l2_subdev *mipi_sd) +{ + struct csi_state *state = mipi_sd_to_csis_state(mipi_sd); + + state->pads[CSIS_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + state->pads[CSIS_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + return media_entity_pads_init(&state->mipi_sd.entity, CSIS_PADS_NUM, + state->pads); +} + static const struct v4l2_subdev_core_ops mipi_csis_core_ops = { .log_status = mipi_csis_log_status, }; @@ -809,6 +816,10 @@ static const struct v4l2_subdev_ops mipi_csis_subdev_ops = { .pad = &mipi_csis_pad_ops, }; +static const struct v4l2_subdev_internal_ops mipi_csis_internal_ops = { + .registered = mipi_csis_registered, +}; + static int mipi_csis_parse_dt(struct platform_device *pdev, struct csi_state *state) { @@ -869,6 +880,7 @@ static int mipi_csis_subdev_init(struct v4l2_subdev *mipi_sd, mipi_sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; mipi_sd->entity.ops = &mipi_csis_entity_ops; + mipi_sd->internal_ops = &mipi_csis_internal_ops; mipi_sd->dev = &pdev->dev; @@ -890,7 +902,6 @@ static int mipi_csis_subdev_init(struct v4l2_subdev *mipi_sd, return ret; } - static int mipi_csis_dump_regs_show(struct seq_file *m, void *private) { struct csi_state *state = m->private; @@ -938,7 +949,7 @@ static int mipi_csis_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct resource *mem_res; struct csi_state *state; - int ret = -ENOMEM; + int ret; state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); if (!state) @@ -973,7 +984,11 @@ static int mipi_csis_probe(struct platform_device *pdev) if (ret < 0) return ret; - mipi_csis_clk_enable(state); + ret = mipi_csis_clk_enable(state); + if (ret < 0) { + dev_err(state->dev, "failed to enable clocks: %d\n", ret); + return ret; + } ret = devm_request_irq(dev, state->irq, mipi_csis_irq_handler, 0, dev_name(dev), state); @@ -990,13 +1005,6 @@ static int mipi_csis_probe(struct platform_device *pdev) if (ret < 0) goto disable_clock; - state->pads[CSIS_PAD_SINK].flags = MEDIA_PAD_FL_SINK; - state->pads[CSIS_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_pads_init(&state->mipi_sd.entity, CSIS_PADS_NUM, - state->pads); - if (ret < 0) - goto unregister_subdev; - memcpy(state->events, mipi_csis_events, sizeof(state->events)); mipi_csis_debugfs_init(state); @@ -1016,7 +1024,6 @@ static int mipi_csis_probe(struct platform_device *pdev) unregister_all: mipi_csis_debugfs_exit(state); media_entity_cleanup(&state->mipi_sd.entity); -unregister_subdev: v4l2_async_unregister_subdev(&state->mipi_sd); disable_clock: mipi_csis_clk_disable(state); diff --git a/drivers/staging/media/ipu3/include/intel-ipu3.h b/drivers/staging/media/ipu3/include/intel-ipu3.h index 1e7184e4311d..c7cd27efac8a 100644 --- a/drivers/staging/media/ipu3/include/intel-ipu3.h +++ b/drivers/staging/media/ipu3/include/intel-ipu3.h @@ -2472,7 +2472,7 @@ struct ipu3_uapi_acc_param { struct ipu3_uapi_yuvp1_yds_config yds2 __attribute__((aligned(32))); struct ipu3_uapi_yuvp2_tcc_static_config tcc __attribute__((aligned(32))); struct ipu3_uapi_anr_config anr; - struct ipu3_uapi_awb_fr_config_s awb_fr; + struct ipu3_uapi_awb_fr_config_s awb_fr __attribute__((aligned(32))); struct ipu3_uapi_ae_config ae; struct ipu3_uapi_af_config_s af; struct ipu3_uapi_awb_config awb; diff --git a/drivers/staging/media/ipu3/ipu3-css-fw.c b/drivers/staging/media/ipu3/ipu3-css-fw.c index 4122d4e42db6..45aff76198e2 100644 --- a/drivers/staging/media/ipu3/ipu3-css-fw.c +++ b/drivers/staging/media/ipu3/ipu3-css-fw.c @@ -200,13 +200,11 @@ int imgu_css_fw_init(struct imgu_css *css) goto bad_fw; for (j = 0; j < bi->info.isp.num_output_formats; j++) - if (bi->info.isp.output_formats[j] < 0 || - bi->info.isp.output_formats[j] >= + if (bi->info.isp.output_formats[j] >= IMGU_ABI_FRAME_FORMAT_NUM) goto bad_fw; for (j = 0; j < bi->info.isp.num_vf_formats; j++) - if (bi->info.isp.vf_formats[j] < 0 || - bi->info.isp.vf_formats[j] >= + if (bi->info.isp.vf_formats[j] >= IMGU_ABI_FRAME_FORMAT_NUM) goto bad_fw; diff --git a/drivers/staging/media/ipu3/ipu3-css.c b/drivers/staging/media/ipu3/ipu3-css.c index 23cf5b2cfe8b..fd1ed84c400c 100644 --- a/drivers/staging/media/ipu3/ipu3-css.c +++ b/drivers/staging/media/ipu3/ipu3-css.c @@ -24,9 +24,8 @@ #define IPU3_CSS_MAX_H 3136 #define IPU3_CSS_MAX_W 4224 -/* filter size from graph settings is fixed as 4 */ -#define FILTER_SIZE 4 -#define MIN_ENVELOPE 8 +/* minimal envelope size(GDC in - out) should be 4 */ +#define MIN_ENVELOPE 4 /* * pre-allocated buffer size for CSS ABI, auxiliary frames @@ -1827,9 +1826,9 @@ int imgu_css_fmt_try(struct imgu_css *css, vf->width = imgu_css_adjust(vf->width, VF_ALIGN_W); vf->height = imgu_css_adjust(vf->height, 1); - s = (bds->width - gdc->width) / 2 - FILTER_SIZE; + s = (bds->width - gdc->width) / 2; env->width = s < MIN_ENVELOPE ? MIN_ENVELOPE : s; - s = (bds->height - gdc->height) / 2 - FILTER_SIZE; + s = (bds->height - gdc->height) / 2; env->height = s < MIN_ENVELOPE ? MIN_ENVELOPE : s; ret = imgu_css_find_binary(css, pipe, q, r); @@ -2251,9 +2250,8 @@ int imgu_css_set_parameters(struct imgu_css *css, unsigned int pipe, css_pipe->aux_frames[a].height, css_pipe->rect[g].width, css_pipe->rect[g].height, - css_pipe->rect[e].width + FILTER_SIZE, - css_pipe->rect[e].height + - FILTER_SIZE); + css_pipe->rect[e].width, + css_pipe->rect[e].height); } } diff --git a/drivers/staging/media/ipu3/ipu3-dmamap.c b/drivers/staging/media/ipu3/ipu3-dmamap.c index d978a00e1e0b..7431322379f6 100644 --- a/drivers/staging/media/ipu3/ipu3-dmamap.c +++ b/drivers/staging/media/ipu3/ipu3-dmamap.c @@ -31,12 +31,11 @@ static void imgu_dmamap_free_buffer(struct page **pages, * Based on the implementation of __iommu_dma_alloc_pages() * defined in drivers/iommu/dma-iommu.c */ -static struct page **imgu_dmamap_alloc_buffer(size_t size, - unsigned long order_mask, - gfp_t gfp) +static struct page **imgu_dmamap_alloc_buffer(size_t size, gfp_t gfp) { struct page **pages; unsigned int i = 0, count = size >> PAGE_SHIFT; + unsigned int order_mask = 1; const gfp_t high_order_gfp = __GFP_NOWARN | __GFP_NORETRY; /* Allocate mem for array of page ptrs */ @@ -45,10 +44,6 @@ static struct page **imgu_dmamap_alloc_buffer(size_t size, if (!pages) return NULL; - order_mask &= (2U << MAX_ORDER) - 1; - if (!order_mask) - return NULL; - gfp |= __GFP_HIGHMEM | __GFP_ZERO; while (count) { @@ -99,7 +94,6 @@ void *imgu_dmamap_alloc(struct imgu_device *imgu, struct imgu_css_map *map, size_t len) { unsigned long shift = iova_shift(&imgu->iova_domain); - unsigned int alloc_sizes = imgu->mmu->pgsize_bitmap; struct device *dev = &imgu->pci_dev->dev; size_t size = PAGE_ALIGN(len); struct page **pages; @@ -114,8 +108,7 @@ void *imgu_dmamap_alloc(struct imgu_device *imgu, struct imgu_css_map *map, if (!iova) return NULL; - pages = imgu_dmamap_alloc_buffer(size, alloc_sizes >> PAGE_SHIFT, - GFP_KERNEL); + pages = imgu_dmamap_alloc_buffer(size, GFP_KERNEL); if (!pages) goto out_free_iova; @@ -257,7 +250,7 @@ int imgu_dmamap_init(struct imgu_device *imgu) if (ret) return ret; - order = __ffs(imgu->mmu->pgsize_bitmap); + order = __ffs(IPU3_PAGE_SIZE); base_pfn = max_t(unsigned long, 1, imgu->mmu->aperture_start >> order); init_iova_domain(&imgu->iova_domain, 1UL << order, base_pfn); diff --git a/drivers/staging/media/ipu3/ipu3-mmu.c b/drivers/staging/media/ipu3/ipu3-mmu.c index cfc2bdfb14b3..3d969b0522ab 100644 --- a/drivers/staging/media/ipu3/ipu3-mmu.c +++ b/drivers/staging/media/ipu3/ipu3-mmu.c @@ -20,9 +20,6 @@ #include "ipu3-mmu.h" -#define IPU3_PAGE_SHIFT 12 -#define IPU3_PAGE_SIZE (1UL << IPU3_PAGE_SHIFT) - #define IPU3_PT_BITS 10 #define IPU3_PT_PTES (1UL << IPU3_PT_BITS) #define IPU3_PT_SIZE (IPU3_PT_PTES << 2) @@ -238,62 +235,31 @@ static int __imgu_mmu_map(struct imgu_mmu *mmu, unsigned long iova, return 0; } -/* - * The following four functions are implemented based on iommu.c - * drivers/iommu/iommu.c/iommu_pgsize(). +/** + * imgu_mmu_map - map a buffer to a physical address + * + * @info: MMU mappable range + * @iova: the virtual address + * @paddr: the physical address + * @size: length of the mappable area + * + * The function has been adapted from iommu_map() in + * drivers/iommu/iommu.c . */ -static size_t imgu_mmu_pgsize(unsigned long pgsize_bitmap, - unsigned long addr_merge, size_t size) -{ - unsigned int pgsize_idx; - size_t pgsize; - - /* Max page size that still fits into 'size' */ - pgsize_idx = __fls(size); - - /* need to consider alignment requirements ? */ - if (likely(addr_merge)) { - /* Max page size allowed by address */ - unsigned int align_pgsize_idx = __ffs(addr_merge); - - pgsize_idx = min(pgsize_idx, align_pgsize_idx); - } - - /* build a mask of acceptable page sizes */ - pgsize = (1UL << (pgsize_idx + 1)) - 1; - - /* throw away page sizes not supported by the hardware */ - pgsize &= pgsize_bitmap; - - /* make sure we're still sane */ - WARN_ON(!pgsize); - - /* pick the biggest page */ - pgsize_idx = __fls(pgsize); - pgsize = 1UL << pgsize_idx; - - return pgsize; -} - -/* drivers/iommu/iommu.c/iommu_map() */ int imgu_mmu_map(struct imgu_mmu_info *info, unsigned long iova, phys_addr_t paddr, size_t size) { struct imgu_mmu *mmu = to_imgu_mmu(info); - unsigned int min_pagesz; int ret = 0; - /* find out the minimum page size supported */ - min_pagesz = 1 << __ffs(mmu->geometry.pgsize_bitmap); - /* * both the virtual address and the physical one, as well as * the size of the mapping, must be aligned (at least) to the * size of the smallest page supported by the hardware */ - if (!IS_ALIGNED(iova | paddr | size, min_pagesz)) { - dev_err(mmu->dev, "unaligned: iova 0x%lx pa %pa size 0x%zx min_pagesz 0x%x\n", - iova, &paddr, size, min_pagesz); + if (!IS_ALIGNED(iova | paddr | size, IPU3_PAGE_SIZE)) { + dev_err(mmu->dev, "unaligned: iova 0x%lx pa %pa size 0x%zx\n", + iova, &paddr, size); return -EINVAL; } @@ -301,19 +267,15 @@ int imgu_mmu_map(struct imgu_mmu_info *info, unsigned long iova, iova, &paddr, size); while (size) { - size_t pgsize = imgu_mmu_pgsize(mmu->geometry.pgsize_bitmap, - iova | paddr, size); - - dev_dbg(mmu->dev, "mapping: iova 0x%lx pa %pa pgsize 0x%zx\n", - iova, &paddr, pgsize); + dev_dbg(mmu->dev, "mapping: iova 0x%lx pa %pa\n", iova, &paddr); ret = __imgu_mmu_map(mmu, iova, paddr); if (ret) break; - iova += pgsize; - paddr += pgsize; - size -= pgsize; + iova += IPU3_PAGE_SIZE; + paddr += IPU3_PAGE_SIZE; + size -= IPU3_PAGE_SIZE; } call_if_imgu_is_powered(mmu, imgu_mmu_tlb_invalidate); @@ -321,28 +283,36 @@ int imgu_mmu_map(struct imgu_mmu_info *info, unsigned long iova, return ret; } -/* drivers/iommu/iommu.c/default_iommu_map_sg() */ +/** + * imgu_mmu_map_sg - Map a scatterlist + * + * @info: MMU mappable range + * @iova: the virtual address + * @sg: the scatterlist to map + * @nents: number of entries in the scatterlist + * + * The function has been adapted from default_iommu_map_sg() in + * drivers/iommu/iommu.c . + */ size_t imgu_mmu_map_sg(struct imgu_mmu_info *info, unsigned long iova, struct scatterlist *sg, unsigned int nents) { struct imgu_mmu *mmu = to_imgu_mmu(info); struct scatterlist *s; size_t s_length, mapped = 0; - unsigned int i, min_pagesz; + unsigned int i; int ret; - min_pagesz = 1 << __ffs(mmu->geometry.pgsize_bitmap); - for_each_sg(sg, s, nents, i) { phys_addr_t phys = page_to_phys(sg_page(s)) + s->offset; s_length = s->length; - if (!IS_ALIGNED(s->offset, min_pagesz)) + if (!IS_ALIGNED(s->offset, IPU3_PAGE_SIZE)) goto out_err; - /* must be min_pagesz aligned to be mapped singlely */ - if (i == nents - 1 && !IS_ALIGNED(s->length, min_pagesz)) + /* must be IPU3_PAGE_SIZE aligned to be mapped singlely */ + if (i == nents - 1 && !IS_ALIGNED(s->length, IPU3_PAGE_SIZE)) s_length = PAGE_ALIGN(s->length); ret = imgu_mmu_map(info, iova + mapped, phys, s_length); @@ -394,25 +364,30 @@ static size_t __imgu_mmu_unmap(struct imgu_mmu *mmu, return unmap; } -/* drivers/iommu/iommu.c/iommu_unmap() */ +/** + * imgu_mmu_unmap - Unmap a buffer + * + * @info: MMU mappable range + * @iova: the virtual address + * @size: the length of the buffer + * + * The function has been adapted from iommu_unmap() in + * drivers/iommu/iommu.c . + */ size_t imgu_mmu_unmap(struct imgu_mmu_info *info, unsigned long iova, size_t size) { struct imgu_mmu *mmu = to_imgu_mmu(info); size_t unmapped_page, unmapped = 0; - unsigned int min_pagesz; - - /* find out the minimum page size supported */ - min_pagesz = 1 << __ffs(mmu->geometry.pgsize_bitmap); /* * The virtual address, as well as the size of the mapping, must be * aligned (at least) to the size of the smallest page supported * by the hardware */ - if (!IS_ALIGNED(iova | size, min_pagesz)) { - dev_err(mmu->dev, "unaligned: iova 0x%lx size 0x%zx min_pagesz 0x%x\n", - iova, size, min_pagesz); + if (!IS_ALIGNED(iova | size, IPU3_PAGE_SIZE)) { + dev_err(mmu->dev, "unaligned: iova 0x%lx size 0x%zx\n", + iova, size); return -EINVAL; } @@ -423,10 +398,7 @@ size_t imgu_mmu_unmap(struct imgu_mmu_info *info, unsigned long iova, * or we hit an area that isn't mapped. */ while (unmapped < size) { - size_t pgsize = imgu_mmu_pgsize(mmu->geometry.pgsize_bitmap, - iova, size - unmapped); - - unmapped_page = __imgu_mmu_unmap(mmu, iova, pgsize); + unmapped_page = __imgu_mmu_unmap(mmu, iova, IPU3_PAGE_SIZE); if (!unmapped_page) break; @@ -444,6 +416,7 @@ size_t imgu_mmu_unmap(struct imgu_mmu_info *info, unsigned long iova, /** * imgu_mmu_init() - initialize IPU3 MMU block + * * @parent: struct device parent * @base: IOMEM base of hardware registers. * @@ -505,7 +478,6 @@ struct imgu_mmu_info *imgu_mmu_init(struct device *parent, void __iomem *base) mmu->geometry.aperture_start = 0; mmu->geometry.aperture_end = DMA_BIT_MASK(IPU3_MMU_ADDRESS_BITS); - mmu->geometry.pgsize_bitmap = IPU3_PAGE_SIZE; return &mmu->geometry; @@ -523,7 +495,8 @@ fail_group: /** * imgu_mmu_exit() - clean up IPU3 MMU block - * @info: IPU3 MMU private data + * + * @info: MMU mappable range */ void imgu_mmu_exit(struct imgu_mmu_info *info) { diff --git a/drivers/staging/media/ipu3/ipu3-mmu.h b/drivers/staging/media/ipu3/ipu3-mmu.h index fa58827eb19c..a5f0bca7e7e0 100644 --- a/drivers/staging/media/ipu3/ipu3-mmu.h +++ b/drivers/staging/media/ipu3/ipu3-mmu.h @@ -5,17 +5,18 @@ #ifndef __IPU3_MMU_H #define __IPU3_MMU_H +#define IPU3_PAGE_SHIFT 12 +#define IPU3_PAGE_SIZE (1UL << IPU3_PAGE_SHIFT) + /** * struct imgu_mmu_info - Describes mmu geometry * * @aperture_start: First address that can be mapped * @aperture_end: Last address that can be mapped - * @pgsize_bitmap: Bitmap of page sizes in use */ struct imgu_mmu_info { dma_addr_t aperture_start; dma_addr_t aperture_end; - unsigned long pgsize_bitmap; }; struct device; diff --git a/drivers/staging/media/ipu3/ipu3-v4l2.c b/drivers/staging/media/ipu3/ipu3-v4l2.c index a7bc22040ed8..3c7ad1eed434 100644 --- a/drivers/staging/media/ipu3/ipu3-v4l2.c +++ b/drivers/staging/media/ipu3/ipu3-v4l2.c @@ -955,12 +955,12 @@ static const struct v4l2_file_operations imgu_v4l2_fops = { static const struct v4l2_ioctl_ops imgu_v4l2_ioctl_ops = { .vidioc_querycap = imgu_vidioc_querycap, - .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap_mplane = imgu_vidioc_g_fmt, .vidioc_s_fmt_vid_cap_mplane = imgu_vidioc_s_fmt, .vidioc_try_fmt_vid_cap_mplane = imgu_vidioc_try_fmt, - .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out, + .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out, .vidioc_g_fmt_vid_out_mplane = imgu_vidioc_g_fmt, .vidioc_s_fmt_vid_out_mplane = imgu_vidioc_s_fmt, .vidioc_try_fmt_vid_out_mplane = imgu_vidioc_try_fmt, diff --git a/drivers/staging/media/meson/vdec/Kconfig b/drivers/staging/media/meson/vdec/Kconfig new file mode 100644 index 000000000000..9e1450193392 --- /dev/null +++ b/drivers/staging/media/meson/vdec/Kconfig @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0 + +config VIDEO_MESON_VDEC + tristate "Amlogic video decoder driver" + depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA + depends on ARCH_MESON || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + select MESON_CANVAS + help + Support for the video decoder found in gxbb/gxl/gxm chips. diff --git a/drivers/staging/media/meson/vdec/Makefile b/drivers/staging/media/meson/vdec/Makefile new file mode 100644 index 000000000000..6bea129084b7 --- /dev/null +++ b/drivers/staging/media/meson/vdec/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 +# Makefile for Amlogic meson video decoder driver + +meson-vdec-objs = esparser.o vdec.o vdec_helpers.o vdec_platform.o +meson-vdec-objs += vdec_1.o +meson-vdec-objs += codec_mpeg12.o + +obj-$(CONFIG_VIDEO_MESON_VDEC) += meson-vdec.o diff --git a/drivers/staging/media/meson/vdec/TODO b/drivers/staging/media/meson/vdec/TODO new file mode 100644 index 000000000000..70ae990cf13b --- /dev/null +++ b/drivers/staging/media/meson/vdec/TODO @@ -0,0 +1,8 @@ +This driver is in staging until the V4L2 documentation about stateful video +decoders is finalized, as well as the corresponding compliance tests. + +It is at the moment not guaranteed to work properly with a userspace +stack that follows the latest version of the specification, especially +with compression standards like MPEG1/2 where the driver does not support +dynamic resolution switching, including the first one used to determine coded +resolution. diff --git a/drivers/staging/media/meson/vdec/codec_mpeg12.c b/drivers/staging/media/meson/vdec/codec_mpeg12.c new file mode 100644 index 000000000000..5398fbf7ce20 --- /dev/null +++ b/drivers/staging/media/meson/vdec/codec_mpeg12.c @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018 BayLibre, SAS + * Author: Maxime Jourdan <mjourdan@baylibre.com> + */ + +#include <media/v4l2-mem2mem.h> +#include <media/videobuf2-dma-contig.h> + +#include "codec_mpeg12.h" +#include "dos_regs.h" +#include "vdec_helpers.h" + +#define SIZE_WORKSPACE SZ_128K +/* Offset substracted by the firmware from the workspace paddr */ +#define WORKSPACE_OFFSET (5 * SZ_1K) + +/* map firmware registers to known MPEG1/2 functions */ +#define MREG_SEQ_INFO AV_SCRATCH_4 + #define MPEG2_SEQ_DAR_MASK GENMASK(3, 0) + #define MPEG2_DAR_4_3 2 + #define MPEG2_DAR_16_9 3 + #define MPEG2_DAR_221_100 4 +#define MREG_PIC_INFO AV_SCRATCH_5 +#define MREG_PIC_WIDTH AV_SCRATCH_6 +#define MREG_PIC_HEIGHT AV_SCRATCH_7 +#define MREG_BUFFERIN AV_SCRATCH_8 +#define MREG_BUFFEROUT AV_SCRATCH_9 +#define MREG_CMD AV_SCRATCH_A +#define MREG_CO_MV_START AV_SCRATCH_B +#define MREG_ERROR_COUNT AV_SCRATCH_C +#define MREG_FRAME_OFFSET AV_SCRATCH_D +#define MREG_WAIT_BUFFER AV_SCRATCH_E +#define MREG_FATAL_ERROR AV_SCRATCH_F + +#define PICINFO_PROG 0x00008000 +#define PICINFO_TOP_FIRST 0x00002000 + +struct codec_mpeg12 { + /* Buffer for the MPEG1/2 Workspace */ + void *workspace_vaddr; + dma_addr_t workspace_paddr; +}; + +static const u8 eos_sequence[SZ_1K] = { 0x00, 0x00, 0x01, 0xB7 }; + +static const u8 *codec_mpeg12_eos_sequence(u32 *len) +{ + *len = ARRAY_SIZE(eos_sequence); + return eos_sequence; +} + +static int codec_mpeg12_can_recycle(struct amvdec_core *core) +{ + return !amvdec_read_dos(core, MREG_BUFFERIN); +} + +static void codec_mpeg12_recycle(struct amvdec_core *core, u32 buf_idx) +{ + amvdec_write_dos(core, MREG_BUFFERIN, buf_idx + 1); +} + +static int codec_mpeg12_start(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + struct codec_mpeg12 *mpeg12 = sess->priv; + int ret; + + mpeg12 = kzalloc(sizeof(*mpeg12), GFP_KERNEL); + if (!mpeg12) + return -ENOMEM; + + /* Allocate some memory for the MPEG1/2 decoder's state */ + mpeg12->workspace_vaddr = dma_alloc_coherent(core->dev, SIZE_WORKSPACE, + &mpeg12->workspace_paddr, + GFP_KERNEL); + if (!mpeg12->workspace_vaddr) { + dev_err(core->dev, "Failed to request MPEG 1/2 Workspace\n"); + ret = -ENOMEM; + goto free_mpeg12; + } + + ret = amvdec_set_canvases(sess, (u32[]){ AV_SCRATCH_0, 0 }, + (u32[]){ 8, 0 }); + if (ret) + goto free_workspace; + + amvdec_write_dos(core, POWER_CTL_VLD, BIT(4)); + amvdec_write_dos(core, MREG_CO_MV_START, + mpeg12->workspace_paddr + WORKSPACE_OFFSET); + + amvdec_write_dos(core, MPEG1_2_REG, 0); + amvdec_write_dos(core, PSCALE_CTRL, 0); + amvdec_write_dos(core, PIC_HEAD_INFO, 0x380); + amvdec_write_dos(core, M4_CONTROL_REG, 0); + amvdec_write_dos(core, MREG_BUFFERIN, 0); + amvdec_write_dos(core, MREG_BUFFEROUT, 0); + amvdec_write_dos(core, MREG_CMD, (sess->width << 16) | sess->height); + amvdec_write_dos(core, MREG_ERROR_COUNT, 0); + amvdec_write_dos(core, MREG_FATAL_ERROR, 0); + amvdec_write_dos(core, MREG_WAIT_BUFFER, 0); + + sess->keyframe_found = 1; + sess->priv = mpeg12; + + return 0; + +free_workspace: + dma_free_coherent(core->dev, SIZE_WORKSPACE, mpeg12->workspace_vaddr, + mpeg12->workspace_paddr); +free_mpeg12: + kfree(mpeg12); + + return ret; +} + +static int codec_mpeg12_stop(struct amvdec_session *sess) +{ + struct codec_mpeg12 *mpeg12 = sess->priv; + struct amvdec_core *core = sess->core; + + if (mpeg12->workspace_vaddr) + dma_free_coherent(core->dev, SIZE_WORKSPACE, + mpeg12->workspace_vaddr, + mpeg12->workspace_paddr); + + return 0; +} + +static void codec_mpeg12_update_dar(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + u32 seq = amvdec_read_dos(core, MREG_SEQ_INFO); + u32 ar = seq & MPEG2_SEQ_DAR_MASK; + + switch (ar) { + case MPEG2_DAR_4_3: + amvdec_set_par_from_dar(sess, 4, 3); + break; + case MPEG2_DAR_16_9: + amvdec_set_par_from_dar(sess, 16, 9); + break; + case MPEG2_DAR_221_100: + amvdec_set_par_from_dar(sess, 221, 100); + break; + default: + sess->pixelaspect.numerator = 1; + sess->pixelaspect.denominator = 1; + break; + } +} + +static irqreturn_t codec_mpeg12_threaded_isr(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + u32 reg; + u32 pic_info; + u32 is_progressive; + u32 buffer_index; + u32 field = V4L2_FIELD_NONE; + u32 offset; + + amvdec_write_dos(core, ASSIST_MBOX1_CLR_REG, 1); + reg = amvdec_read_dos(core, MREG_FATAL_ERROR); + if (reg == 1) { + dev_err(core->dev, "MPEG1/2 fatal error\n"); + amvdec_abort(sess); + return IRQ_HANDLED; + } + + reg = amvdec_read_dos(core, MREG_BUFFEROUT); + if (!reg) + return IRQ_HANDLED; + + /* Unclear what this means */ + if ((reg & GENMASK(23, 17)) == GENMASK(23, 17)) + goto end; + + pic_info = amvdec_read_dos(core, MREG_PIC_INFO); + is_progressive = pic_info & PICINFO_PROG; + + if (!is_progressive) + field = (pic_info & PICINFO_TOP_FIRST) ? + V4L2_FIELD_INTERLACED_TB : + V4L2_FIELD_INTERLACED_BT; + + codec_mpeg12_update_dar(sess); + buffer_index = ((reg & 0xf) - 1) & 7; + offset = amvdec_read_dos(core, MREG_FRAME_OFFSET); + amvdec_dst_buf_done_idx(sess, buffer_index, offset, field); + +end: + amvdec_write_dos(core, MREG_BUFFEROUT, 0); + return IRQ_HANDLED; +} + +static irqreturn_t codec_mpeg12_isr(struct amvdec_session *sess) +{ + return IRQ_WAKE_THREAD; +} + +struct amvdec_codec_ops codec_mpeg12_ops = { + .start = codec_mpeg12_start, + .stop = codec_mpeg12_stop, + .isr = codec_mpeg12_isr, + .threaded_isr = codec_mpeg12_threaded_isr, + .can_recycle = codec_mpeg12_can_recycle, + .recycle = codec_mpeg12_recycle, + .eos_sequence = codec_mpeg12_eos_sequence, +}; diff --git a/drivers/staging/media/meson/vdec/codec_mpeg12.h b/drivers/staging/media/meson/vdec/codec_mpeg12.h new file mode 100644 index 000000000000..43cab5f39ca0 --- /dev/null +++ b/drivers/staging/media/meson/vdec/codec_mpeg12.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2018 BayLibre, SAS + * Author: Maxime Jourdan <mjourdan@baylibre.com> + */ + +#ifndef __MESON_VDEC_CODEC_MPEG12_H_ +#define __MESON_VDEC_CODEC_MPEG12_H_ + +#include "vdec.h" + +extern struct amvdec_codec_ops codec_mpeg12_ops; + +#endif diff --git a/drivers/staging/media/meson/vdec/dos_regs.h b/drivers/staging/media/meson/vdec/dos_regs.h new file mode 100644 index 000000000000..abd810542dbb --- /dev/null +++ b/drivers/staging/media/meson/vdec/dos_regs.h @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2018 BayLibre, SAS + * Author: Maxime Jourdan <mjourdan@baylibre.com> + */ + +#ifndef __MESON_VDEC_DOS_REGS_H_ +#define __MESON_VDEC_DOS_REGS_H_ + +/* DOS registers */ +#define VDEC_ASSIST_AMR1_INT8 0x00b4 + +#define ASSIST_MBOX1_CLR_REG 0x01d4 +#define ASSIST_MBOX1_MASK 0x01d8 + +#define MPSR 0x0c04 +#define MCPU_INTR_MSK 0x0c10 +#define CPSR 0x0c84 + +#define IMEM_DMA_CTRL 0x0d00 +#define IMEM_DMA_ADR 0x0d04 +#define IMEM_DMA_COUNT 0x0d08 +#define LMEM_DMA_CTRL 0x0d40 + +#define MC_STATUS0 0x2424 +#define MC_CTRL1 0x242c + +#define PSCALE_RST 0x2440 +#define PSCALE_CTRL 0x2444 +#define PSCALE_BMEM_ADDR 0x247c +#define PSCALE_BMEM_DAT 0x2480 + +#define DBLK_CTRL 0x2544 +#define DBLK_STATUS 0x254c + +#define GCLK_EN 0x260c +#define MDEC_PIC_DC_CTRL 0x2638 +#define MDEC_PIC_DC_STATUS 0x263c +#define ANC0_CANVAS_ADDR 0x2640 +#define MDEC_PIC_DC_THRESH 0x26e0 + +/* Firmware interface registers */ +#define AV_SCRATCH_0 0x2700 +#define AV_SCRATCH_1 0x2704 +#define AV_SCRATCH_2 0x2708 +#define AV_SCRATCH_3 0x270c +#define AV_SCRATCH_4 0x2710 +#define AV_SCRATCH_5 0x2714 +#define AV_SCRATCH_6 0x2718 +#define AV_SCRATCH_7 0x271c +#define AV_SCRATCH_8 0x2720 +#define AV_SCRATCH_9 0x2724 +#define AV_SCRATCH_A 0x2728 +#define AV_SCRATCH_B 0x272c +#define AV_SCRATCH_C 0x2730 +#define AV_SCRATCH_D 0x2734 +#define AV_SCRATCH_E 0x2738 +#define AV_SCRATCH_F 0x273c +#define AV_SCRATCH_G 0x2740 +#define AV_SCRATCH_H 0x2744 +#define AV_SCRATCH_I 0x2748 +#define AV_SCRATCH_J 0x274c +#define AV_SCRATCH_K 0x2750 +#define AV_SCRATCH_L 0x2754 + +#define MPEG1_2_REG 0x3004 +#define PIC_HEAD_INFO 0x300c +#define POWER_CTL_VLD 0x3020 +#define M4_CONTROL_REG 0x30a4 + +/* Stream Buffer (stbuf) regs */ +#define VLD_MEM_VIFIFO_START_PTR 0x3100 +#define VLD_MEM_VIFIFO_CURR_PTR 0x3104 +#define VLD_MEM_VIFIFO_END_PTR 0x3108 +#define VLD_MEM_VIFIFO_CONTROL 0x3110 + #define MEM_FIFO_CNT_BIT 16 + #define MEM_FILL_ON_LEVEL BIT(10) + #define MEM_CTRL_EMPTY_EN BIT(2) + #define MEM_CTRL_FILL_EN BIT(1) +#define VLD_MEM_VIFIFO_WP 0x3114 +#define VLD_MEM_VIFIFO_RP 0x3118 +#define VLD_MEM_VIFIFO_LEVEL 0x311c +#define VLD_MEM_VIFIFO_BUF_CNTL 0x3120 + #define MEM_BUFCTRL_MANUAL BIT(1) +#define VLD_MEM_VIFIFO_WRAP_COUNT 0x3144 + +#define DCAC_DMA_CTRL 0x3848 + +#define DOS_SW_RESET0 0xfc00 +#define DOS_GCLK_EN0 0xfc04 +#define DOS_GEN_CTRL0 0xfc08 +#define DOS_MEM_PD_VDEC 0xfcc0 +#define DOS_MEM_PD_HEVC 0xfccc +#define DOS_SW_RESET3 0xfcd0 +#define DOS_GCLK_EN3 0xfcd4 +#define DOS_VDEC_MCRCC_STALL_CTRL 0xfd00 + +#endif diff --git a/drivers/staging/media/meson/vdec/esparser.c b/drivers/staging/media/meson/vdec/esparser.c new file mode 100644 index 000000000000..3a21a8cec799 --- /dev/null +++ b/drivers/staging/media/meson/vdec/esparser.c @@ -0,0 +1,324 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018 BayLibre, SAS + * Author: Maxime Jourdan <mjourdan@baylibre.com> + * + * The Elementary Stream Parser is a HW bitstream parser. + * It reads bitstream buffers and feeds them to the VIFIFO + */ + +#include <linux/init.h> +#include <linux/ioctl.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/reset.h> +#include <linux/interrupt.h> +#include <media/videobuf2-dma-contig.h> +#include <media/v4l2-mem2mem.h> + +#include "dos_regs.h" +#include "esparser.h" +#include "vdec_helpers.h" + +/* PARSER REGS (CBUS) */ +#define PARSER_CONTROL 0x00 + #define ES_PACK_SIZE_BIT 8 + #define ES_WRITE BIT(5) + #define ES_SEARCH BIT(1) + #define ES_PARSER_START BIT(0) +#define PARSER_FETCH_ADDR 0x4 +#define PARSER_FETCH_CMD 0x8 +#define PARSER_CONFIG 0x14 + #define PS_CFG_MAX_FETCH_CYCLE_BIT 0 + #define PS_CFG_STARTCODE_WID_24_BIT 10 + #define PS_CFG_MAX_ES_WR_CYCLE_BIT 12 + #define PS_CFG_PFIFO_EMPTY_CNT_BIT 16 +#define PFIFO_WR_PTR 0x18 +#define PFIFO_RD_PTR 0x1c +#define PARSER_SEARCH_PATTERN 0x24 + #define ES_START_CODE_PATTERN 0x00000100 +#define PARSER_SEARCH_MASK 0x28 + #define ES_START_CODE_MASK 0xffffff00 + #define FETCH_ENDIAN_BIT 27 +#define PARSER_INT_ENABLE 0x2c + #define PARSER_INT_HOST_EN_BIT 8 +#define PARSER_INT_STATUS 0x30 + #define PARSER_INTSTAT_SC_FOUND 1 +#define PARSER_ES_CONTROL 0x5c +#define PARSER_VIDEO_START_PTR 0x80 +#define PARSER_VIDEO_END_PTR 0x84 +#define PARSER_VIDEO_WP 0x88 +#define PARSER_VIDEO_HOLE 0x90 + +#define SEARCH_PATTERN_LEN 512 + +static DECLARE_WAIT_QUEUE_HEAD(wq); +static int search_done; + +static irqreturn_t esparser_isr(int irq, void *dev) +{ + int int_status; + struct amvdec_core *core = dev; + + int_status = amvdec_read_parser(core, PARSER_INT_STATUS); + amvdec_write_parser(core, PARSER_INT_STATUS, int_status); + + if (int_status & PARSER_INTSTAT_SC_FOUND) { + amvdec_write_parser(core, PFIFO_RD_PTR, 0); + amvdec_write_parser(core, PFIFO_WR_PTR, 0); + search_done = 1; + wake_up_interruptible(&wq); + } + + return IRQ_HANDLED; +} + +/* Pad the packet to at least 4KiB bytes otherwise the VDEC unit won't trigger + * ISRs. + * Also append a start code 000001ff at the end to trigger + * the ESPARSER interrupt. + */ +static u32 esparser_pad_start_code(struct vb2_buffer *vb) +{ + u32 payload_size = vb2_get_plane_payload(vb, 0); + u32 pad_size = 0; + u8 *vaddr = vb2_plane_vaddr(vb, 0) + payload_size; + + if (payload_size < ESPARSER_MIN_PACKET_SIZE) { + pad_size = ESPARSER_MIN_PACKET_SIZE - payload_size; + memset(vaddr, 0, pad_size); + } + + memset(vaddr + pad_size, 0, SEARCH_PATTERN_LEN); + vaddr[pad_size] = 0x00; + vaddr[pad_size + 1] = 0x00; + vaddr[pad_size + 2] = 0x01; + vaddr[pad_size + 3] = 0xff; + + return pad_size; +} + +static int +esparser_write_data(struct amvdec_core *core, dma_addr_t addr, u32 size) +{ + amvdec_write_parser(core, PFIFO_RD_PTR, 0); + amvdec_write_parser(core, PFIFO_WR_PTR, 0); + amvdec_write_parser(core, PARSER_CONTROL, + ES_WRITE | + ES_PARSER_START | + ES_SEARCH | + (size << ES_PACK_SIZE_BIT)); + + amvdec_write_parser(core, PARSER_FETCH_ADDR, addr); + amvdec_write_parser(core, PARSER_FETCH_CMD, + (7 << FETCH_ENDIAN_BIT) | + (size + SEARCH_PATTERN_LEN)); + + search_done = 0; + return wait_event_interruptible_timeout(wq, search_done, (HZ / 5)); +} + +static u32 esparser_vififo_get_free_space(struct amvdec_session *sess) +{ + u32 vififo_usage; + struct amvdec_ops *vdec_ops = sess->fmt_out->vdec_ops; + struct amvdec_core *core = sess->core; + + vififo_usage = vdec_ops->vififo_level(sess); + vififo_usage += amvdec_read_parser(core, PARSER_VIDEO_HOLE); + vififo_usage += (6 * SZ_1K); // 6 KiB internal fifo + + if (vififo_usage > sess->vififo_size) { + dev_warn(sess->core->dev, + "VIFIFO usage (%u) > VIFIFO size (%u)\n", + vififo_usage, sess->vififo_size); + return 0; + } + + return sess->vififo_size - vififo_usage; +} + +int esparser_queue_eos(struct amvdec_core *core, const u8 *data, u32 len) +{ + struct device *dev = core->dev; + void *eos_vaddr; + dma_addr_t eos_paddr; + int ret; + + eos_vaddr = dma_alloc_coherent(dev, len + SEARCH_PATTERN_LEN, + &eos_paddr, GFP_KERNEL); + if (!eos_vaddr) + return -ENOMEM; + + memcpy(eos_vaddr, data, len); + ret = esparser_write_data(core, eos_paddr, len); + dma_free_coherent(dev, len + SEARCH_PATTERN_LEN, + eos_vaddr, eos_paddr); + + return ret; +} + +static u32 esparser_get_offset(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + u32 offset = amvdec_read_parser(core, PARSER_VIDEO_WP) - + sess->vififo_paddr; + + if (offset < sess->last_offset) + sess->wrap_count++; + + sess->last_offset = offset; + offset += (sess->wrap_count * sess->vififo_size); + + return offset; +} + +static int +esparser_queue(struct amvdec_session *sess, struct vb2_v4l2_buffer *vbuf) +{ + int ret; + struct vb2_buffer *vb = &vbuf->vb2_buf; + struct amvdec_core *core = sess->core; + struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops; + u32 num_dst_bufs = 0; + u32 payload_size = vb2_get_plane_payload(vb, 0); + dma_addr_t phy = vb2_dma_contig_plane_dma_addr(vb, 0); + u32 offset; + u32 pad_size; + + if (codec_ops->num_pending_bufs) + num_dst_bufs = codec_ops->num_pending_bufs(sess); + + num_dst_bufs += v4l2_m2m_num_dst_bufs_ready(sess->m2m_ctx); + + if (esparser_vififo_get_free_space(sess) < payload_size || + atomic_read(&sess->esparser_queued_bufs) >= num_dst_bufs) + return -EAGAIN; + + v4l2_m2m_src_buf_remove_by_buf(sess->m2m_ctx, vbuf); + + offset = esparser_get_offset(sess); + + amvdec_add_ts_reorder(sess, vb->timestamp, offset); + dev_dbg(core->dev, "esparser: ts = %llu pld_size = %u offset = %08X\n", + vb->timestamp, payload_size, offset); + + pad_size = esparser_pad_start_code(vb); + ret = esparser_write_data(core, phy, payload_size + pad_size); + + if (ret <= 0) { + dev_warn(core->dev, "esparser: input parsing error\n"); + amvdec_remove_ts(sess, vb->timestamp); + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); + amvdec_write_parser(core, PARSER_FETCH_CMD, 0); + + return 0; + } + + /* We need to wait until we parse the first keyframe. + * All buffers prior to the first keyframe must be dropped. + */ + if (!sess->keyframe_found) + usleep_range(1000, 2000); + + if (sess->keyframe_found) + atomic_inc(&sess->esparser_queued_bufs); + else + amvdec_remove_ts(sess, vb->timestamp); + + vbuf->flags = 0; + vbuf->field = V4L2_FIELD_NONE; + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE); + + return 0; +} + +void esparser_queue_all_src(struct work_struct *work) +{ + struct v4l2_m2m_buffer *buf, *n; + struct amvdec_session *sess = + container_of(work, struct amvdec_session, esparser_queue_work); + + mutex_lock(&sess->lock); + v4l2_m2m_for_each_src_buf_safe(sess->m2m_ctx, buf, n) { + if (sess->should_stop) + break; + + if (esparser_queue(sess, &buf->vb) < 0) + break; + } + mutex_unlock(&sess->lock); +} + +int esparser_power_up(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + struct amvdec_ops *vdec_ops = sess->fmt_out->vdec_ops; + + reset_control_reset(core->esparser_reset); + amvdec_write_parser(core, PARSER_CONFIG, + (10 << PS_CFG_PFIFO_EMPTY_CNT_BIT) | + (1 << PS_CFG_MAX_ES_WR_CYCLE_BIT) | + (16 << PS_CFG_MAX_FETCH_CYCLE_BIT)); + + amvdec_write_parser(core, PFIFO_RD_PTR, 0); + amvdec_write_parser(core, PFIFO_WR_PTR, 0); + + amvdec_write_parser(core, PARSER_SEARCH_PATTERN, + ES_START_CODE_PATTERN); + amvdec_write_parser(core, PARSER_SEARCH_MASK, ES_START_CODE_MASK); + + amvdec_write_parser(core, PARSER_CONFIG, + (10 << PS_CFG_PFIFO_EMPTY_CNT_BIT) | + (1 << PS_CFG_MAX_ES_WR_CYCLE_BIT) | + (16 << PS_CFG_MAX_FETCH_CYCLE_BIT) | + (2 << PS_CFG_STARTCODE_WID_24_BIT)); + + amvdec_write_parser(core, PARSER_CONTROL, + (ES_SEARCH | ES_PARSER_START)); + + amvdec_write_parser(core, PARSER_VIDEO_START_PTR, sess->vififo_paddr); + amvdec_write_parser(core, PARSER_VIDEO_END_PTR, + sess->vififo_paddr + sess->vififo_size - 8); + amvdec_write_parser(core, PARSER_ES_CONTROL, + amvdec_read_parser(core, PARSER_ES_CONTROL) & ~1); + + if (vdec_ops->conf_esparser) + vdec_ops->conf_esparser(sess); + + amvdec_write_parser(core, PARSER_INT_STATUS, 0xffff); + amvdec_write_parser(core, PARSER_INT_ENABLE, + BIT(PARSER_INT_HOST_EN_BIT)); + + return 0; +} + +int esparser_init(struct platform_device *pdev, struct amvdec_core *core) +{ + struct device *dev = &pdev->dev; + int ret; + int irq; + + irq = platform_get_irq_byname(pdev, "esparser"); + if (irq < 0) { + dev_err(dev, "Failed getting ESPARSER IRQ from dtb\n"); + return irq; + } + + ret = devm_request_irq(dev, irq, esparser_isr, IRQF_SHARED, + "esparserirq", core); + if (ret) { + dev_err(dev, "Failed requesting ESPARSER IRQ\n"); + return ret; + } + + core->esparser_reset = + devm_reset_control_get_exclusive(dev, "esparser"); + if (IS_ERR(core->esparser_reset)) { + dev_err(dev, "Failed to get esparser_reset\n"); + return PTR_ERR(core->esparser_reset); + } + + return 0; +} diff --git a/drivers/staging/media/meson/vdec/esparser.h b/drivers/staging/media/meson/vdec/esparser.h new file mode 100644 index 000000000000..ff51fe7fda66 --- /dev/null +++ b/drivers/staging/media/meson/vdec/esparser.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2018 BayLibre, SAS + * Author: Maxime Jourdan <mjourdan@baylibre.com> + */ + +#ifndef __MESON_VDEC_ESPARSER_H_ +#define __MESON_VDEC_ESPARSER_H_ + +#include <linux/platform_device.h> + +#include "vdec.h" + +int esparser_init(struct platform_device *pdev, struct amvdec_core *core); +int esparser_power_up(struct amvdec_session *sess); + +/** + * esparser_queue_eos() - write End Of Stream sequence to the ESPARSER + * + * @core vdec core struct + */ +int esparser_queue_eos(struct amvdec_core *core, const u8 *data, u32 len); + +/** + * esparser_queue_all_src() - work handler that writes as many src buffers + * as possible to the ESPARSER + */ +void esparser_queue_all_src(struct work_struct *work); + +#define ESPARSER_MIN_PACKET_SIZE SZ_4K + +#endif diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c new file mode 100644 index 000000000000..4e4f9d614e41 --- /dev/null +++ b/drivers/staging/media/meson/vdec/vdec.c @@ -0,0 +1,1098 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018 BayLibre, SAS + * Author: Maxime Jourdan <mjourdan@baylibre.com> + */ + +#include <linux/of_device.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/mfd/syscon.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-event.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-mem2mem.h> +#include <media/v4l2-dev.h> +#include <media/videobuf2-dma-contig.h> + +#include "vdec.h" +#include "esparser.h" +#include "vdec_helpers.h" + +struct dummy_buf { + struct vb2_v4l2_buffer vb; + struct list_head list; +}; + +/* 16 MiB for parsed bitstream swap exchange */ +#define SIZE_VIFIFO SZ_16M + +static u32 get_output_size(u32 width, u32 height) +{ + return ALIGN(width * height, SZ_64K); +} + +u32 amvdec_get_output_size(struct amvdec_session *sess) +{ + return get_output_size(sess->width, sess->height); +} +EXPORT_SYMBOL_GPL(amvdec_get_output_size); + +static int vdec_codec_needs_recycle(struct amvdec_session *sess) +{ + struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops; + + return codec_ops->can_recycle && codec_ops->recycle; +} + +static int vdec_recycle_thread(void *data) +{ + struct amvdec_session *sess = data; + struct amvdec_core *core = sess->core; + struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops; + struct amvdec_buffer *tmp, *n; + + while (!kthread_should_stop()) { + mutex_lock(&sess->bufs_recycle_lock); + list_for_each_entry_safe(tmp, n, &sess->bufs_recycle, list) { + if (!codec_ops->can_recycle(core)) + break; + + codec_ops->recycle(core, tmp->vb->index); + list_del(&tmp->list); + kfree(tmp); + } + mutex_unlock(&sess->bufs_recycle_lock); + + usleep_range(5000, 10000); + } + + return 0; +} + +static int vdec_poweron(struct amvdec_session *sess) +{ + int ret; + struct amvdec_ops *vdec_ops = sess->fmt_out->vdec_ops; + + ret = clk_prepare_enable(sess->core->dos_parser_clk); + if (ret) + return ret; + + ret = clk_prepare_enable(sess->core->dos_clk); + if (ret) + goto disable_dos_parser; + + ret = vdec_ops->start(sess); + if (ret) + goto disable_dos; + + esparser_power_up(sess); + + return 0; + +disable_dos: + clk_disable_unprepare(sess->core->dos_clk); +disable_dos_parser: + clk_disable_unprepare(sess->core->dos_parser_clk); + + return ret; +} + +static void vdec_wait_inactive(struct amvdec_session *sess) +{ + /* We consider 50ms with no IRQ to be inactive. */ + while (time_is_after_jiffies64(sess->last_irq_jiffies + + msecs_to_jiffies(50))) + msleep(25); +} + +static void vdec_poweroff(struct amvdec_session *sess) +{ + struct amvdec_ops *vdec_ops = sess->fmt_out->vdec_ops; + struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops; + + sess->should_stop = 1; + vdec_wait_inactive(sess); + if (codec_ops->drain) + codec_ops->drain(sess); + + vdec_ops->stop(sess); + clk_disable_unprepare(sess->core->dos_clk); + clk_disable_unprepare(sess->core->dos_parser_clk); +} + +static void +vdec_queue_recycle(struct amvdec_session *sess, struct vb2_buffer *vb) +{ + struct amvdec_buffer *new_buf; + + new_buf = kmalloc(sizeof(*new_buf), GFP_KERNEL); + new_buf->vb = vb; + + mutex_lock(&sess->bufs_recycle_lock); + list_add_tail(&new_buf->list, &sess->bufs_recycle); + mutex_unlock(&sess->bufs_recycle_lock); +} + +static void vdec_m2m_device_run(void *priv) +{ + struct amvdec_session *sess = priv; + + schedule_work(&sess->esparser_queue_work); +} + +static void vdec_m2m_job_abort(void *priv) +{ + struct amvdec_session *sess = priv; + + v4l2_m2m_job_finish(sess->m2m_dev, sess->m2m_ctx); +} + +static const struct v4l2_m2m_ops vdec_m2m_ops = { + .device_run = vdec_m2m_device_run, + .job_abort = vdec_m2m_job_abort, +}; + +static void process_num_buffers(struct vb2_queue *q, + struct amvdec_session *sess, + unsigned int *num_buffers, + bool is_reqbufs) +{ + const struct amvdec_format *fmt_out = sess->fmt_out; + unsigned int buffers_total = q->num_buffers + *num_buffers; + + if (is_reqbufs && buffers_total < fmt_out->min_buffers) + *num_buffers = fmt_out->min_buffers - q->num_buffers; + if (buffers_total > fmt_out->max_buffers) + *num_buffers = fmt_out->max_buffers - q->num_buffers; + + /* We need to program the complete CAPTURE buffer list + * in registers during start_streaming, and the firmwares + * are free to choose any of them to write frames to. As such, + * we need all of them to be queued into the driver + */ + sess->num_dst_bufs = q->num_buffers + *num_buffers; + q->min_buffers_needed = max(fmt_out->min_buffers, sess->num_dst_bufs); +} + +static int vdec_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, + unsigned int *num_planes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct amvdec_session *sess = vb2_get_drv_priv(q); + u32 output_size = amvdec_get_output_size(sess); + + if (*num_planes) { + switch (q->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + if (*num_planes != 1 || sizes[0] < output_size) + return -EINVAL; + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + switch (sess->pixfmt_cap) { + case V4L2_PIX_FMT_NV12M: + if (*num_planes != 2 || + sizes[0] < output_size || + sizes[1] < output_size / 2) + return -EINVAL; + break; + case V4L2_PIX_FMT_YUV420M: + if (*num_planes != 3 || + sizes[0] < output_size || + sizes[1] < output_size / 4 || + sizes[2] < output_size / 4) + return -EINVAL; + break; + default: + return -EINVAL; + } + + process_num_buffers(q, sess, num_buffers, false); + break; + } + + return 0; + } + + switch (q->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + sizes[0] = amvdec_get_output_size(sess); + *num_planes = 1; + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + switch (sess->pixfmt_cap) { + case V4L2_PIX_FMT_NV12M: + sizes[0] = output_size; + sizes[1] = output_size / 2; + *num_planes = 2; + break; + case V4L2_PIX_FMT_YUV420M: + sizes[0] = output_size; + sizes[1] = output_size / 4; + sizes[2] = output_size / 4; + *num_planes = 3; + break; + default: + return -EINVAL; + } + + process_num_buffers(q, sess, num_buffers, true); + break; + default: + return -EINVAL; + } + + return 0; +} + +static void vdec_vb2_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct amvdec_session *sess = vb2_get_drv_priv(vb->vb2_queue); + struct v4l2_m2m_ctx *m2m_ctx = sess->m2m_ctx; + + v4l2_m2m_buf_queue(m2m_ctx, vbuf); + + if (!sess->streamon_out || !sess->streamon_cap) + return; + + if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + vdec_codec_needs_recycle(sess)) + vdec_queue_recycle(sess, vb); + + schedule_work(&sess->esparser_queue_work); +} + +static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct amvdec_session *sess = vb2_get_drv_priv(q); + struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops; + struct amvdec_core *core = sess->core; + struct vb2_v4l2_buffer *buf; + int ret; + + if (core->cur_sess && core->cur_sess != sess) { + ret = -EBUSY; + goto bufs_done; + } + + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + sess->streamon_out = 1; + else + sess->streamon_cap = 1; + + if (!sess->streamon_out || !sess->streamon_cap) + return 0; + + if (sess->status == STATUS_NEEDS_RESUME && + q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + codec_ops->resume(sess); + sess->status = STATUS_RUNNING; + return 0; + } + + sess->vififo_size = SIZE_VIFIFO; + sess->vififo_vaddr = + dma_alloc_coherent(sess->core->dev, sess->vififo_size, + &sess->vififo_paddr, GFP_KERNEL); + if (!sess->vififo_vaddr) { + dev_err(sess->core->dev, "Failed to request VIFIFO buffer\n"); + ret = -ENOMEM; + goto bufs_done; + } + + sess->should_stop = 0; + sess->keyframe_found = 0; + sess->last_offset = 0; + sess->wrap_count = 0; + sess->pixelaspect.numerator = 1; + sess->pixelaspect.denominator = 1; + atomic_set(&sess->esparser_queued_bufs, 0); + v4l2_ctrl_s_ctrl(sess->ctrl_min_buf_capture, 1); + + ret = vdec_poweron(sess); + if (ret) + goto vififo_free; + + sess->sequence_cap = 0; + if (vdec_codec_needs_recycle(sess)) + sess->recycle_thread = kthread_run(vdec_recycle_thread, sess, + "vdec_recycle"); + + sess->status = STATUS_RUNNING; + core->cur_sess = sess; + + return 0; + +vififo_free: + dma_free_coherent(sess->core->dev, sess->vififo_size, + sess->vififo_vaddr, sess->vififo_paddr); +bufs_done: + while ((buf = v4l2_m2m_src_buf_remove(sess->m2m_ctx))) + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED); + while ((buf = v4l2_m2m_dst_buf_remove(sess->m2m_ctx))) + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED); + + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + sess->streamon_out = 0; + else + sess->streamon_cap = 0; + + return ret; +} + +static void vdec_free_canvas(struct amvdec_session *sess) +{ + int i; + + for (i = 0; i < sess->canvas_num; ++i) + meson_canvas_free(sess->core->canvas, sess->canvas_alloc[i]); + + sess->canvas_num = 0; +} + +static void vdec_reset_timestamps(struct amvdec_session *sess) +{ + struct amvdec_timestamp *tmp, *n; + + list_for_each_entry_safe(tmp, n, &sess->timestamps, list) { + list_del(&tmp->list); + kfree(tmp); + } +} + +static void vdec_reset_bufs_recycle(struct amvdec_session *sess) +{ + struct amvdec_buffer *tmp, *n; + + list_for_each_entry_safe(tmp, n, &sess->bufs_recycle, list) { + list_del(&tmp->list); + kfree(tmp); + } +} + +static void vdec_stop_streaming(struct vb2_queue *q) +{ + struct amvdec_session *sess = vb2_get_drv_priv(q); + struct amvdec_core *core = sess->core; + struct vb2_v4l2_buffer *buf; + + if (sess->status == STATUS_RUNNING || + (sess->status == STATUS_NEEDS_RESUME && + (!sess->streamon_out || !sess->streamon_cap))) { + if (vdec_codec_needs_recycle(sess)) + kthread_stop(sess->recycle_thread); + + vdec_poweroff(sess); + vdec_free_canvas(sess); + dma_free_coherent(sess->core->dev, sess->vififo_size, + sess->vififo_vaddr, sess->vififo_paddr); + vdec_reset_timestamps(sess); + vdec_reset_bufs_recycle(sess); + kfree(sess->priv); + sess->priv = NULL; + core->cur_sess = NULL; + sess->status = STATUS_STOPPED; + } + + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + while ((buf = v4l2_m2m_src_buf_remove(sess->m2m_ctx))) + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR); + + sess->streamon_out = 0; + } else { + while ((buf = v4l2_m2m_dst_buf_remove(sess->m2m_ctx))) + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR); + + sess->streamon_cap = 0; + } +} + +static int vdec_vb2_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + vbuf->field = V4L2_FIELD_NONE; + return 0; +} + +static const struct vb2_ops vdec_vb2_ops = { + .queue_setup = vdec_queue_setup, + .start_streaming = vdec_start_streaming, + .stop_streaming = vdec_stop_streaming, + .buf_queue = vdec_vb2_buf_queue, + .buf_prepare = vdec_vb2_buf_prepare, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int +vdec_querycap(struct file *file, void *fh, struct v4l2_capability *cap) +{ + strscpy(cap->driver, "meson-vdec", sizeof(cap->driver)); + strscpy(cap->card, "Amlogic Video Decoder", sizeof(cap->card)); + strscpy(cap->bus_info, "platform:meson-vdec", sizeof(cap->bus_info)); + + return 0; +} + +static const struct amvdec_format * +find_format(const struct amvdec_format *fmts, u32 size, u32 pixfmt) +{ + unsigned int i; + + for (i = 0; i < size; i++) { + if (fmts[i].pixfmt == pixfmt) + return &fmts[i]; + } + + return NULL; +} + +static unsigned int +vdec_supports_pixfmt_cap(const struct amvdec_format *fmt_out, u32 pixfmt_cap) +{ + int i; + + for (i = 0; fmt_out->pixfmts_cap[i]; i++) + if (fmt_out->pixfmts_cap[i] == pixfmt_cap) + return 1; + + return 0; +} + +static const struct amvdec_format * +vdec_try_fmt_common(struct amvdec_session *sess, u32 size, + struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; + struct v4l2_plane_pix_format *pfmt = pixmp->plane_fmt; + const struct amvdec_format *fmts = sess->core->platform->formats; + const struct amvdec_format *fmt_out; + + memset(pfmt[0].reserved, 0, sizeof(pfmt[0].reserved)); + memset(pixmp->reserved, 0, sizeof(pixmp->reserved)); + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + fmt_out = find_format(fmts, size, pixmp->pixelformat); + if (!fmt_out) { + pixmp->pixelformat = V4L2_PIX_FMT_MPEG2; + fmt_out = find_format(fmts, size, pixmp->pixelformat); + } + + pfmt[0].sizeimage = + get_output_size(pixmp->width, pixmp->height); + pfmt[0].bytesperline = 0; + pixmp->num_planes = 1; + } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + fmt_out = sess->fmt_out; + if (!vdec_supports_pixfmt_cap(fmt_out, pixmp->pixelformat)) + pixmp->pixelformat = fmt_out->pixfmts_cap[0]; + + memset(pfmt[1].reserved, 0, sizeof(pfmt[1].reserved)); + if (pixmp->pixelformat == V4L2_PIX_FMT_NV12M) { + pfmt[0].sizeimage = + get_output_size(pixmp->width, pixmp->height); + pfmt[0].bytesperline = ALIGN(pixmp->width, 64); + + pfmt[1].sizeimage = + get_output_size(pixmp->width, pixmp->height) / 2; + pfmt[1].bytesperline = ALIGN(pixmp->width, 64); + pixmp->num_planes = 2; + } else if (pixmp->pixelformat == V4L2_PIX_FMT_YUV420M) { + pfmt[0].sizeimage = + get_output_size(pixmp->width, pixmp->height); + pfmt[0].bytesperline = ALIGN(pixmp->width, 64); + + pfmt[1].sizeimage = + get_output_size(pixmp->width, pixmp->height) / 4; + pfmt[1].bytesperline = ALIGN(pixmp->width, 64) / 2; + + pfmt[2].sizeimage = + get_output_size(pixmp->width, pixmp->height) / 4; + pfmt[2].bytesperline = ALIGN(pixmp->width, 64) / 2; + pixmp->num_planes = 3; + } + } else { + return NULL; + } + + pixmp->width = clamp(pixmp->width, (u32)256, fmt_out->max_width); + pixmp->height = clamp(pixmp->height, (u32)144, fmt_out->max_height); + + if (pixmp->field == V4L2_FIELD_ANY) + pixmp->field = V4L2_FIELD_NONE; + + return fmt_out; +} + +static int vdec_try_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct amvdec_session *sess = + container_of(file->private_data, struct amvdec_session, fh); + + vdec_try_fmt_common(sess, sess->core->platform->num_formats, f); + + return 0; +} + +static int vdec_g_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct amvdec_session *sess = + container_of(file->private_data, struct amvdec_session, fh); + struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; + + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + pixmp->pixelformat = sess->pixfmt_cap; + else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + pixmp->pixelformat = sess->fmt_out->pixfmt; + + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + pixmp->width = sess->width; + pixmp->height = sess->height; + pixmp->colorspace = sess->colorspace; + pixmp->ycbcr_enc = sess->ycbcr_enc; + pixmp->quantization = sess->quantization; + pixmp->xfer_func = sess->xfer_func; + } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + pixmp->width = sess->width; + pixmp->height = sess->height; + } + + vdec_try_fmt_common(sess, sess->core->platform->num_formats, f); + + return 0; +} + +static int vdec_s_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct amvdec_session *sess = + container_of(file->private_data, struct amvdec_session, fh); + struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp; + u32 num_formats = sess->core->platform->num_formats; + const struct amvdec_format *fmt_out; + struct v4l2_pix_format_mplane orig_pixmp; + struct v4l2_format format; + u32 pixfmt_out = 0, pixfmt_cap = 0; + + orig_pixmp = *pixmp; + + fmt_out = vdec_try_fmt_common(sess, num_formats, f); + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + pixfmt_out = pixmp->pixelformat; + pixfmt_cap = sess->pixfmt_cap; + } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + pixfmt_cap = pixmp->pixelformat; + pixfmt_out = sess->fmt_out->pixfmt; + } + + memset(&format, 0, sizeof(format)); + + format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + format.fmt.pix_mp.pixelformat = pixfmt_out; + format.fmt.pix_mp.width = orig_pixmp.width; + format.fmt.pix_mp.height = orig_pixmp.height; + vdec_try_fmt_common(sess, num_formats, &format); + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + sess->width = format.fmt.pix_mp.width; + sess->height = format.fmt.pix_mp.height; + sess->colorspace = pixmp->colorspace; + sess->ycbcr_enc = pixmp->ycbcr_enc; + sess->quantization = pixmp->quantization; + sess->xfer_func = pixmp->xfer_func; + } + + memset(&format, 0, sizeof(format)); + + format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + format.fmt.pix_mp.pixelformat = pixfmt_cap; + format.fmt.pix_mp.width = orig_pixmp.width; + format.fmt.pix_mp.height = orig_pixmp.height; + vdec_try_fmt_common(sess, num_formats, &format); + + sess->width = format.fmt.pix_mp.width; + sess->height = format.fmt.pix_mp.height; + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + sess->fmt_out = fmt_out; + else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + sess->pixfmt_cap = format.fmt.pix_mp.pixelformat; + + return 0; +} + +static int vdec_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + struct amvdec_session *sess = + container_of(file->private_data, struct amvdec_session, fh); + const struct vdec_platform *platform = sess->core->platform; + const struct amvdec_format *fmt_out; + + memset(f->reserved, 0, sizeof(f->reserved)); + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + if (f->index >= platform->num_formats) + return -EINVAL; + + fmt_out = &platform->formats[f->index]; + f->pixelformat = fmt_out->pixfmt; + f->flags = fmt_out->flags; + } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + fmt_out = sess->fmt_out; + if (f->index >= 4 || !fmt_out->pixfmts_cap[f->index]) + return -EINVAL; + + f->pixelformat = fmt_out->pixfmts_cap[f->index]; + } else { + return -EINVAL; + } + + return 0; +} + +static int vdec_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct amvdec_session *sess = + container_of(file->private_data, struct amvdec_session, fh); + const struct amvdec_format *formats = sess->core->platform->formats; + const struct amvdec_format *fmt; + u32 num_formats = sess->core->platform->num_formats; + + fmt = find_format(formats, num_formats, fsize->pixel_format); + if (!fmt || fsize->index) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; + + fsize->stepwise.min_width = 256; + fsize->stepwise.max_width = fmt->max_width; + fsize->stepwise.step_width = 1; + fsize->stepwise.min_height = 144; + fsize->stepwise.max_height = fmt->max_height; + fsize->stepwise.step_height = 1; + + return 0; +} + +static int +vdec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd) +{ + struct amvdec_session *sess = + container_of(file->private_data, struct amvdec_session, fh); + struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops; + struct device *dev = sess->core->dev; + int ret; + + ret = v4l2_m2m_ioctl_try_decoder_cmd(file, fh, cmd); + if (ret) + return ret; + + if (!(sess->streamon_out & sess->streamon_cap)) + return 0; + + /* Currently not handled since we do not support dynamic resolution + * for MPEG2. We consider both queues streaming to mean that the + * decoding session is started + */ + if (cmd->cmd == V4L2_DEC_CMD_START) + return 0; + + /* Should not happen */ + if (cmd->cmd != V4L2_DEC_CMD_STOP) + return -EINVAL; + + dev_dbg(dev, "Received V4L2_DEC_CMD_STOP\n"); + sess->should_stop = 1; + + vdec_wait_inactive(sess); + + if (codec_ops->drain) { + codec_ops->drain(sess); + } else if (codec_ops->eos_sequence) { + u32 len; + const u8 *data = codec_ops->eos_sequence(&len); + + esparser_queue_eos(sess->core, data, len); + } + + return ret; +} + +static int vdec_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_EOS: + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_event_subscribe(fh, sub, 0, NULL); + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subscribe_event(fh, sub); + default: + return -EINVAL; + } +} + +static int vdec_g_pixelaspect(struct file *file, void *fh, int type, + struct v4l2_fract *f) +{ + struct amvdec_session *sess = + container_of(file->private_data, struct amvdec_session, fh); + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return -EINVAL; + + *f = sess->pixelaspect; + return 0; +} + +static const struct v4l2_ioctl_ops vdec_ioctl_ops = { + .vidioc_querycap = vdec_querycap, + .vidioc_enum_fmt_vid_cap = vdec_enum_fmt, + .vidioc_enum_fmt_vid_out = vdec_enum_fmt, + .vidioc_s_fmt_vid_cap_mplane = vdec_s_fmt, + .vidioc_s_fmt_vid_out_mplane = vdec_s_fmt, + .vidioc_g_fmt_vid_cap_mplane = vdec_g_fmt, + .vidioc_g_fmt_vid_out_mplane = vdec_g_fmt, + .vidioc_try_fmt_vid_cap_mplane = vdec_try_fmt, + .vidioc_try_fmt_vid_out_mplane = vdec_try_fmt, + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + .vidioc_enum_framesizes = vdec_enum_framesizes, + .vidioc_subscribe_event = vdec_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_try_decoder_cmd, + .vidioc_decoder_cmd = vdec_decoder_cmd, + .vidioc_g_pixelaspect = vdec_g_pixelaspect, +}; + +static int m2m_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct amvdec_session *sess = priv; + int ret; + + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->ops = &vdec_vb2_ops; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->drv_priv = sess; + src_vq->buf_struct_size = sizeof(struct dummy_buf); + src_vq->min_buffers_needed = 1; + src_vq->dev = sess->core->dev; + src_vq->lock = &sess->lock; + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->ops = &vdec_vb2_ops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->drv_priv = sess; + dst_vq->buf_struct_size = sizeof(struct dummy_buf); + dst_vq->min_buffers_needed = 1; + dst_vq->dev = sess->core->dev; + dst_vq->lock = &sess->lock; + ret = vb2_queue_init(dst_vq); + if (ret) { + vb2_queue_release(src_vq); + return ret; + } + + return 0; +} + +static int vdec_init_ctrls(struct amvdec_session *sess) +{ + struct v4l2_ctrl_handler *ctrl_handler = &sess->ctrl_handler; + int ret; + + ret = v4l2_ctrl_handler_init(ctrl_handler, 1); + if (ret) + return ret; + + sess->ctrl_min_buf_capture = + v4l2_ctrl_new_std(ctrl_handler, NULL, + V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 1, 32, 1, + 1); + + ret = ctrl_handler->error; + if (ret) { + v4l2_ctrl_handler_free(ctrl_handler); + return ret; + } + + return 0; +} + +static int vdec_open(struct file *file) +{ + struct amvdec_core *core = video_drvdata(file); + struct device *dev = core->dev; + const struct amvdec_format *formats = core->platform->formats; + struct amvdec_session *sess; + int ret; + + sess = kzalloc(sizeof(*sess), GFP_KERNEL); + if (!sess) + return -ENOMEM; + + sess->core = core; + + sess->m2m_dev = v4l2_m2m_init(&vdec_m2m_ops); + if (IS_ERR(sess->m2m_dev)) { + dev_err(dev, "Fail to v4l2_m2m_init\n"); + ret = PTR_ERR(sess->m2m_dev); + goto err_free_sess; + } + + sess->m2m_ctx = v4l2_m2m_ctx_init(sess->m2m_dev, sess, m2m_queue_init); + if (IS_ERR(sess->m2m_ctx)) { + dev_err(dev, "Fail to v4l2_m2m_ctx_init\n"); + ret = PTR_ERR(sess->m2m_ctx); + goto err_m2m_release; + } + + ret = vdec_init_ctrls(sess); + if (ret) + goto err_m2m_release; + + sess->pixfmt_cap = formats[0].pixfmts_cap[0]; + sess->fmt_out = &formats[0]; + sess->width = 1280; + sess->height = 720; + sess->pixelaspect.numerator = 1; + sess->pixelaspect.denominator = 1; + + INIT_LIST_HEAD(&sess->timestamps); + INIT_LIST_HEAD(&sess->bufs_recycle); + INIT_WORK(&sess->esparser_queue_work, esparser_queue_all_src); + mutex_init(&sess->lock); + mutex_init(&sess->bufs_recycle_lock); + spin_lock_init(&sess->ts_spinlock); + + v4l2_fh_init(&sess->fh, core->vdev_dec); + sess->fh.ctrl_handler = &sess->ctrl_handler; + v4l2_fh_add(&sess->fh); + sess->fh.m2m_ctx = sess->m2m_ctx; + file->private_data = &sess->fh; + + return 0; + +err_m2m_release: + v4l2_m2m_release(sess->m2m_dev); +err_free_sess: + kfree(sess); + return ret; +} + +static int vdec_close(struct file *file) +{ + struct amvdec_session *sess = + container_of(file->private_data, struct amvdec_session, fh); + + v4l2_m2m_ctx_release(sess->m2m_ctx); + v4l2_m2m_release(sess->m2m_dev); + v4l2_fh_del(&sess->fh); + v4l2_fh_exit(&sess->fh); + + mutex_destroy(&sess->lock); + mutex_destroy(&sess->bufs_recycle_lock); + + kfree(sess); + + return 0; +} + +static const struct v4l2_file_operations vdec_fops = { + .owner = THIS_MODULE, + .open = vdec_open, + .release = vdec_close, + .unlocked_ioctl = video_ioctl2, + .poll = v4l2_m2m_fop_poll, + .mmap = v4l2_m2m_fop_mmap, +}; + +static irqreturn_t vdec_isr(int irq, void *data) +{ + struct amvdec_core *core = data; + struct amvdec_session *sess = core->cur_sess; + + sess->last_irq_jiffies = get_jiffies_64(); + + return sess->fmt_out->codec_ops->isr(sess); +} + +static irqreturn_t vdec_threaded_isr(int irq, void *data) +{ + struct amvdec_core *core = data; + struct amvdec_session *sess = core->cur_sess; + + return sess->fmt_out->codec_ops->threaded_isr(sess); +} + +static const struct of_device_id vdec_dt_match[] = { + { .compatible = "amlogic,gxbb-vdec", + .data = &vdec_platform_gxbb }, + { .compatible = "amlogic,gxm-vdec", + .data = &vdec_platform_gxm }, + { .compatible = "amlogic,gxl-vdec", + .data = &vdec_platform_gxl }, + {} +}; +MODULE_DEVICE_TABLE(of, vdec_dt_match); + +static int vdec_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct video_device *vdev; + struct amvdec_core *core; + struct resource *r; + const struct of_device_id *of_id; + int irq; + int ret; + + core = devm_kzalloc(dev, sizeof(*core), GFP_KERNEL); + if (!core) + return -ENOMEM; + + core->dev = dev; + platform_set_drvdata(pdev, core); + + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dos"); + core->dos_base = devm_ioremap_resource(dev, r); + if (IS_ERR(core->dos_base)) { + dev_err(dev, "Couldn't remap DOS memory\n"); + return PTR_ERR(core->dos_base); + } + + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "esparser"); + core->esparser_base = devm_ioremap_resource(dev, r); + if (IS_ERR(core->esparser_base)) { + dev_err(dev, "Couldn't remap ESPARSER memory\n"); + return PTR_ERR(core->esparser_base); + } + + core->regmap_ao = + syscon_regmap_lookup_by_phandle(dev->of_node, + "amlogic,ao-sysctrl"); + if (IS_ERR(core->regmap_ao)) { + dev_err(dev, "Couldn't regmap AO sysctrl\n"); + return PTR_ERR(core->regmap_ao); + } + + core->canvas = meson_canvas_get(dev); + if (IS_ERR(core->canvas)) + return PTR_ERR(core->canvas); + + core->dos_parser_clk = devm_clk_get(dev, "dos_parser"); + if (IS_ERR(core->dos_parser_clk)) + return -EPROBE_DEFER; + + core->dos_clk = devm_clk_get(dev, "dos"); + if (IS_ERR(core->dos_clk)) + return -EPROBE_DEFER; + + core->vdec_1_clk = devm_clk_get(dev, "vdec_1"); + if (IS_ERR(core->vdec_1_clk)) + return -EPROBE_DEFER; + + core->vdec_hevc_clk = devm_clk_get(dev, "vdec_hevc"); + if (IS_ERR(core->vdec_hevc_clk)) + return -EPROBE_DEFER; + + irq = platform_get_irq_byname(pdev, "vdec"); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(core->dev, irq, vdec_isr, + vdec_threaded_isr, IRQF_ONESHOT, + "vdec", core); + if (ret) + return ret; + + ret = esparser_init(pdev, core); + if (ret) + return ret; + + ret = v4l2_device_register(dev, &core->v4l2_dev); + if (ret) { + dev_err(dev, "Couldn't register v4l2 device\n"); + return -ENOMEM; + } + + vdev = video_device_alloc(); + if (!vdev) { + ret = -ENOMEM; + goto err_vdev_release; + } + + of_id = of_match_node(vdec_dt_match, dev->of_node); + core->platform = of_id->data; + core->vdev_dec = vdev; + core->dev_dec = dev; + mutex_init(&core->lock); + + strscpy(vdev->name, "meson-video-decoder", sizeof(vdev->name)); + vdev->release = video_device_release; + vdev->fops = &vdec_fops; + vdev->ioctl_ops = &vdec_ioctl_ops; + vdev->vfl_dir = VFL_DIR_M2M; + vdev->v4l2_dev = &core->v4l2_dev; + vdev->lock = &core->lock; + vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; + + video_set_drvdata(vdev, core); + + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + if (ret) { + dev_err(dev, "Failed registering video device\n"); + goto err_vdev_release; + } + + return 0; + +err_vdev_release: + video_device_release(vdev); + return ret; +} + +static int vdec_remove(struct platform_device *pdev) +{ + struct amvdec_core *core = platform_get_drvdata(pdev); + + video_unregister_device(core->vdev_dec); + + return 0; +} + +static struct platform_driver meson_vdec_driver = { + .probe = vdec_probe, + .remove = vdec_remove, + .driver = { + .name = "meson-vdec", + .of_match_table = vdec_dt_match, + }, +}; +module_platform_driver(meson_vdec_driver); + +MODULE_DESCRIPTION("Meson video decoder driver for GXBB/GXL/GXM"); +MODULE_AUTHOR("Maxime Jourdan <mjourdan@baylibre.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/meson/vdec/vdec.h b/drivers/staging/media/meson/vdec/vdec.h new file mode 100644 index 000000000000..d811e7976519 --- /dev/null +++ b/drivers/staging/media/meson/vdec/vdec.h @@ -0,0 +1,267 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2018 BayLibre, SAS + * Author: Maxime Jourdan <mjourdan@baylibre.com> + */ + +#ifndef __MESON_VDEC_CORE_H_ +#define __MESON_VDEC_CORE_H_ + +#include <linux/irqreturn.h> +#include <linux/regmap.h> +#include <linux/list.h> +#include <media/videobuf2-v4l2.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <linux/soc/amlogic/meson-canvas.h> + +#include "vdec_platform.h" + +/* 32 buffers in 3-plane YUV420 */ +#define MAX_CANVAS (32 * 3) + +struct amvdec_buffer { + struct list_head list; + struct vb2_buffer *vb; +}; + +/** + * struct amvdec_timestamp - stores a src timestamp along with a VIFIFO offset + * + * @list: used to make lists out of this struct + * @ts: timestamp + * @offset: offset in the VIFIFO where the associated packet was written + */ +struct amvdec_timestamp { + struct list_head list; + u64 ts; + u32 offset; +}; + +struct amvdec_session; + +/** + * struct amvdec_core - device parameters, singleton + * + * @dos_base: DOS memory base address + * @esparser_base: PARSER memory base address + * @regmap_ao: regmap for the AO bus + * @dev: core device + * @dev_dec: decoder device + * @platform: platform-specific data + * @canvas: canvas provider reference + * @dos_parser_clk: DOS_PARSER clock + * @dos_clk: DOS clock + * @vdec_1_clk: VDEC_1 clock + * @vdec_hevc_clk: VDEC_HEVC clock + * @esparser_reset: RESET for the PARSER + * @vdec_dec: video device for the decoder + * @v4l2_dev: v4l2 device + * @cur_sess: current decoding session + */ +struct amvdec_core { + void __iomem *dos_base; + void __iomem *esparser_base; + struct regmap *regmap_ao; + + struct device *dev; + struct device *dev_dec; + const struct vdec_platform *platform; + + struct meson_canvas *canvas; + + struct clk *dos_parser_clk; + struct clk *dos_clk; + struct clk *vdec_1_clk; + struct clk *vdec_hevc_clk; + + struct reset_control *esparser_reset; + + struct video_device *vdev_dec; + struct v4l2_device v4l2_dev; + + struct amvdec_session *cur_sess; + struct mutex lock; /* video device lock */ +}; + +/** + * struct amvdec_ops - vdec operations + * + * @start: mandatory call when the vdec needs to initialize + * @stop: mandatory call when the vdec needs to stop + * @conf_esparser: mandatory call to let the vdec configure the ESPARSER + * @vififo_level: mandatory call to get the current amount of data + * in the VIFIFO + * @use_offsets: mandatory call. Returns 1 if the VDEC supports vififo offsets + */ +struct amvdec_ops { + int (*start)(struct amvdec_session *sess); + int (*stop)(struct amvdec_session *sess); + void (*conf_esparser)(struct amvdec_session *sess); + u32 (*vififo_level)(struct amvdec_session *sess); +}; + +/** + * struct amvdec_codec_ops - codec operations + * + * @start: mandatory call when the codec needs to initialize + * @stop: mandatory call when the codec needs to stop + * @load_extended_firmware: optional call to load additional firmware bits + * @num_pending_bufs: optional call to get the number of dst buffers on hold + * @can_recycle: optional call to know if the codec is ready to recycle + * a dst buffer + * @recycle: optional call to tell the codec to recycle a dst buffer. Must go + * in pair with @can_recycle + * @drain: optional call if the codec has a custom way of draining + * @eos_sequence: optional call to get an end sequence to send to esparser + * for flush. Mutually exclusive with @drain. + * @isr: mandatory call when the ISR triggers + * @threaded_isr: mandatory call for the threaded ISR + */ +struct amvdec_codec_ops { + int (*start)(struct amvdec_session *sess); + int (*stop)(struct amvdec_session *sess); + int (*load_extended_firmware)(struct amvdec_session *sess, + const u8 *data, u32 len); + u32 (*num_pending_bufs)(struct amvdec_session *sess); + int (*can_recycle)(struct amvdec_core *core); + void (*recycle)(struct amvdec_core *core, u32 buf_idx); + void (*drain)(struct amvdec_session *sess); + void (*resume)(struct amvdec_session *sess); + const u8 * (*eos_sequence)(u32 *len); + irqreturn_t (*isr)(struct amvdec_session *sess); + irqreturn_t (*threaded_isr)(struct amvdec_session *sess); +}; + +/** + * struct amvdec_format - describes one of the OUTPUT (src) format supported + * + * @pixfmt: V4L2 pixel format + * @min_buffers: minimum amount of CAPTURE (dst) buffers + * @max_buffers: maximum amount of CAPTURE (dst) buffers + * @max_width: maximum picture width supported + * @max_height: maximum picture height supported + * @flags: enum flags associated with this pixfmt + * @vdec_ops: the VDEC operations that support this format + * @codec_ops: the codec operations that support this format + * @firmware_path: Path to the firmware that supports this format + * @pixfmts_cap: list of CAPTURE pixel formats available with pixfmt + */ +struct amvdec_format { + u32 pixfmt; + u32 min_buffers; + u32 max_buffers; + u32 max_width; + u32 max_height; + u32 flags; + + struct amvdec_ops *vdec_ops; + struct amvdec_codec_ops *codec_ops; + + char *firmware_path; + u32 pixfmts_cap[4]; +}; + +enum amvdec_status { + STATUS_STOPPED, + STATUS_RUNNING, + STATUS_NEEDS_RESUME, +}; + +/** + * struct amvdec_session - decoding session parameters + * + * @core: reference to the vdec core struct + * @fh: v4l2 file handle + * @m2m_dev: v4l2 m2m device + * @m2m_ctx: v4l2 m2m context + * @ctrl_handler: V4L2 control handler + * @ctrl_min_buf_capture: V4L2 control V4L2_CID_MIN_BUFFERS_FOR_CAPTURE + * @fmt_out: vdec pixel format for the OUTPUT queue + * @pixfmt_cap: V4L2 pixel format for the CAPTURE queue + * @width: current picture width + * @height: current picture height + * @colorspace: current colorspace + * @ycbcr_enc: current ycbcr_enc + * @quantization: current quantization + * @xfer_func: current transfer function + * @pixelaspect: Pixel Aspect Ratio reported by the decoder + * @esparser_queued_bufs: number of buffers currently queued into ESPARSER + * @esparser_queue_work: work struct for the ESPARSER to process src buffers + * @streamon_cap: stream on flag for capture queue + * @streamon_out: stream on flag for output queue + * @sequence_cap: capture sequence counter + * @should_stop: flag set if userspace signaled EOS via command + * or empty buffer + * @keyframe_found: flag set once a keyframe has been parsed + * @canvas_alloc: array of all the canvas IDs allocated + * @canvas_num: number of canvas IDs allocated + * @vififo_vaddr: virtual address for the VIFIFO + * @vififo_paddr: physical address for the VIFIFO + * @vififo_size: size of the VIFIFO dma alloc + * @bufs_recycle: list of buffers that need to be recycled + * @bufs_recycle_lock: lock for the bufs_recycle list + * @recycle_thread: task struct for the recycling thread + * @timestamps: chronological list of src timestamps + * @ts_spinlock: spinlock for the timestamps list + * @last_irq_jiffies: tracks last time the vdec triggered an IRQ + * @status: current decoding status + * @priv: codec private data + */ +struct amvdec_session { + struct amvdec_core *core; + + struct v4l2_fh fh; + struct v4l2_m2m_dev *m2m_dev; + struct v4l2_m2m_ctx *m2m_ctx; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *ctrl_min_buf_capture; + struct mutex lock; /* cap & out queues lock */ + + const struct amvdec_format *fmt_out; + u32 pixfmt_cap; + + u32 width; + u32 height; + u32 colorspace; + u8 ycbcr_enc; + u8 quantization; + u8 xfer_func; + + struct v4l2_fract pixelaspect; + + atomic_t esparser_queued_bufs; + struct work_struct esparser_queue_work; + + unsigned int streamon_cap, streamon_out; + unsigned int sequence_cap; + unsigned int should_stop; + unsigned int keyframe_found; + unsigned int num_dst_bufs; + + u8 canvas_alloc[MAX_CANVAS]; + u32 canvas_num; + + void *vififo_vaddr; + dma_addr_t vififo_paddr; + u32 vififo_size; + + struct list_head bufs_recycle; + struct mutex bufs_recycle_lock; /* bufs_recycle list lock */ + struct task_struct *recycle_thread; + + struct list_head timestamps; + spinlock_t ts_spinlock; /* timestamp list lock */ + + u64 last_irq_jiffies; + u32 last_offset; + u32 wrap_count; + u32 fw_idx_to_vb2_idx[32]; + + enum amvdec_status status; + void *priv; +}; + +u32 amvdec_get_output_size(struct amvdec_session *sess); + +#endif diff --git a/drivers/staging/media/meson/vdec/vdec_1.c b/drivers/staging/media/meson/vdec/vdec_1.c new file mode 100644 index 000000000000..3a15c6fc0567 --- /dev/null +++ b/drivers/staging/media/meson/vdec/vdec_1.c @@ -0,0 +1,230 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018 BayLibre, SAS + * Author: Maxime Jourdan <mjourdan@baylibre.com> + * + * VDEC_1 is a video decoding block that allows decoding of + * MPEG 1/2/4, H.263, H.264, MJPEG, VC1 + */ + +#include <linux/firmware.h> +#include <linux/clk.h> + +#include "vdec_1.h" +#include "vdec_helpers.h" +#include "dos_regs.h" + +/* AO Registers */ +#define AO_RTI_GEN_PWR_SLEEP0 0xe8 +#define AO_RTI_GEN_PWR_ISO0 0xec + #define GEN_PWR_VDEC_1 (BIT(3) | BIT(2)) + +#define MC_SIZE (4096 * 4) + +static int +vdec_1_load_firmware(struct amvdec_session *sess, const char *fwname) +{ + const struct firmware *fw; + struct amvdec_core *core = sess->core; + struct device *dev = core->dev_dec; + struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops; + static void *mc_addr; + static dma_addr_t mc_addr_map; + int ret; + u32 i = 1000; + + ret = request_firmware(&fw, fwname, dev); + if (ret < 0) + return -EINVAL; + + if (fw->size < MC_SIZE) { + dev_err(dev, "Firmware size %zu is too small. Expected %u.\n", + fw->size, MC_SIZE); + ret = -EINVAL; + goto release_firmware; + } + + mc_addr = dma_alloc_coherent(core->dev, MC_SIZE, + &mc_addr_map, GFP_KERNEL); + if (!mc_addr) { + ret = -ENOMEM; + goto release_firmware; + } + + memcpy(mc_addr, fw->data, MC_SIZE); + + amvdec_write_dos(core, MPSR, 0); + amvdec_write_dos(core, CPSR, 0); + + amvdec_clear_dos_bits(core, MDEC_PIC_DC_CTRL, BIT(31)); + + amvdec_write_dos(core, IMEM_DMA_ADR, mc_addr_map); + amvdec_write_dos(core, IMEM_DMA_COUNT, MC_SIZE / 4); + amvdec_write_dos(core, IMEM_DMA_CTRL, (0x8000 | (7 << 16))); + + while (--i && amvdec_read_dos(core, IMEM_DMA_CTRL) & 0x8000); + + if (i == 0) { + dev_err(dev, "Firmware load fail (DMA hang?)\n"); + ret = -EINVAL; + goto free_mc; + } + + if (codec_ops->load_extended_firmware) + ret = codec_ops->load_extended_firmware(sess, + fw->data + MC_SIZE, + fw->size - MC_SIZE); + +free_mc: + dma_free_coherent(core->dev, MC_SIZE, mc_addr, mc_addr_map); +release_firmware: + release_firmware(fw); + return ret; +} + +static int vdec_1_stbuf_power_up(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + + amvdec_write_dos(core, VLD_MEM_VIFIFO_CONTROL, 0); + amvdec_write_dos(core, VLD_MEM_VIFIFO_WRAP_COUNT, 0); + amvdec_write_dos(core, POWER_CTL_VLD, BIT(4)); + + amvdec_write_dos(core, VLD_MEM_VIFIFO_START_PTR, sess->vififo_paddr); + amvdec_write_dos(core, VLD_MEM_VIFIFO_CURR_PTR, sess->vififo_paddr); + amvdec_write_dos(core, VLD_MEM_VIFIFO_END_PTR, + sess->vififo_paddr + sess->vififo_size - 8); + + amvdec_write_dos_bits(core, VLD_MEM_VIFIFO_CONTROL, 1); + amvdec_clear_dos_bits(core, VLD_MEM_VIFIFO_CONTROL, 1); + + amvdec_write_dos(core, VLD_MEM_VIFIFO_BUF_CNTL, MEM_BUFCTRL_MANUAL); + amvdec_write_dos(core, VLD_MEM_VIFIFO_WP, sess->vififo_paddr); + + amvdec_write_dos_bits(core, VLD_MEM_VIFIFO_BUF_CNTL, 1); + amvdec_clear_dos_bits(core, VLD_MEM_VIFIFO_BUF_CNTL, 1); + + amvdec_write_dos_bits(core, VLD_MEM_VIFIFO_CONTROL, + (0x11 << MEM_FIFO_CNT_BIT) | MEM_FILL_ON_LEVEL | + MEM_CTRL_FILL_EN | MEM_CTRL_EMPTY_EN); + + return 0; +} + +static void vdec_1_conf_esparser(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + + /* VDEC_1 specific ESPARSER stuff */ + amvdec_write_dos(core, DOS_GEN_CTRL0, 0); + amvdec_write_dos(core, VLD_MEM_VIFIFO_BUF_CNTL, 1); + amvdec_clear_dos_bits(core, VLD_MEM_VIFIFO_BUF_CNTL, 1); +} + +static u32 vdec_1_vififo_level(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + + return amvdec_read_dos(core, VLD_MEM_VIFIFO_LEVEL); +} + +static int vdec_1_stop(struct amvdec_session *sess) +{ + struct amvdec_core *core = sess->core; + struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops; + + amvdec_write_dos(core, MPSR, 0); + amvdec_write_dos(core, CPSR, 0); + amvdec_write_dos(core, ASSIST_MBOX1_MASK, 0); + + amvdec_write_dos(core, DOS_SW_RESET0, BIT(12) | BIT(11)); + amvdec_write_dos(core, DOS_SW_RESET0, 0); + amvdec_read_dos(core, DOS_SW_RESET0); + + /* enable vdec1 isolation */ + regmap_write(core->regmap_ao, AO_RTI_GEN_PWR_ISO0, 0xc0); + /* power off vdec1 memories */ + amvdec_write_dos(core, DOS_MEM_PD_VDEC, 0xffffffff); + /* power off vdec1 */ + regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, + GEN_PWR_VDEC_1, GEN_PWR_VDEC_1); + + clk_disable_unprepare(core->vdec_1_clk); + + if (sess->priv) + codec_ops->stop(sess); + + return 0; +} + +static int vdec_1_start(struct amvdec_session *sess) +{ + int ret; + struct amvdec_core *core = sess->core; + struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops; + + /* Configure the vdec clk to the maximum available */ + clk_set_rate(core->vdec_1_clk, 666666666); + ret = clk_prepare_enable(core->vdec_1_clk); + if (ret) + return ret; + + /* Enable power for VDEC_1 */ + regmap_update_bits(core->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, + GEN_PWR_VDEC_1, 0); + usleep_range(10, 20); + + /* Reset VDEC1 */ + amvdec_write_dos(core, DOS_SW_RESET0, 0xfffffffc); + amvdec_write_dos(core, DOS_SW_RESET0, 0x00000000); + + amvdec_write_dos(core, DOS_GCLK_EN0, 0x3ff); + + /* enable VDEC Memories */ + amvdec_write_dos(core, DOS_MEM_PD_VDEC, 0); + /* Remove VDEC1 Isolation */ + regmap_write(core->regmap_ao, AO_RTI_GEN_PWR_ISO0, 0); + /* Reset DOS top registers */ + amvdec_write_dos(core, DOS_VDEC_MCRCC_STALL_CTRL, 0); + + amvdec_write_dos(core, GCLK_EN, 0x3ff); + amvdec_clear_dos_bits(core, MDEC_PIC_DC_CTRL, BIT(31)); + + vdec_1_stbuf_power_up(sess); + + ret = vdec_1_load_firmware(sess, sess->fmt_out->firmware_path); + if (ret) + goto stop; + + ret = codec_ops->start(sess); + if (ret) + goto stop; + + /* Enable IRQ */ + amvdec_write_dos(core, ASSIST_MBOX1_CLR_REG, 1); + amvdec_write_dos(core, ASSIST_MBOX1_MASK, 1); + + /* Enable 2-plane output */ + if (sess->pixfmt_cap == V4L2_PIX_FMT_NV12M) + amvdec_write_dos_bits(core, MDEC_PIC_DC_CTRL, BIT(17)); + else + amvdec_clear_dos_bits(core, MDEC_PIC_DC_CTRL, BIT(17)); + + /* Enable firmware processor */ + amvdec_write_dos(core, MPSR, 1); + /* Let the firmware settle */ + usleep_range(10, 20); + + return 0; + +stop: + vdec_1_stop(sess); + return ret; +} + +struct amvdec_ops vdec_1_ops = { + .start = vdec_1_start, + .stop = vdec_1_stop, + .conf_esparser = vdec_1_conf_esparser, + .vififo_level = vdec_1_vififo_level, +}; diff --git a/drivers/staging/media/meson/vdec/vdec_1.h b/drivers/staging/media/meson/vdec/vdec_1.h new file mode 100644 index 000000000000..042d930c40d7 --- /dev/null +++ b/drivers/staging/media/meson/vdec/vdec_1.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2018 BayLibre, SAS + * Author: Maxime Jourdan <mjourdan@baylibre.com> + */ + +#ifndef __MESON_VDEC_VDEC_1_H_ +#define __MESON_VDEC_VDEC_1_H_ + +#include "vdec.h" + +extern struct amvdec_ops vdec_1_ops; + +#endif diff --git a/drivers/staging/media/meson/vdec/vdec_helpers.c b/drivers/staging/media/meson/vdec/vdec_helpers.c new file mode 100644 index 000000000000..f16948bdbf2f --- /dev/null +++ b/drivers/staging/media/meson/vdec/vdec_helpers.c @@ -0,0 +1,449 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018 BayLibre, SAS + * Author: Maxime Jourdan <mjourdan@baylibre.com> + */ + +#include <linux/gcd.h> +#include <media/v4l2-mem2mem.h> +#include <media/v4l2-event.h> +#include <media/videobuf2-dma-contig.h> + +#include "vdec_helpers.h" + +#define NUM_CANVAS_NV12 2 +#define NUM_CANVAS_YUV420 3 + +u32 amvdec_read_dos(struct amvdec_core *core, u32 reg) +{ + return readl_relaxed(core->dos_base + reg); +} +EXPORT_SYMBOL_GPL(amvdec_read_dos); + +void amvdec_write_dos(struct amvdec_core *core, u32 reg, u32 val) +{ + writel_relaxed(val, core->dos_base + reg); +} +EXPORT_SYMBOL_GPL(amvdec_write_dos); + +void amvdec_write_dos_bits(struct amvdec_core *core, u32 reg, u32 val) +{ + amvdec_write_dos(core, reg, amvdec_read_dos(core, reg) | val); +} +EXPORT_SYMBOL_GPL(amvdec_write_dos_bits); + +void amvdec_clear_dos_bits(struct amvdec_core *core, u32 reg, u32 val) +{ + amvdec_write_dos(core, reg, amvdec_read_dos(core, reg) & ~val); +} +EXPORT_SYMBOL_GPL(amvdec_clear_dos_bits); + +u32 amvdec_read_parser(struct amvdec_core *core, u32 reg) +{ + return readl_relaxed(core->esparser_base + reg); +} +EXPORT_SYMBOL_GPL(amvdec_read_parser); + +void amvdec_write_parser(struct amvdec_core *core, u32 reg, u32 val) +{ + writel_relaxed(val, core->esparser_base + reg); +} +EXPORT_SYMBOL_GPL(amvdec_write_parser); + +static int canvas_alloc(struct amvdec_session *sess, u8 *canvas_id) +{ + int ret; + + if (sess->canvas_num >= MAX_CANVAS) { + dev_err(sess->core->dev, "Reached max number of canvas\n"); + return -ENOMEM; + } + + ret = meson_canvas_alloc(sess->core->canvas, canvas_id); + if (ret) + return ret; + + sess->canvas_alloc[sess->canvas_num++] = *canvas_id; + return 0; +} + +static int set_canvas_yuv420m(struct amvdec_session *sess, + struct vb2_buffer *vb, u32 width, + u32 height, u32 reg) +{ + struct amvdec_core *core = sess->core; + u8 canvas_id[NUM_CANVAS_YUV420]; /* Y U V */ + dma_addr_t buf_paddr[NUM_CANVAS_YUV420]; /* Y U V */ + int ret, i; + + for (i = 0; i < NUM_CANVAS_YUV420; ++i) { + ret = canvas_alloc(sess, &canvas_id[i]); + if (ret) + return ret; + + buf_paddr[i] = + vb2_dma_contig_plane_dma_addr(vb, i); + } + + /* Y plane */ + meson_canvas_config(core->canvas, canvas_id[0], buf_paddr[0], + width, height, MESON_CANVAS_WRAP_NONE, + MESON_CANVAS_BLKMODE_LINEAR, + MESON_CANVAS_ENDIAN_SWAP64); + + /* U plane */ + meson_canvas_config(core->canvas, canvas_id[1], buf_paddr[1], + width / 2, height / 2, MESON_CANVAS_WRAP_NONE, + MESON_CANVAS_BLKMODE_LINEAR, + MESON_CANVAS_ENDIAN_SWAP64); + + /* V plane */ + meson_canvas_config(core->canvas, canvas_id[2], buf_paddr[2], + width / 2, height / 2, MESON_CANVAS_WRAP_NONE, + MESON_CANVAS_BLKMODE_LINEAR, + MESON_CANVAS_ENDIAN_SWAP64); + + amvdec_write_dos(core, reg, + ((canvas_id[2]) << 16) | + ((canvas_id[1]) << 8) | + (canvas_id[0])); + + return 0; +} + +static int set_canvas_nv12m(struct amvdec_session *sess, + struct vb2_buffer *vb, u32 width, + u32 height, u32 reg) +{ + struct amvdec_core *core = sess->core; + u8 canvas_id[NUM_CANVAS_NV12]; /* Y U/V */ + dma_addr_t buf_paddr[NUM_CANVAS_NV12]; /* Y U/V */ + int ret, i; + + for (i = 0; i < NUM_CANVAS_NV12; ++i) { + ret = canvas_alloc(sess, &canvas_id[i]); + if (ret) + return ret; + + buf_paddr[i] = + vb2_dma_contig_plane_dma_addr(vb, i); + } + + /* Y plane */ + meson_canvas_config(core->canvas, canvas_id[0], buf_paddr[0], + width, height, MESON_CANVAS_WRAP_NONE, + MESON_CANVAS_BLKMODE_LINEAR, + MESON_CANVAS_ENDIAN_SWAP64); + + /* U/V plane */ + meson_canvas_config(core->canvas, canvas_id[1], buf_paddr[1], + width, height / 2, MESON_CANVAS_WRAP_NONE, + MESON_CANVAS_BLKMODE_LINEAR, + MESON_CANVAS_ENDIAN_SWAP64); + + amvdec_write_dos(core, reg, + ((canvas_id[1]) << 16) | + ((canvas_id[1]) << 8) | + (canvas_id[0])); + + return 0; +} + +int amvdec_set_canvases(struct amvdec_session *sess, + u32 reg_base[], u32 reg_num[]) +{ + struct v4l2_m2m_buffer *buf; + u32 pixfmt = sess->pixfmt_cap; + u32 width = ALIGN(sess->width, 64); + u32 height = ALIGN(sess->height, 64); + u32 reg_cur = reg_base[0]; + u32 reg_num_cur = 0; + u32 reg_base_cur = 0; + int i = 0; + int ret; + + v4l2_m2m_for_each_dst_buf(sess->m2m_ctx, buf) { + if (!reg_base[reg_base_cur]) + return -EINVAL; + + reg_cur = reg_base[reg_base_cur] + reg_num_cur * 4; + + switch (pixfmt) { + case V4L2_PIX_FMT_NV12M: + ret = set_canvas_nv12m(sess, &buf->vb.vb2_buf, width, + height, reg_cur); + if (ret) + return ret; + break; + case V4L2_PIX_FMT_YUV420M: + ret = set_canvas_yuv420m(sess, &buf->vb.vb2_buf, width, + height, reg_cur); + if (ret) + return ret; + break; + default: + dev_err(sess->core->dev, "Unsupported pixfmt %08X\n", + pixfmt); + return -EINVAL; + } + + reg_num_cur++; + if (reg_num_cur >= reg_num[reg_base_cur]) { + reg_base_cur++; + reg_num_cur = 0; + } + + sess->fw_idx_to_vb2_idx[i++] = buf->vb.vb2_buf.index; + } + + return 0; +} +EXPORT_SYMBOL_GPL(amvdec_set_canvases); + +void amvdec_add_ts_reorder(struct amvdec_session *sess, u64 ts, u32 offset) +{ + struct amvdec_timestamp *new_ts, *tmp; + unsigned long flags; + + new_ts = kmalloc(sizeof(*new_ts), GFP_KERNEL); + new_ts->ts = ts; + new_ts->offset = offset; + + spin_lock_irqsave(&sess->ts_spinlock, flags); + + if (list_empty(&sess->timestamps)) + goto add_tail; + + list_for_each_entry(tmp, &sess->timestamps, list) { + if (ts <= tmp->ts) { + list_add_tail(&new_ts->list, &tmp->list); + goto unlock; + } + } + +add_tail: + list_add_tail(&new_ts->list, &sess->timestamps); +unlock: + spin_unlock_irqrestore(&sess->ts_spinlock, flags); +} +EXPORT_SYMBOL_GPL(amvdec_add_ts_reorder); + +void amvdec_remove_ts(struct amvdec_session *sess, u64 ts) +{ + struct amvdec_timestamp *tmp; + unsigned long flags; + + spin_lock_irqsave(&sess->ts_spinlock, flags); + list_for_each_entry(tmp, &sess->timestamps, list) { + if (tmp->ts == ts) { + list_del(&tmp->list); + kfree(tmp); + goto unlock; + } + } + dev_warn(sess->core->dev_dec, + "Couldn't remove buffer with timestamp %llu from list\n", ts); + +unlock: + spin_unlock_irqrestore(&sess->ts_spinlock, flags); +} +EXPORT_SYMBOL_GPL(amvdec_remove_ts); + +static void dst_buf_done(struct amvdec_session *sess, + struct vb2_v4l2_buffer *vbuf, + u32 field, + u64 timestamp) +{ + struct device *dev = sess->core->dev_dec; + u32 output_size = amvdec_get_output_size(sess); + + switch (sess->pixfmt_cap) { + case V4L2_PIX_FMT_NV12M: + vbuf->vb2_buf.planes[0].bytesused = output_size; + vbuf->vb2_buf.planes[1].bytesused = output_size / 2; + break; + case V4L2_PIX_FMT_YUV420M: + vbuf->vb2_buf.planes[0].bytesused = output_size; + vbuf->vb2_buf.planes[1].bytesused = output_size / 4; + vbuf->vb2_buf.planes[2].bytesused = output_size / 4; + break; + } + + vbuf->vb2_buf.timestamp = timestamp; + vbuf->sequence = sess->sequence_cap++; + + if (sess->should_stop && + atomic_read(&sess->esparser_queued_bufs) <= 2) { + const struct v4l2_event ev = { .type = V4L2_EVENT_EOS }; + + dev_dbg(dev, "Signaling EOS\n"); + v4l2_event_queue_fh(&sess->fh, &ev); + vbuf->flags |= V4L2_BUF_FLAG_LAST; + } else if (sess->should_stop) + dev_dbg(dev, "should_stop, %u bufs remain\n", + atomic_read(&sess->esparser_queued_bufs)); + + dev_dbg(dev, "Buffer %u done\n", vbuf->vb2_buf.index); + vbuf->field = field; + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE); + + /* Buffer done probably means the vififo got freed */ + schedule_work(&sess->esparser_queue_work); +} + +void amvdec_dst_buf_done(struct amvdec_session *sess, + struct vb2_v4l2_buffer *vbuf, u32 field) +{ + struct device *dev = sess->core->dev_dec; + struct amvdec_timestamp *tmp; + struct list_head *timestamps = &sess->timestamps; + u64 timestamp; + unsigned long flags; + + spin_lock_irqsave(&sess->ts_spinlock, flags); + if (list_empty(timestamps)) { + dev_err(dev, "Buffer %u done but list is empty\n", + vbuf->vb2_buf.index); + + v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); + spin_unlock_irqrestore(&sess->ts_spinlock, flags); + return; + } + + tmp = list_first_entry(timestamps, struct amvdec_timestamp, list); + timestamp = tmp->ts; + list_del(&tmp->list); + kfree(tmp); + spin_unlock_irqrestore(&sess->ts_spinlock, flags); + + dst_buf_done(sess, vbuf, field, timestamp); + atomic_dec(&sess->esparser_queued_bufs); +} +EXPORT_SYMBOL_GPL(amvdec_dst_buf_done); + +void amvdec_dst_buf_done_offset(struct amvdec_session *sess, + struct vb2_v4l2_buffer *vbuf, + u32 offset, u32 field, bool allow_drop) +{ + struct device *dev = sess->core->dev_dec; + struct amvdec_timestamp *match = NULL; + struct amvdec_timestamp *tmp, *n; + u64 timestamp = 0; + unsigned long flags; + + spin_lock_irqsave(&sess->ts_spinlock, flags); + + /* Look for our vififo offset to get the corresponding timestamp. */ + list_for_each_entry_safe(tmp, n, &sess->timestamps, list) { + s64 delta = (s64)offset - tmp->offset; + + /* Offsets reported by codecs usually differ slightly, + * so we need some wiggle room. + * 4KiB being the minimum packet size, there is no risk here. + */ + if (delta > (-1 * (s32)SZ_4K) && delta < SZ_4K) { + match = tmp; + break; + } + + if (!allow_drop) + continue; + + /* Delete any timestamp entry that appears before our target + * (not all src packets/timestamps lead to a frame) + */ + if (delta > 0 || delta < -1 * (s32)sess->vififo_size) { + atomic_dec(&sess->esparser_queued_bufs); + list_del(&tmp->list); + kfree(tmp); + } + } + + if (!match) { + dev_dbg(dev, "Buffer %u done but can't match offset (%08X)\n", + vbuf->vb2_buf.index, offset); + } else { + timestamp = match->ts; + list_del(&match->list); + kfree(match); + } + spin_unlock_irqrestore(&sess->ts_spinlock, flags); + + dst_buf_done(sess, vbuf, field, timestamp); + if (match) + atomic_dec(&sess->esparser_queued_bufs); +} +EXPORT_SYMBOL_GPL(amvdec_dst_buf_done_offset); + +void amvdec_dst_buf_done_idx(struct amvdec_session *sess, + u32 buf_idx, u32 offset, u32 field) +{ + struct vb2_v4l2_buffer *vbuf; + struct device *dev = sess->core->dev_dec; + + vbuf = v4l2_m2m_dst_buf_remove_by_idx(sess->m2m_ctx, + sess->fw_idx_to_vb2_idx[buf_idx]); + + if (!vbuf) { + dev_err(dev, + "Buffer %u done but it doesn't exist in m2m_ctx\n", + buf_idx); + return; + } + + if (offset != -1) + amvdec_dst_buf_done_offset(sess, vbuf, offset, field, true); + else + amvdec_dst_buf_done(sess, vbuf, field); +} +EXPORT_SYMBOL_GPL(amvdec_dst_buf_done_idx); + +void amvdec_set_par_from_dar(struct amvdec_session *sess, + u32 dar_num, u32 dar_den) +{ + u32 div; + + sess->pixelaspect.numerator = sess->height * dar_num; + sess->pixelaspect.denominator = sess->width * dar_den; + div = gcd(sess->pixelaspect.numerator, sess->pixelaspect.denominator); + sess->pixelaspect.numerator /= div; + sess->pixelaspect.denominator /= div; +} +EXPORT_SYMBOL_GPL(amvdec_set_par_from_dar); + +void amvdec_src_change(struct amvdec_session *sess, u32 width, + u32 height, u32 dpb_size) +{ + static const struct v4l2_event ev = { + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION }; + + v4l2_ctrl_s_ctrl(sess->ctrl_min_buf_capture, dpb_size); + + /* Check if the capture queue is already configured well for our + * usecase. If so, keep decoding with it and do not send the event + */ + if (sess->width == width && + sess->height == height && + dpb_size <= sess->num_dst_bufs) { + sess->fmt_out->codec_ops->resume(sess); + return; + } + + sess->width = width; + sess->height = height; + sess->status = STATUS_NEEDS_RESUME; + + dev_dbg(sess->core->dev, "Res. changed (%ux%u), DPB size %u\n", + width, height, dpb_size); + v4l2_event_queue_fh(&sess->fh, &ev); +} +EXPORT_SYMBOL_GPL(amvdec_src_change); + +void amvdec_abort(struct amvdec_session *sess) +{ + dev_info(sess->core->dev, "Aborting decoding session!\n"); + vb2_queue_error(&sess->m2m_ctx->cap_q_ctx.q); + vb2_queue_error(&sess->m2m_ctx->out_q_ctx.q); +} +EXPORT_SYMBOL_GPL(amvdec_abort); diff --git a/drivers/staging/media/meson/vdec/vdec_helpers.h b/drivers/staging/media/meson/vdec/vdec_helpers.h new file mode 100644 index 000000000000..a455a9ee1cc2 --- /dev/null +++ b/drivers/staging/media/meson/vdec/vdec_helpers.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2018 BayLibre, SAS + * Author: Maxime Jourdan <mjourdan@baylibre.com> + */ + +#ifndef __MESON_VDEC_HELPERS_H_ +#define __MESON_VDEC_HELPERS_H_ + +#include "vdec.h" + +/** + * amvdec_set_canvases() - Map VB2 buffers to canvases + * + * @sess: current session + * @reg_base: Registry bases of where to write the canvas indexes + * @reg_num: number of contiguous registers after each reg_base (including it) + */ +int amvdec_set_canvases(struct amvdec_session *sess, + u32 reg_base[], u32 reg_num[]); + +/* Helpers to read/write to the various IPs (DOS, PARSER) */ +u32 amvdec_read_dos(struct amvdec_core *core, u32 reg); +void amvdec_write_dos(struct amvdec_core *core, u32 reg, u32 val); +void amvdec_write_dos_bits(struct amvdec_core *core, u32 reg, u32 val); +void amvdec_clear_dos_bits(struct amvdec_core *core, u32 reg, u32 val); +u32 amvdec_read_parser(struct amvdec_core *core, u32 reg); +void amvdec_write_parser(struct amvdec_core *core, u32 reg, u32 val); + +/** + * amvdec_dst_buf_done_idx() - Signal that a buffer is done decoding + * + * @sess: current session + * @buf_idx: hardware buffer index + * @offset: VIFIFO bitstream offset corresponding to the buffer + * @field: V4L2 interlaced field + */ +void amvdec_dst_buf_done_idx(struct amvdec_session *sess, u32 buf_idx, + u32 offset, u32 field); +void amvdec_dst_buf_done(struct amvdec_session *sess, + struct vb2_v4l2_buffer *vbuf, u32 field); +void amvdec_dst_buf_done_offset(struct amvdec_session *sess, + struct vb2_v4l2_buffer *vbuf, + u32 offset, u32 field, bool allow_drop); + +/** + * amvdec_add_ts_reorder() - Add a timestamp to the list in chronological order + * + * @sess: current session + * @ts: timestamp to add + * @offset: offset in the VIFIFO where the associated packet was written + */ +void amvdec_add_ts_reorder(struct amvdec_session *sess, u64 ts, u32 offset); +void amvdec_remove_ts(struct amvdec_session *sess, u64 ts); + +/** + * amvdec_set_par_from_dar() - Set Pixel Aspect Ratio from Display Aspect Ratio + * + * @sess: current session + * @dar_num: numerator of the DAR + * @dar_den: denominator of the DAR + */ +void amvdec_set_par_from_dar(struct amvdec_session *sess, + u32 dar_num, u32 dar_den); + +/** + * amvdec_src_change() - Notify new resolution/DPB size to the core + * + * @sess: current session + * @width: picture width detected by the hardware + * @height: picture height detected by the hardware + * @dpb_size: Decoded Picture Buffer size (= amount of buffers for decoding) + */ +void amvdec_src_change(struct amvdec_session *sess, u32 width, + u32 height, u32 dpb_size); + +/** + * amvdec_abort() - Abort the current decoding session + * + * @sess: current session + */ +void amvdec_abort(struct amvdec_session *sess); +#endif diff --git a/drivers/staging/media/meson/vdec/vdec_platform.c b/drivers/staging/media/meson/vdec/vdec_platform.c new file mode 100644 index 000000000000..824dbc7f46f5 --- /dev/null +++ b/drivers/staging/media/meson/vdec/vdec_platform.c @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018 BayLibre, SAS + * Author: Maxime Jourdan <mjourdan@baylibre.com> + */ + +#include "vdec_platform.h" +#include "vdec.h" + +#include "vdec_1.h" +#include "codec_mpeg12.h" + +static const struct amvdec_format vdec_formats_gxbb[] = { + { + .pixfmt = V4L2_PIX_FMT_MPEG1, + .min_buffers = 8, + .max_buffers = 8, + .max_width = 1920, + .max_height = 1080, + .vdec_ops = &vdec_1_ops, + .codec_ops = &codec_mpeg12_ops, + .firmware_path = "meson/vdec/gxl_mpeg12.bin", + .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, + }, { + .pixfmt = V4L2_PIX_FMT_MPEG2, + .min_buffers = 8, + .max_buffers = 8, + .max_width = 1920, + .max_height = 1080, + .vdec_ops = &vdec_1_ops, + .codec_ops = &codec_mpeg12_ops, + .firmware_path = "meson/vdec/gxl_mpeg12.bin", + .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, + }, +}; + +static const struct amvdec_format vdec_formats_gxl[] = { + { + .pixfmt = V4L2_PIX_FMT_MPEG1, + .min_buffers = 8, + .max_buffers = 8, + .max_width = 1920, + .max_height = 1080, + .vdec_ops = &vdec_1_ops, + .codec_ops = &codec_mpeg12_ops, + .firmware_path = "meson/vdec/gxl_mpeg12.bin", + .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, + }, { + .pixfmt = V4L2_PIX_FMT_MPEG2, + .min_buffers = 8, + .max_buffers = 8, + .max_width = 1920, + .max_height = 1080, + .vdec_ops = &vdec_1_ops, + .codec_ops = &codec_mpeg12_ops, + .firmware_path = "meson/vdec/gxl_mpeg12.bin", + .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, + }, +}; + +static const struct amvdec_format vdec_formats_gxm[] = { + { + .pixfmt = V4L2_PIX_FMT_MPEG1, + .min_buffers = 8, + .max_buffers = 8, + .max_width = 1920, + .max_height = 1080, + .vdec_ops = &vdec_1_ops, + .codec_ops = &codec_mpeg12_ops, + .firmware_path = "meson/vdec/gxl_mpeg12.bin", + .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, + }, { + .pixfmt = V4L2_PIX_FMT_MPEG2, + .min_buffers = 8, + .max_buffers = 8, + .max_width = 1920, + .max_height = 1080, + .vdec_ops = &vdec_1_ops, + .codec_ops = &codec_mpeg12_ops, + .firmware_path = "meson/vdec/gxl_mpeg12.bin", + .pixfmts_cap = { V4L2_PIX_FMT_NV12M, V4L2_PIX_FMT_YUV420M, 0 }, + }, +}; + +const struct vdec_platform vdec_platform_gxbb = { + .formats = vdec_formats_gxbb, + .num_formats = ARRAY_SIZE(vdec_formats_gxbb), + .revision = VDEC_REVISION_GXBB, +}; + +const struct vdec_platform vdec_platform_gxl = { + .formats = vdec_formats_gxl, + .num_formats = ARRAY_SIZE(vdec_formats_gxl), + .revision = VDEC_REVISION_GXL, +}; + +const struct vdec_platform vdec_platform_gxm = { + .formats = vdec_formats_gxm, + .num_formats = ARRAY_SIZE(vdec_formats_gxm), + .revision = VDEC_REVISION_GXM, +}; diff --git a/drivers/staging/media/meson/vdec/vdec_platform.h b/drivers/staging/media/meson/vdec/vdec_platform.h new file mode 100644 index 000000000000..f6025326db1d --- /dev/null +++ b/drivers/staging/media/meson/vdec/vdec_platform.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2018 BayLibre, SAS + * Author: Maxime Jourdan <mjourdan@baylibre.com> + */ + +#ifndef __MESON_VDEC_PLATFORM_H_ +#define __MESON_VDEC_PLATFORM_H_ + +#include "vdec.h" + +struct amvdec_format; + +enum vdec_revision { + VDEC_REVISION_GXBB, + VDEC_REVISION_GXL, + VDEC_REVISION_GXM, +}; + +struct vdec_platform { + const struct amvdec_format *formats; + const u32 num_formats; + enum vdec_revision revision; +}; + +extern const struct vdec_platform vdec_platform_gxbb; +extern const struct vdec_platform vdec_platform_gxm; +extern const struct vdec_platform vdec_platform_gxl; + +#endif diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index c2c5a9cd8642..c307707480f7 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -533,12 +533,6 @@ iss_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap) strscpy(cap->driver, ISS_VIDEO_DRIVER_NAME, sizeof(cap->driver)); strscpy(cap->card, video->video.name, sizeof(cap->card)); strscpy(cap->bus_info, "media", sizeof(cap->bus_info)); - - if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - else - cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; - cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT; @@ -1272,6 +1266,11 @@ int omap4iss_video_register(struct iss_video *video, struct v4l2_device *vdev) int ret; video->video.v4l2_dev = vdev; + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + video->video.device_caps = V4L2_CAP_VIDEO_CAPTURE; + else + video->video.device_caps = V4L2_CAP_VIDEO_OUTPUT; + video->video.device_caps |= V4L2_CAP_STREAMING; ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1); if (ret < 0) diff --git a/drivers/staging/media/rockchip/vpu/Kconfig b/drivers/staging/media/rockchip/vpu/Kconfig index fc54bbf6753d..842b003e08b8 100644 --- a/drivers/staging/media/rockchip/vpu/Kconfig +++ b/drivers/staging/media/rockchip/vpu/Kconfig @@ -3,6 +3,7 @@ config VIDEO_ROCKCHIP_VPU tristate "Rockchip VPU driver" depends on ARCH_ROCKCHIP || COMPILE_TEST depends on VIDEO_DEV && VIDEO_V4L2 && MEDIA_CONTROLLER + depends on MEDIA_CONTROLLER_REQUEST_API select VIDEOBUF2_DMA_CONTIG select VIDEOBUF2_VMALLOC select V4L2_MEM2MEM_DEV diff --git a/drivers/staging/media/rockchip/vpu/Makefile b/drivers/staging/media/rockchip/vpu/Makefile index ae5d143a0bfa..be278157d196 100644 --- a/drivers/staging/media/rockchip/vpu/Makefile +++ b/drivers/staging/media/rockchip/vpu/Makefile @@ -3,9 +3,12 @@ obj-$(CONFIG_VIDEO_ROCKCHIP_VPU) += rockchip-vpu.o rockchip-vpu-y += \ rockchip_vpu_drv.o \ - rockchip_vpu_enc.o \ + rockchip_vpu_v4l2.o \ rk3288_vpu_hw.o \ rk3288_vpu_hw_jpeg_enc.o \ + rk3288_vpu_hw_mpeg2_dec.o \ rk3399_vpu_hw.o \ rk3399_vpu_hw_jpeg_enc.o \ - rockchip_vpu_jpeg.o + rk3399_vpu_hw_mpeg2_dec.o \ + rockchip_vpu_jpeg.o \ + rockchip_vpu_mpeg2.o diff --git a/drivers/staging/media/rockchip/vpu/rk3288_vpu_hw.c b/drivers/staging/media/rockchip/vpu/rk3288_vpu_hw.c index a5e9d183fffd..003143c77d37 100644 --- a/drivers/staging/media/rockchip/vpu/rk3288_vpu_hw.c +++ b/drivers/staging/media/rockchip/vpu/rk3288_vpu_hw.c @@ -55,6 +55,26 @@ static const struct rockchip_vpu_fmt rk3288_vpu_enc_fmts[] = { }, }; +static const struct rockchip_vpu_fmt rk3288_vpu_dec_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_NV12, + .codec_mode = RK_VPU_MODE_NONE, + }, + { + .fourcc = V4L2_PIX_FMT_MPEG2_SLICE, + .codec_mode = RK_VPU_MODE_MPEG2_DEC, + .max_depth = 2, + .frmsize = { + .min_width = 48, + .max_width = 1920, + .step_width = MPEG2_MB_DIM, + .min_height = 48, + .max_height = 1088, + .step_height = MPEG2_MB_DIM, + }, + }, +}; + static irqreturn_t rk3288_vepu_irq(int irq, void *dev_id) { struct rockchip_vpu_dev *vpu = dev_id; @@ -74,6 +94,24 @@ static irqreturn_t rk3288_vepu_irq(int irq, void *dev_id) return IRQ_HANDLED; } +static irqreturn_t rk3288_vdpu_irq(int irq, void *dev_id) +{ + struct rockchip_vpu_dev *vpu = dev_id; + enum vb2_buffer_state state; + u32 status; + + status = vdpu_read(vpu, VDPU_REG_INTERRUPT); + state = (status & VDPU_REG_INTERRUPT_DEC_RDY_INT) ? + VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR; + + vdpu_write(vpu, 0, VDPU_REG_INTERRUPT); + vdpu_write(vpu, VDPU_REG_CONFIG_DEC_CLK_GATE_E, VDPU_REG_CONFIG); + + rockchip_vpu_irq_done(vpu, 0, state); + + return IRQ_HANDLED; +} + static int rk3288_vpu_hw_init(struct rockchip_vpu_dev *vpu) { /* Bump ACLK to max. possible freq. to improve performance. */ @@ -90,6 +128,15 @@ static void rk3288_vpu_enc_reset(struct rockchip_vpu_ctx *ctx) vepu_write(vpu, 0, VEPU_REG_AXI_CTRL); } +static void rk3288_vpu_dec_reset(struct rockchip_vpu_ctx *ctx) +{ + struct rockchip_vpu_dev *vpu = ctx->dev; + + vdpu_write(vpu, VDPU_REG_INTERRUPT_DEC_IRQ_DIS, VDPU_REG_INTERRUPT); + vdpu_write(vpu, VDPU_REG_CONFIG_DEC_CLK_GATE_E, VDPU_REG_CONFIG); + vdpu_write(vpu, 1, VDPU_REG_SOFT_RESET); +} + /* * Supported codec ops. */ @@ -98,6 +145,14 @@ static const struct rockchip_vpu_codec_ops rk3288_vpu_codec_ops[] = { [RK_VPU_MODE_JPEG_ENC] = { .run = rk3288_vpu_jpeg_enc_run, .reset = rk3288_vpu_enc_reset, + .init = rockchip_vpu_jpeg_enc_init, + .exit = rockchip_vpu_jpeg_enc_exit, + }, + [RK_VPU_MODE_MPEG2_DEC] = { + .run = rk3288_vpu_mpeg2_dec_run, + .reset = rk3288_vpu_dec_reset, + .init = rockchip_vpu_mpeg2_dec_init, + .exit = rockchip_vpu_mpeg2_dec_exit, }, }; @@ -109,9 +164,13 @@ const struct rockchip_vpu_variant rk3288_vpu_variant = { .enc_offset = 0x0, .enc_fmts = rk3288_vpu_enc_fmts, .num_enc_fmts = ARRAY_SIZE(rk3288_vpu_enc_fmts), + .dec_offset = 0x400, + .dec_fmts = rk3288_vpu_dec_fmts, + .num_dec_fmts = ARRAY_SIZE(rk3288_vpu_dec_fmts), + .codec = RK_VPU_JPEG_ENCODER | RK_VPU_MPEG2_DECODER, .codec_ops = rk3288_vpu_codec_ops, - .codec = RK_VPU_CODEC_JPEG, .vepu_irq = rk3288_vepu_irq, + .vdpu_irq = rk3288_vdpu_irq, .init = rk3288_vpu_hw_init, .clk_names = {"aclk", "hclk"}, .num_clocks = 2 diff --git a/drivers/staging/media/rockchip/vpu/rk3288_vpu_hw_jpeg_enc.c b/drivers/staging/media/rockchip/vpu/rk3288_vpu_hw_jpeg_enc.c index 06daea66fb49..68176e91330a 100644 --- a/drivers/staging/media/rockchip/vpu/rk3288_vpu_hw_jpeg_enc.c +++ b/drivers/staging/media/rockchip/vpu/rk3288_vpu_hw_jpeg_enc.c @@ -9,7 +9,7 @@ #include <media/v4l2-mem2mem.h> #include "rockchip_vpu_jpeg.h" #include "rockchip_vpu.h" -#include "rockchip_vpu_common.h" +#include "rockchip_vpu_v4l2.h" #include "rockchip_vpu_hw.h" #include "rk3288_vpu_regs.h" @@ -37,9 +37,9 @@ static void rk3288_vpu_jpeg_enc_set_buffers(struct rockchip_vpu_dev *vpu, WARN_ON(pix_fmt->num_planes > 3); - vepu_write_relaxed(vpu, ctx->bounce_dma_addr, + vepu_write_relaxed(vpu, ctx->jpeg_enc.bounce_buffer.dma, VEPU_REG_ADDR_OUTPUT_STREAM); - vepu_write_relaxed(vpu, ctx->bounce_size, + vepu_write_relaxed(vpu, ctx->jpeg_enc.bounce_buffer.size, VEPU_REG_STR_BUF_LIMIT); if (pix_fmt->num_planes == 1) { diff --git a/drivers/staging/media/rockchip/vpu/rk3288_vpu_hw_mpeg2_dec.c b/drivers/staging/media/rockchip/vpu/rk3288_vpu_hw_mpeg2_dec.c new file mode 100644 index 000000000000..e9eee47fcea1 --- /dev/null +++ b/drivers/staging/media/rockchip/vpu/rk3288_vpu_hw_mpeg2_dec.c @@ -0,0 +1,261 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip VPU codec driver + * + * Copyright (C) 2018 Rockchip Electronics Co., Ltd. + */ + +#include <asm/unaligned.h> +#include <linux/bitfield.h> +#include <media/v4l2-mem2mem.h> +#include "rockchip_vpu.h" +#include "rockchip_vpu_hw.h" + +#define VDPU_SWREG(nr) ((nr) * 4) + +#define VDPU_REG_RLC_VLC_BASE VDPU_SWREG(12) +#define VDPU_REG_DEC_OUT_BASE VDPU_SWREG(13) +#define VDPU_REG_REFER0_BASE VDPU_SWREG(14) +#define VDPU_REG_REFER1_BASE VDPU_SWREG(15) +#define VDPU_REG_REFER2_BASE VDPU_SWREG(16) +#define VDPU_REG_REFER3_BASE VDPU_SWREG(17) +#define VDPU_REG_QTABLE_BASE VDPU_SWREG(40) +#define VDPU_REG_DEC_E(v) ((v) ? BIT(0) : 0) + +#define VDPU_REG_DEC_AXI_RD_ID(v) (((v) << 24) & GENMASK(31, 24)) +#define VDPU_REG_DEC_TIMEOUT_E(v) ((v) ? BIT(23) : 0) +#define VDPU_REG_DEC_STRSWAP32_E(v) ((v) ? BIT(22) : 0) +#define VDPU_REG_DEC_STRENDIAN_E(v) ((v) ? BIT(21) : 0) +#define VDPU_REG_DEC_INSWAP32_E(v) ((v) ? BIT(20) : 0) +#define VDPU_REG_DEC_OUTSWAP32_E(v) ((v) ? BIT(19) : 0) +#define VDPU_REG_DEC_DATA_DISC_E(v) ((v) ? BIT(18) : 0) +#define VDPU_REG_DEC_LATENCY(v) (((v) << 11) & GENMASK(16, 11)) +#define VDPU_REG_DEC_CLK_GATE_E(v) ((v) ? BIT(10) : 0) +#define VDPU_REG_DEC_IN_ENDIAN(v) ((v) ? BIT(9) : 0) +#define VDPU_REG_DEC_OUT_ENDIAN(v) ((v) ? BIT(8) : 0) +#define VDPU_REG_DEC_ADV_PRE_DIS(v) ((v) ? BIT(6) : 0) +#define VDPU_REG_DEC_SCMD_DIS(v) ((v) ? BIT(5) : 0) +#define VDPU_REG_DEC_MAX_BURST(v) (((v) << 0) & GENMASK(4, 0)) + +#define VDPU_REG_DEC_MODE(v) (((v) << 28) & GENMASK(31, 28)) +#define VDPU_REG_RLC_MODE_E(v) ((v) ? BIT(27) : 0) +#define VDPU_REG_PIC_INTERLACE_E(v) ((v) ? BIT(23) : 0) +#define VDPU_REG_PIC_FIELDMODE_E(v) ((v) ? BIT(22) : 0) +#define VDPU_REG_PIC_B_E(v) ((v) ? BIT(21) : 0) +#define VDPU_REG_PIC_INTER_E(v) ((v) ? BIT(20) : 0) +#define VDPU_REG_PIC_TOPFIELD_E(v) ((v) ? BIT(19) : 0) +#define VDPU_REG_FWD_INTERLACE_E(v) ((v) ? BIT(18) : 0) +#define VDPU_REG_FILTERING_DIS(v) ((v) ? BIT(14) : 0) +#define VDPU_REG_WRITE_MVS_E(v) ((v) ? BIT(12) : 0) +#define VDPU_REG_DEC_AXI_WR_ID(v) (((v) << 0) & GENMASK(7, 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_INIT_QP(v) (((v) << 25) & GENMASK(30, 25)) +#define VDPU_REG_STREAM_LEN(v) (((v) << 0) & GENMASK(23, 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) + +#define VDPU_REG_STARTMB_X(v) (((v) << 23) & GENMASK(31, 23)) +#define VDPU_REG_STARTMB_Y(v) (((v) << 15) & GENMASK(22, 15)) + +#define VDPU_REG_APF_THRESHOLD(v) (((v) << 0) & GENMASK(13, 0)) + +#define PICT_TOP_FIELD 1 +#define PICT_BOTTOM_FIELD 2 +#define PICT_FRAME 3 + +static void +rk3288_vpu_mpeg2_dec_set_quantization(struct rockchip_vpu_dev *vpu, + struct rockchip_vpu_ctx *ctx) +{ + struct v4l2_ctrl_mpeg2_quantization *quantization; + + quantization = rockchip_vpu_get_ctrl(ctx, + V4L2_CID_MPEG_VIDEO_MPEG2_QUANTIZATION); + rockchip_vpu_mpeg2_dec_copy_qtable(ctx->mpeg2_dec.qtable.cpu, + quantization); + vdpu_write_relaxed(vpu, ctx->mpeg2_dec.qtable.dma, + VDPU_REG_QTABLE_BASE); +} + +static void +rk3288_vpu_mpeg2_dec_set_buffers(struct rockchip_vpu_dev *vpu, + struct rockchip_vpu_ctx *ctx, + struct vb2_buffer *src_buf, + struct vb2_buffer *dst_buf, + const struct v4l2_mpeg2_sequence *sequence, + const struct v4l2_mpeg2_picture *picture, + const struct v4l2_ctrl_mpeg2_slice_params *slice_params) +{ + dma_addr_t forward_addr = 0, backward_addr = 0; + dma_addr_t current_addr, addr; + struct vb2_queue *vq; + + vq = v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx); + + switch (picture->picture_coding_type) { + case V4L2_MPEG2_PICTURE_CODING_TYPE_B: + backward_addr = rockchip_vpu_get_ref(vq, + slice_params->backward_ref_ts); + /* fall-through */ + case V4L2_MPEG2_PICTURE_CODING_TYPE_P: + forward_addr = rockchip_vpu_get_ref(vq, + slice_params->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 (picture->picture_structure == PICT_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 (picture->picture_structure == PICT_FRAME || + picture->picture_coding_type == V4L2_MPEG2_PICTURE_CODING_TYPE_B || + (picture->picture_structure == PICT_TOP_FIELD && + picture->top_field_first) || + (picture->picture_structure == PICT_BOTTOM_FIELD && + !picture->top_field_first)) { + vdpu_write_relaxed(vpu, forward_addr, VDPU_REG_REFER0_BASE); + vdpu_write_relaxed(vpu, forward_addr, VDPU_REG_REFER1_BASE); + } else if (picture->picture_structure == PICT_TOP_FIELD) { + vdpu_write_relaxed(vpu, forward_addr, VDPU_REG_REFER0_BASE); + vdpu_write_relaxed(vpu, current_addr, VDPU_REG_REFER1_BASE); + } else if (picture->picture_structure == PICT_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); +} + +void rk3288_vpu_mpeg2_dec_run(struct rockchip_vpu_ctx *ctx) +{ + struct rockchip_vpu_dev *vpu = ctx->dev; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + const struct v4l2_ctrl_mpeg2_slice_params *slice_params; + const struct v4l2_mpeg2_sequence *sequence; + const struct v4l2_mpeg2_picture *picture; + u32 reg; + + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + + /* Apply request controls if any */ + v4l2_ctrl_request_setup(src_buf->vb2_buf.req_obj.req, + &ctx->ctrl_handler); + + slice_params = rockchip_vpu_get_ctrl(ctx, + V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS); + sequence = &slice_params->sequence; + picture = &slice_params->picture; + + reg = VDPU_REG_DEC_AXI_RD_ID(0) | + VDPU_REG_DEC_TIMEOUT_E(1) | + VDPU_REG_DEC_STRSWAP32_E(1) | + VDPU_REG_DEC_STRENDIAN_E(1) | + VDPU_REG_DEC_INSWAP32_E(1) | + VDPU_REG_DEC_OUTSWAP32_E(1) | + VDPU_REG_DEC_DATA_DISC_E(0) | + VDPU_REG_DEC_LATENCY(0) | + VDPU_REG_DEC_CLK_GATE_E(1) | + VDPU_REG_DEC_IN_ENDIAN(1) | + VDPU_REG_DEC_OUT_ENDIAN(1) | + VDPU_REG_DEC_ADV_PRE_DIS(0) | + VDPU_REG_DEC_SCMD_DIS(0) | + VDPU_REG_DEC_MAX_BURST(16); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(2)); + + reg = VDPU_REG_DEC_MODE(5) | + VDPU_REG_RLC_MODE_E(0) | + VDPU_REG_PIC_INTERLACE_E(!sequence->progressive_sequence) | + VDPU_REG_PIC_FIELDMODE_E(picture->picture_structure != PICT_FRAME) | + VDPU_REG_PIC_B_E(picture->picture_coding_type == V4L2_MPEG2_PICTURE_CODING_TYPE_B) | + VDPU_REG_PIC_INTER_E(picture->picture_coding_type != V4L2_MPEG2_PICTURE_CODING_TYPE_I) | + VDPU_REG_PIC_TOPFIELD_E(picture->picture_structure == PICT_TOP_FIELD) | + VDPU_REG_FWD_INTERLACE_E(0) | + VDPU_REG_FILTERING_DIS(1) | + VDPU_REG_WRITE_MVS_E(0) | + VDPU_REG_DEC_AXI_WR_ID(0); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(3)); + + reg = VDPU_REG_PIC_MB_WIDTH(MPEG2_MB_WIDTH(ctx->dst_fmt.width)) | + VDPU_REG_PIC_MB_HEIGHT_P(MPEG2_MB_HEIGHT(ctx->dst_fmt.height)) | + VDPU_REG_ALT_SCAN_E(picture->alternate_scan) | + VDPU_REG_TOPFIELDFIRST_E(picture->top_field_first); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(4)); + + reg = VDPU_REG_STRM_START_BIT(slice_params->data_bit_offset) | + VDPU_REG_QSCALE_TYPE(picture->q_scale_type) | + VDPU_REG_CON_MV_E(picture->concealment_motion_vectors) | + VDPU_REG_INTRA_DC_PREC(picture->intra_dc_precision) | + VDPU_REG_INTRA_VLC_TAB(picture->intra_vlc_format) | + VDPU_REG_FRAME_PRED_DCT(picture->frame_pred_frame_dct); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(5)); + + reg = VDPU_REG_INIT_QP(1) | + VDPU_REG_STREAM_LEN(slice_params->bit_size >> 3); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(6)); + + reg = VDPU_REG_ALT_SCAN_FLAG_E(picture->alternate_scan) | + VDPU_REG_FCODE_FWD_HOR(picture->f_code[0][0]) | + VDPU_REG_FCODE_FWD_VER(picture->f_code[0][1]) | + VDPU_REG_FCODE_BWD_HOR(picture->f_code[1][0]) | + VDPU_REG_FCODE_BWD_VER(picture->f_code[1][1]) | + VDPU_REG_MV_ACCURACY_FWD(1) | + VDPU_REG_MV_ACCURACY_BWD(1); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(18)); + + reg = VDPU_REG_STARTMB_X(0) | + VDPU_REG_STARTMB_Y(0); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(48)); + + reg = VDPU_REG_APF_THRESHOLD(8); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(55)); + + rk3288_vpu_mpeg2_dec_set_quantization(vpu, ctx); + + rk3288_vpu_mpeg2_dec_set_buffers(vpu, ctx, &src_buf->vb2_buf, + &dst_buf->vb2_buf, + sequence, picture, slice_params); + + /* Controls no longer in-use, we can complete them */ + v4l2_ctrl_request_complete(src_buf->vb2_buf.req_obj.req, + &ctx->ctrl_handler); + + /* Kick the watchdog and start decoding */ + schedule_delayed_work(&vpu->watchdog_work, msecs_to_jiffies(2000)); + + reg = VDPU_REG_DEC_E(1); + vdpu_write(vpu, reg, VDPU_SWREG(1)); +} diff --git a/drivers/staging/media/rockchip/vpu/rk3288_vpu_regs.h b/drivers/staging/media/rockchip/vpu/rk3288_vpu_regs.h index 9d0b9bdf3297..c9631b713804 100644 --- a/drivers/staging/media/rockchip/vpu/rk3288_vpu_regs.h +++ b/drivers/staging/media/rockchip/vpu/rk3288_vpu_regs.h @@ -438,5 +438,6 @@ #define VDPU_REG_REF_BUF_CTRL2_REFBU2_THR(x) (((x) & 0xfff) << 19) #define VDPU_REG_REF_BUF_CTRL2_REFBU2_PICID(x) (((x) & 0x1f) << 14) #define VDPU_REG_REF_BUF_CTRL2_APF_THRESHOLD(x) (((x) & 0x3fff) << 0) +#define VDPU_REG_SOFT_RESET 0x194 #endif /* RK3288_VPU_REGS_H_ */ diff --git a/drivers/staging/media/rockchip/vpu/rk3399_vpu_hw.c b/drivers/staging/media/rockchip/vpu/rk3399_vpu_hw.c index 6fdef61e2127..2b3689968ef4 100644 --- a/drivers/staging/media/rockchip/vpu/rk3399_vpu_hw.c +++ b/drivers/staging/media/rockchip/vpu/rk3399_vpu_hw.c @@ -55,6 +55,26 @@ static const struct rockchip_vpu_fmt rk3399_vpu_enc_fmts[] = { }, }; +static const struct rockchip_vpu_fmt rk3399_vpu_dec_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_NV12, + .codec_mode = RK_VPU_MODE_NONE, + }, + { + .fourcc = V4L2_PIX_FMT_MPEG2_SLICE, + .codec_mode = RK_VPU_MODE_MPEG2_DEC, + .max_depth = 2, + .frmsize = { + .min_width = 48, + .max_width = 1920, + .step_width = MPEG2_MB_DIM, + .min_height = 48, + .max_height = 1088, + .step_height = MPEG2_MB_DIM, + }, + }, +}; + static irqreturn_t rk3399_vepu_irq(int irq, void *dev_id) { struct rockchip_vpu_dev *vpu = dev_id; @@ -74,6 +94,24 @@ static irqreturn_t rk3399_vepu_irq(int irq, void *dev_id) return IRQ_HANDLED; } +static irqreturn_t rk3399_vdpu_irq(int irq, void *dev_id) +{ + struct rockchip_vpu_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); + + rockchip_vpu_irq_done(vpu, 0, state); + + return IRQ_HANDLED; +} + static int rk3399_vpu_hw_init(struct rockchip_vpu_dev *vpu) { /* Bump ACLK to max. possible freq. to improve performance. */ @@ -90,6 +128,15 @@ static void rk3399_vpu_enc_reset(struct rockchip_vpu_ctx *ctx) vepu_write(vpu, 0, VEPU_REG_AXI_CTRL); } +static void rk3399_vpu_dec_reset(struct rockchip_vpu_ctx *ctx) +{ + struct rockchip_vpu_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); +} + /* * Supported codec ops. */ @@ -98,6 +145,14 @@ static const struct rockchip_vpu_codec_ops rk3399_vpu_codec_ops[] = { [RK_VPU_MODE_JPEG_ENC] = { .run = rk3399_vpu_jpeg_enc_run, .reset = rk3399_vpu_enc_reset, + .init = rockchip_vpu_jpeg_enc_init, + .exit = rockchip_vpu_jpeg_enc_exit, + }, + [RK_VPU_MODE_MPEG2_DEC] = { + .run = rk3399_vpu_mpeg2_dec_run, + .reset = rk3399_vpu_dec_reset, + .init = rockchip_vpu_mpeg2_dec_init, + .exit = rockchip_vpu_mpeg2_dec_exit, }, }; @@ -109,9 +164,13 @@ const struct rockchip_vpu_variant rk3399_vpu_variant = { .enc_offset = 0x0, .enc_fmts = rk3399_vpu_enc_fmts, .num_enc_fmts = ARRAY_SIZE(rk3399_vpu_enc_fmts), - .codec = RK_VPU_CODEC_JPEG, + .dec_offset = 0x400, + .dec_fmts = rk3399_vpu_dec_fmts, + .num_dec_fmts = ARRAY_SIZE(rk3399_vpu_dec_fmts), + .codec = RK_VPU_JPEG_ENCODER | RK_VPU_MPEG2_DECODER, .codec_ops = rk3399_vpu_codec_ops, .vepu_irq = rk3399_vepu_irq, + .vdpu_irq = rk3399_vdpu_irq, .init = rk3399_vpu_hw_init, .clk_names = {"aclk", "hclk"}, .num_clocks = 2 diff --git a/drivers/staging/media/rockchip/vpu/rk3399_vpu_hw_jpeg_enc.c b/drivers/staging/media/rockchip/vpu/rk3399_vpu_hw_jpeg_enc.c index 3d438797692e..460edc5ebe4d 100644 --- a/drivers/staging/media/rockchip/vpu/rk3399_vpu_hw_jpeg_enc.c +++ b/drivers/staging/media/rockchip/vpu/rk3399_vpu_hw_jpeg_enc.c @@ -27,7 +27,7 @@ #include <media/v4l2-mem2mem.h> #include "rockchip_vpu_jpeg.h" #include "rockchip_vpu.h" -#include "rockchip_vpu_common.h" +#include "rockchip_vpu_v4l2.h" #include "rockchip_vpu_hw.h" #include "rk3399_vpu_regs.h" @@ -69,9 +69,9 @@ static void rk3399_vpu_jpeg_enc_set_buffers(struct rockchip_vpu_dev *vpu, WARN_ON(pix_fmt->num_planes > 3); - vepu_write_relaxed(vpu, ctx->bounce_dma_addr, + vepu_write_relaxed(vpu, ctx->jpeg_enc.bounce_buffer.dma, VEPU_REG_ADDR_OUTPUT_STREAM); - vepu_write_relaxed(vpu, ctx->bounce_size, + vepu_write_relaxed(vpu, ctx->jpeg_enc.bounce_buffer.size, VEPU_REG_STR_BUF_LIMIT); if (pix_fmt->num_planes == 1) { @@ -113,11 +113,15 @@ void rk3399_vpu_jpeg_enc_run(struct rockchip_vpu_ctx *ctx) struct rockchip_vpu_dev *vpu = ctx->dev; struct vb2_v4l2_buffer *src_buf, *dst_buf; struct rockchip_vpu_jpeg_ctx jpeg_ctx; + struct media_request *src_req; u32 reg; src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + src_req = src_buf->vb2_buf.req_obj.req; + v4l2_ctrl_request_setup(src_req, &ctx->ctrl_handler); + 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; @@ -153,6 +157,8 @@ void rk3399_vpu_jpeg_enc_run(struct rockchip_vpu_ctx *ctx) | VEPU_REG_ENCODE_FORMAT_JPEG | VEPU_REG_ENCODE_ENABLE; + v4l2_ctrl_request_complete(src_req, &ctx->ctrl_handler); + /* Kick the watchdog and start encoding */ schedule_delayed_work(&vpu->watchdog_work, msecs_to_jiffies(2000)); vepu_write(vpu, reg, VEPU_REG_ENCODE_START); diff --git a/drivers/staging/media/rockchip/vpu/rk3399_vpu_hw_mpeg2_dec.c b/drivers/staging/media/rockchip/vpu/rk3399_vpu_hw_mpeg2_dec.c new file mode 100644 index 000000000000..c4c092c2004a --- /dev/null +++ b/drivers/staging/media/rockchip/vpu/rk3399_vpu_hw_mpeg2_dec.c @@ -0,0 +1,267 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip VPU codec driver + * + * Copyright (C) 2018 Rockchip Electronics Co., Ltd. + */ + +#include <asm/unaligned.h> +#include <linux/bitfield.h> +#include <media/v4l2-mem2mem.h> +#include "rockchip_vpu.h" +#include "rockchip_vpu_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) + +#define PICT_TOP_FIELD 1 +#define PICT_BOTTOM_FIELD 2 +#define PICT_FRAME 3 + +static void +rk3399_vpu_mpeg2_dec_set_quantization(struct rockchip_vpu_dev *vpu, + struct rockchip_vpu_ctx *ctx) +{ + struct v4l2_ctrl_mpeg2_quantization *quantization; + + quantization = rockchip_vpu_get_ctrl(ctx, + V4L2_CID_MPEG_VIDEO_MPEG2_QUANTIZATION); + rockchip_vpu_mpeg2_dec_copy_qtable(ctx->mpeg2_dec.qtable.cpu, + quantization); + vdpu_write_relaxed(vpu, ctx->mpeg2_dec.qtable.dma, + VDPU_REG_QTABLE_BASE); +} + +static void +rk3399_vpu_mpeg2_dec_set_buffers(struct rockchip_vpu_dev *vpu, + struct rockchip_vpu_ctx *ctx, + struct vb2_buffer *src_buf, + struct vb2_buffer *dst_buf, + const struct v4l2_mpeg2_sequence *sequence, + const struct v4l2_mpeg2_picture *picture, + const struct v4l2_ctrl_mpeg2_slice_params *slice_params) +{ + dma_addr_t forward_addr = 0, backward_addr = 0; + dma_addr_t current_addr, addr; + struct vb2_queue *vq; + + vq = v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx); + + switch (picture->picture_coding_type) { + case V4L2_MPEG2_PICTURE_CODING_TYPE_B: + backward_addr = rockchip_vpu_get_ref(vq, + slice_params->backward_ref_ts); + /* fall-through */ + case V4L2_MPEG2_PICTURE_CODING_TYPE_P: + forward_addr = rockchip_vpu_get_ref(vq, + slice_params->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 (picture->picture_structure == PICT_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 (picture->picture_structure == PICT_FRAME || + picture->picture_coding_type == V4L2_MPEG2_PICTURE_CODING_TYPE_B || + (picture->picture_structure == PICT_TOP_FIELD && + picture->top_field_first) || + (picture->picture_structure == PICT_BOTTOM_FIELD && + !picture->top_field_first)) { + vdpu_write_relaxed(vpu, forward_addr, VDPU_REG_REFER0_BASE); + vdpu_write_relaxed(vpu, forward_addr, VDPU_REG_REFER1_BASE); + } else if (picture->picture_structure == PICT_TOP_FIELD) { + vdpu_write_relaxed(vpu, forward_addr, VDPU_REG_REFER0_BASE); + vdpu_write_relaxed(vpu, current_addr, VDPU_REG_REFER1_BASE); + } else if (picture->picture_structure == PICT_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); +} + +void rk3399_vpu_mpeg2_dec_run(struct rockchip_vpu_ctx *ctx) +{ + struct rockchip_vpu_dev *vpu = ctx->dev; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + const struct v4l2_ctrl_mpeg2_slice_params *slice_params; + const struct v4l2_mpeg2_sequence *sequence; + const struct v4l2_mpeg2_picture *picture; + u32 reg; + + src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + + /* Apply request controls if any */ + v4l2_ctrl_request_setup(src_buf->vb2_buf.req_obj.req, + &ctx->ctrl_handler); + + slice_params = rockchip_vpu_get_ctrl(ctx, + V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS); + sequence = &slice_params->sequence; + picture = &slice_params->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(slice_params->bit_size >> 3); + 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(!sequence->progressive_sequence) | + VDPU_REG_PIC_FIELDMODE_E(picture->picture_structure != PICT_FRAME) | + VDPU_REG_PIC_B_E(picture->picture_coding_type == V4L2_MPEG2_PICTURE_CODING_TYPE_B) | + VDPU_REG_PIC_INTER_E(picture->picture_coding_type != V4L2_MPEG2_PICTURE_CODING_TYPE_I) | + VDPU_REG_PIC_TOPFIELD_E(picture->picture_structure == PICT_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(MPEG2_MB_WIDTH(ctx->dst_fmt.width)) | + VDPU_REG_PIC_MB_HEIGHT_P(MPEG2_MB_HEIGHT(ctx->dst_fmt.height)) | + VDPU_REG_ALT_SCAN_E(picture->alternate_scan) | + VDPU_REG_TOPFIELDFIRST_E(picture->top_field_first); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(120)); + + reg = VDPU_REG_STRM_START_BIT(slice_params->data_bit_offset) | + VDPU_REG_QSCALE_TYPE(picture->q_scale_type) | + VDPU_REG_CON_MV_E(picture->concealment_motion_vectors) | + VDPU_REG_INTRA_DC_PREC(picture->intra_dc_precision) | + VDPU_REG_INTRA_VLC_TAB(picture->intra_vlc_format) | + VDPU_REG_FRAME_PRED_DCT(picture->frame_pred_frame_dct); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(122)); + + reg = VDPU_REG_ALT_SCAN_FLAG_E(picture->alternate_scan) | + VDPU_REG_FCODE_FWD_HOR(picture->f_code[0][0]) | + VDPU_REG_FCODE_FWD_VER(picture->f_code[0][1]) | + VDPU_REG_FCODE_BWD_HOR(picture->f_code[1][0]) | + VDPU_REG_FCODE_BWD_VER(picture->f_code[1][1]) | + VDPU_REG_MV_ACCURACY_FWD(1) | + VDPU_REG_MV_ACCURACY_BWD(1); + vdpu_write_relaxed(vpu, reg, VDPU_SWREG(136)); + + rk3399_vpu_mpeg2_dec_set_quantization(vpu, ctx); + + rk3399_vpu_mpeg2_dec_set_buffers(vpu, ctx, &src_buf->vb2_buf, + &dst_buf->vb2_buf, + sequence, picture, slice_params); + + /* Controls no longer in-use, we can complete them */ + v4l2_ctrl_request_complete(src_buf->vb2_buf.req_obj.req, + &ctx->ctrl_handler); + + /* Kick the watchdog and start decoding */ + schedule_delayed_work(&vpu->watchdog_work, msecs_to_jiffies(2000)); + + reg = vdpu_read(vpu, VDPU_SWREG(57)) | VDPU_REG_DEC_E(1); + vdpu_write(vpu, reg, VDPU_SWREG(57)); +} diff --git a/drivers/staging/media/rockchip/vpu/rockchip_vpu.h b/drivers/staging/media/rockchip/vpu/rockchip_vpu.h index 1ec2be483e27..3093821440c0 100644 --- a/drivers/staging/media/rockchip/vpu/rockchip_vpu.h +++ b/drivers/staging/media/rockchip/vpu/rockchip_vpu.h @@ -27,6 +27,10 @@ #define ROCKCHIP_VPU_MAX_CLOCKS 4 +#define MPEG2_MB_DIM 16 +#define MPEG2_MB_WIDTH(w) DIV_ROUND_UP(w, MPEG2_MB_DIM) +#define MPEG2_MB_HEIGHT(h) DIV_ROUND_UP(h, MPEG2_MB_DIM) + #define JPEG_MB_DIM 16 #define JPEG_MB_WIDTH(w) DIV_ROUND_UP(w, JPEG_MB_DIM) #define JPEG_MB_HEIGHT(h) DIV_ROUND_UP(h, JPEG_MB_DIM) @@ -34,29 +38,41 @@ struct rockchip_vpu_ctx; struct rockchip_vpu_codec_ops; -#define RK_VPU_CODEC_JPEG BIT(0) +#define RK_VPU_JPEG_ENCODER BIT(0) +#define RK_VPU_ENCODERS 0x0000ffff + +#define RK_VPU_MPEG2_DECODER BIT(16) +#define RK_VPU_DECODERS 0xffff0000 /** * struct rockchip_vpu_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. * @codec: Supported codecs * @codec_ops: Codec ops. * @init: Initialize hardware. * @vepu_irq: encoder interrupt handler + * @vdpu_irq: decoder interrupt handler * @clk_names: array of clock names * @num_clocks: number of clocks in the array */ struct rockchip_vpu_variant { unsigned int enc_offset; + unsigned int dec_offset; const struct rockchip_vpu_fmt *enc_fmts; unsigned int num_enc_fmts; + const struct rockchip_vpu_fmt *dec_fmts; + unsigned int num_dec_fmts; unsigned int codec; const struct rockchip_vpu_codec_ops *codec_ops; int (*init)(struct rockchip_vpu_dev *vpu); irqreturn_t (*vepu_irq)(int irq, void *priv); + irqreturn_t (*vdpu_irq)(int irq, void *priv); const char *clk_names[ROCKCHIP_VPU_MAX_CLOCKS]; int num_clocks; }; @@ -65,24 +81,75 @@ struct rockchip_vpu_variant { * enum rockchip_vpu_codec_mode - codec operating mode. * @RK_VPU_MODE_NONE: No operating mode. Used for RAW video formats. * @RK_VPU_MODE_JPEG_ENC: JPEG encoder. + * @RK_VPU_MODE_MPEG2_DEC: MPEG-2 decoder. */ enum rockchip_vpu_codec_mode { RK_VPU_MODE_NONE = -1, RK_VPU_MODE_JPEG_ENC, + RK_VPU_MODE_MPEG2_DEC, +}; + +/* + * struct rockchip_vpu_ctrl - helper type to declare supported controls + * @id: V4L2 control ID (V4L2_CID_xxx) + * @codec: codec id this control belong to (RK_VPU_JPEG_ENCODER, etc.) + * @cfg: control configuration + */ +struct rockchip_vpu_ctrl { + unsigned int id; + unsigned int codec; + struct v4l2_ctrl_config cfg; +}; + +/* + * struct rockchip_vpu_func - rockchip 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 rockchip_vpu_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 rockchip_vpu_func * +rockchip_vpu_vdev_to_func(struct video_device *vdev) +{ + return container_of(vdev, struct rockchip_vpu_func, vdev); +} + /** * struct rockchip_vpu_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. - * @vfd_enc: Video device for encoder. + * @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. * @base: Mapped address of VPU registers. * @enc_base: Mapped address of VPU encoder register for convenience. + * @dec_base: Mapped address of VPU decoder register for convenience. * @vpu_mutex: Mutex to synchronize V4L2 calls. * @irqlock: Spinlock to synchronize access to data structures * shared with interrupt handlers. @@ -93,12 +160,14 @@ struct rockchip_vpu_dev { struct v4l2_device v4l2_dev; struct v4l2_m2m_dev *m2m_dev; struct media_device mdev; - struct video_device *vfd_enc; + struct rockchip_vpu_func *encoder; + struct rockchip_vpu_func *decoder; struct platform_device *pdev; struct device *dev; struct clk_bulk_data clocks[ROCKCHIP_VPU_MAX_CLOCKS]; void __iomem *base; void __iomem *enc_base; + void __iomem *dec_base; struct mutex vpu_mutex; /* video_device lock */ spinlock_t irqlock; @@ -123,11 +192,12 @@ struct rockchip_vpu_dev { * @ctrl_handler: Control handler used to register controls. * @jpeg_quality: User-specified JPEG compression quality. * + * @buf_finish: Buffer finish. This depends on encoder or decoder + * context, and it's called right before + * calling v4l2_m2m_job_finish. * @codec_ops: Set of operations related to codec mode. - * - * @bounce_dma_addr: Bounce buffer bus address. - * @bounce_buf: Bounce buffer pointer. - * @bounce_size: Bounce buffer size. + * @jpeg_enc: JPEG-encoding context. + * @mpeg2_dec: MPEG-2-decoding context. */ struct rockchip_vpu_ctx { struct rockchip_vpu_dev *dev; @@ -144,11 +214,17 @@ struct rockchip_vpu_ctx { struct v4l2_ctrl_handler ctrl_handler; int jpeg_quality; + int (*buf_finish)(struct rockchip_vpu_ctx *ctx, + struct vb2_buffer *buf, + unsigned int bytesused); + const struct rockchip_vpu_codec_ops *codec_ops; - dma_addr_t bounce_dma_addr; - void *bounce_buf; - size_t bounce_size; + /* Specific for particular codec modes. */ + union { + struct rockchip_vpu_jpeg_enc_hw_ctx jpeg_enc; + struct rockchip_vpu_mpeg2_dec_hw_ctx mpeg2_dec; + }; }; /** @@ -229,4 +305,30 @@ static inline u32 vepu_read(struct rockchip_vpu_dev *vpu, u32 reg) return val; } +static inline void vdpu_write_relaxed(struct rockchip_vpu_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 rockchip_vpu_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 u32 vdpu_read(struct rockchip_vpu_dev *vpu, u32 reg) +{ + u32 val = readl(vpu->dec_base + reg); + + vpu_debug(6, "0x%04x = 0x%08x\n", reg / 4, val); + return val; +} + +bool rockchip_vpu_is_encoder_ctx(const struct rockchip_vpu_ctx *ctx); + +void *rockchip_vpu_get_ctrl(struct rockchip_vpu_ctx *ctx, u32 id); +dma_addr_t rockchip_vpu_get_ref(struct vb2_queue *q, u64 ts); + #endif /* ROCKCHIP_VPU_H_ */ diff --git a/drivers/staging/media/rockchip/vpu/rockchip_vpu_drv.c b/drivers/staging/media/rockchip/vpu/rockchip_vpu_drv.c index 8bbc905b26c8..b94ff97451db 100644 --- a/drivers/staging/media/rockchip/vpu/rockchip_vpu_drv.c +++ b/drivers/staging/media/rockchip/vpu/rockchip_vpu_drv.c @@ -24,7 +24,7 @@ #include <media/videobuf2-core.h> #include <media/videobuf2-vmalloc.h> -#include "rockchip_vpu_common.h" +#include "rockchip_vpu_v4l2.h" #include "rockchip_vpu.h" #include "rockchip_vpu_hw.h" @@ -35,13 +35,66 @@ module_param_named(debug, rockchip_vpu_debug, int, 0644); MODULE_PARM_DESC(debug, "Debug level - higher value produces more verbose messages"); +void *rockchip_vpu_get_ctrl(struct rockchip_vpu_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 rockchip_vpu_get_ref(struct vb2_queue *q, u64 ts) +{ + int index; + + index = vb2_find_timestamp(q, ts, 0); + if (index >= 0) + return vb2_dma_contig_plane_dma_addr(q->bufs[index], 0); + return 0; +} + +static int +rockchip_vpu_enc_buf_finish(struct rockchip_vpu_ctx *ctx, + struct vb2_buffer *buf, + unsigned int bytesused) +{ + size_t avail_size; + + avail_size = vb2_plane_size(buf, 0) - ctx->vpu_dst_fmt->header_size; + if (bytesused > avail_size) + return -EINVAL; + /* + * The bounce buffer is only for the JPEG encoder. + * TODO: Rework the JPEG encoder to eliminate the need + * for a bounce buffer. + */ + if (ctx->jpeg_enc.bounce_buffer.cpu) { + memcpy(vb2_plane_vaddr(buf, 0) + + ctx->vpu_dst_fmt->header_size, + ctx->jpeg_enc.bounce_buffer.cpu, bytesused); + } + buf->planes[0].bytesused = + ctx->vpu_dst_fmt->header_size + bytesused; + return 0; +} + +static int +rockchip_vpu_dec_buf_finish(struct rockchip_vpu_ctx *ctx, + struct vb2_buffer *buf, + unsigned int bytesused) +{ + /* For decoders set bytesused as per the output picture. */ + buf->planes[0].bytesused = ctx->dst_fmt.plane_fmt[0].sizeimage; + return 0; +} + static void rockchip_vpu_job_finish(struct rockchip_vpu_dev *vpu, struct rockchip_vpu_ctx *ctx, unsigned int bytesused, enum vb2_buffer_state result) { struct vb2_v4l2_buffer *src, *dst; - size_t avail_size; + int ret; pm_runtime_mark_last_busy(vpu->dev); pm_runtime_put_autosuspend(vpu->dev); @@ -58,28 +111,11 @@ static void rockchip_vpu_job_finish(struct rockchip_vpu_dev *vpu, src->sequence = ctx->sequence_out++; dst->sequence = ctx->sequence_cap++; - dst->field = src->field; - if (src->flags & V4L2_BUF_FLAG_TIMECODE) - dst->timecode = src->timecode; - dst->vb2_buf.timestamp = src->vb2_buf.timestamp; - dst->flags &= ~(V4L2_BUF_FLAG_TSTAMP_SRC_MASK | - V4L2_BUF_FLAG_TIMECODE); - dst->flags |= src->flags & (V4L2_BUF_FLAG_TSTAMP_SRC_MASK | - V4L2_BUF_FLAG_TIMECODE); - - avail_size = vb2_plane_size(&dst->vb2_buf, 0) - - ctx->vpu_dst_fmt->header_size; - if (bytesused <= avail_size) { - if (ctx->bounce_buf) { - memcpy(vb2_plane_vaddr(&dst->vb2_buf, 0) + - ctx->vpu_dst_fmt->header_size, - ctx->bounce_buf, bytesused); - } - dst->vb2_buf.planes[0].bytesused = - ctx->vpu_dst_fmt->header_size + bytesused; - } else { + v4l2_m2m_buf_copy_metadata(src, dst, true); + + ret = ctx->buf_finish(ctx, &dst->vb2_buf, bytesused); + if (ret) result = VB2_BUF_STATE_ERROR; - } v4l2_m2m_buf_done(src, result); v4l2_m2m_buf_done(dst, result); @@ -137,12 +173,17 @@ err_cancel_job: rockchip_vpu_job_finish(ctx->dev, ctx, 0, VB2_BUF_STATE_ERROR); } +bool rockchip_vpu_is_encoder_ctx(const struct rockchip_vpu_ctx *ctx) +{ + return ctx->buf_finish == rockchip_vpu_enc_buf_finish; +} + static struct v4l2_m2m_ops vpu_m2m_ops = { .device_run = device_run, }; static int -enc_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) +queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) { struct rockchip_vpu_ctx *ctx = priv; int ret; @@ -150,7 +191,7 @@ enc_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) 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 = &rockchip_vpu_enc_queue_ops; + src_vq->ops = &rockchip_vpu_queue_ops; src_vq->mem_ops = &vb2_dma_contig_memops; /* @@ -164,24 +205,32 @@ enc_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) 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; /* - * The CAPTURE queue doesn't need dma memory, - * as the CPU needs to create the JPEG frames, - * from the hardware-produced JPEG payload. + * When encoding, the CAPTURE queue doesn't need dma memory, + * as the CPU needs to create the JPEG frames, from the + * hardware-produced JPEG payload. * - * For the DMA destination buffer, we use - * a bounce buffer. + * For the DMA destination buffer, we use a bounce buffer. */ + if (rockchip_vpu_is_encoder_ctx(ctx)) { + dst_vq->mem_ops = &vb2_vmalloc_memops; + } else { + dst_vq->bidirectional = true; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES | + 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 = &rockchip_vpu_enc_queue_ops; - dst_vq->mem_ops = &vb2_vmalloc_memops; + dst_vq->ops = &rockchip_vpu_queue_ops; dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; dst_vq->lock = &ctx->dev->vpu_mutex; @@ -214,22 +263,63 @@ static const struct v4l2_ctrl_ops rockchip_vpu_ctrl_ops = { .s_ctrl = rockchip_vpu_s_ctrl, }; +static struct rockchip_vpu_ctrl controls[] = { + { + .id = V4L2_CID_JPEG_COMPRESSION_QUALITY, + .codec = RK_VPU_JPEG_ENCODER, + .cfg = { + .min = 5, + .max = 100, + .step = 1, + .def = 50, + }, + }, { + .id = V4L2_CID_MPEG_VIDEO_MPEG2_SLICE_PARAMS, + .codec = RK_VPU_MPEG2_DECODER, + .cfg = { + .elem_size = sizeof(struct v4l2_ctrl_mpeg2_slice_params), + }, + }, { + .id = V4L2_CID_MPEG_VIDEO_MPEG2_QUANTIZATION, + .codec = RK_VPU_MPEG2_DECODER, + .cfg = { + .elem_size = sizeof(struct v4l2_ctrl_mpeg2_quantization), + }, + }, +}; + static int rockchip_vpu_ctrls_setup(struct rockchip_vpu_dev *vpu, - struct rockchip_vpu_ctx *ctx) + struct rockchip_vpu_ctx *ctx, + int allowed_codecs) { - v4l2_ctrl_handler_init(&ctx->ctrl_handler, 1); - if (vpu->variant->codec & RK_VPU_CODEC_JPEG) { - v4l2_ctrl_new_std(&ctx->ctrl_handler, &rockchip_vpu_ctrl_ops, - V4L2_CID_JPEG_COMPRESSION_QUALITY, - 5, 100, 1, 50); + 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; + if (!controls[i].cfg.elem_size) { + v4l2_ctrl_new_std(&ctx->ctrl_handler, + &rockchip_vpu_ctrl_ops, + controls[i].id, controls[i].cfg.min, + controls[i].cfg.max, + controls[i].cfg.step, + controls[i].cfg.def); + } else { + controls[i].cfg.id = controls[i].id; + v4l2_ctrl_new_custom(&ctx->ctrl_handler, + &controls[i].cfg, NULL); + } + if (ctx->ctrl_handler.error) { - vpu_err("Adding JPEG control failed %d\n", + vpu_err("Adding control (%d) failed %d\n", + controls[i].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); } @@ -241,8 +331,9 @@ static int rockchip_vpu_open(struct file *filp) { struct rockchip_vpu_dev *vpu = video_drvdata(filp); struct video_device *vdev = video_devdata(filp); + struct rockchip_vpu_func *func = rockchip_vpu_vdev_to_func(vdev); struct rockchip_vpu_ctx *ctx; - int ret; + int allowed_codecs, ret; /* * We do not need any extra locking here, because we operate only @@ -258,11 +349,19 @@ static int rockchip_vpu_open(struct file *filp) return -ENOMEM; ctx->dev = vpu; - if (vdev == vpu->vfd_enc) + if (func->id == MEDIA_ENT_F_PROC_VIDEO_ENCODER) { + allowed_codecs = vpu->variant->codec & RK_VPU_ENCODERS; + ctx->buf_finish = rockchip_vpu_enc_buf_finish; ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(vpu->m2m_dev, ctx, - &enc_queue_init); - else + queue_init); + } else if (func->id == MEDIA_ENT_F_PROC_VIDEO_DECODER) { + allowed_codecs = vpu->variant->codec & RK_VPU_DECODERS; + ctx->buf_finish = rockchip_vpu_dec_buf_finish; + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(vpu->m2m_dev, ctx, + queue_init); + } else { ctx->fh.m2m_ctx = ERR_PTR(-ENODEV); + } if (IS_ERR(ctx->fh.m2m_ctx)) { ret = PTR_ERR(ctx->fh.m2m_ctx); kfree(ctx); @@ -273,12 +372,9 @@ static int rockchip_vpu_open(struct file *filp) filp->private_data = &ctx->fh; v4l2_fh_add(&ctx->fh); - if (vdev == vpu->vfd_enc) { - rockchip_vpu_enc_reset_dst_fmt(vpu, ctx); - rockchip_vpu_enc_reset_src_fmt(vpu, ctx); - } + rockchip_vpu_reset_fmts(ctx); - ret = rockchip_vpu_ctrls_setup(vpu, ctx); + ret = rockchip_vpu_ctrls_setup(vpu, ctx, allowed_codecs); if (ret) { vpu_err("Failed to set up controls\n"); goto err_fh_free; @@ -328,52 +424,249 @@ static const struct of_device_id of_rockchip_vpu_match[] = { }; MODULE_DEVICE_TABLE(of, of_rockchip_vpu_match); -static int rockchip_vpu_video_device_register(struct rockchip_vpu_dev *vpu) +static int rockchip_vpu_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 rockchip_attach_func(struct rockchip_vpu_dev *vpu, + struct rockchip_vpu_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 = rockchip_vpu_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 = rockchip_vpu_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 = rockchip_vpu_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, 1, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + if (ret) + goto err_rel_entity2; + + ret = media_create_pad_link(&func->proc, 0, &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 rockchip_detach_func(struct rockchip_vpu_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 rockchip_vpu_add_func(struct rockchip_vpu_dev *vpu, + unsigned int funcid) { const struct of_device_id *match; + struct rockchip_vpu_func *func; struct video_device *vfd; - int function, ret; + int ret; match = of_match_node(of_rockchip_vpu_match, vpu->dev->of_node); - vfd = video_device_alloc(); - if (!vfd) { + 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 = &rockchip_vpu_fops; - vfd->release = video_device_release; + 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 = &rockchip_vpu_enc_ioctl_ops; - snprintf(vfd->name, sizeof(vfd->name), "%s-enc", match->compatible); - vpu->vfd_enc = vfd; + vfd->ioctl_ops = &rockchip_vpu_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; + video_set_drvdata(vfd, vpu); ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); if (ret) { v4l2_err(&vpu->v4l2_dev, "Failed to register video device\n"); - goto err_free_dev; + return ret; } - v4l2_info(&vpu->v4l2_dev, "registered as /dev/video%d\n", vfd->num); - function = MEDIA_ENT_F_PROC_VIDEO_ENCODER; - ret = v4l2_m2m_register_media_controller(vpu->m2m_dev, vfd, function); + ret = rockchip_attach_func(vpu, func); if (ret) { - v4l2_err(&vpu->v4l2_dev, "Failed to init mem2mem media controller\n"); - goto err_unreg_video; + v4l2_err(&vpu->v4l2_dev, + "Failed to attach functionality to the media device\n"); + goto err_unreg_dev; } + + v4l2_info(&vpu->v4l2_dev, "registered as /dev/video%d\n", vfd->num); + return 0; -err_unreg_video: +err_unreg_dev: video_unregister_device(vfd); -err_free_dev: - video_device_release(vfd); return ret; } +static int rockchip_vpu_add_enc_func(struct rockchip_vpu_dev *vpu) +{ + if (!vpu->variant->enc_fmts) + return 0; + + return rockchip_vpu_add_func(vpu, MEDIA_ENT_F_PROC_VIDEO_ENCODER); +} + +static int rockchip_vpu_add_dec_func(struct rockchip_vpu_dev *vpu) +{ + if (!vpu->variant->dec_fmts) + return 0; + + return rockchip_vpu_add_func(vpu, MEDIA_ENT_F_PROC_VIDEO_DECODER); +} + +static void rockchip_vpu_remove_func(struct rockchip_vpu_dev *vpu, + unsigned int funcid) +{ + struct rockchip_vpu_func *func; + + if (funcid == MEDIA_ENT_F_PROC_VIDEO_ENCODER) + func = vpu->encoder; + else + func = vpu->decoder; + + if (!func) + return; + + rockchip_detach_func(func); + video_unregister_device(&func->vdev); +} + +static void rockchip_vpu_remove_enc_func(struct rockchip_vpu_dev *vpu) +{ + rockchip_vpu_remove_func(vpu, MEDIA_ENT_F_PROC_VIDEO_ENCODER); +} + +static void rockchip_vpu_remove_dec_func(struct rockchip_vpu_dev *vpu) +{ + rockchip_vpu_remove_func(vpu, MEDIA_ENT_F_PROC_VIDEO_DECODER); +} + +static const struct media_device_ops rockchip_m2m_media_ops = { + .req_validate = vb2_request_validate, + .req_queue = v4l2_m2m_request_queue, +}; + static int rockchip_vpu_probe(struct platform_device *pdev) { const struct of_device_id *match; @@ -407,6 +700,7 @@ static int rockchip_vpu_probe(struct platform_device *pdev) if (IS_ERR(vpu->base)) return PTR_ERR(vpu->base); vpu->enc_base = vpu->base + vpu->variant->enc_offset; + vpu->dec_base = vpu->base + vpu->variant->dec_offset; ret = dma_set_coherent_mask(vpu->dev, DMA_BIT_MASK(32)); if (ret) { @@ -414,6 +708,23 @@ static int rockchip_vpu_probe(struct platform_device *pdev) return ret; } + if (vpu->variant->vdpu_irq) { + int irq; + + irq = platform_get_irq_byname(vpu->pdev, "vdpu"); + if (irq <= 0) { + dev_err(vpu->dev, "Could not get vdpu IRQ.\n"); + return -ENXIO; + } + + ret = devm_request_irq(vpu->dev, irq, vpu->variant->vdpu_irq, + 0, dev_name(vpu->dev), vpu); + if (ret) { + dev_err(vpu->dev, "Could not request vdpu IRQ.\n"); + return ret; + } + } + if (vpu->variant->vepu_irq) { int irq; @@ -466,26 +777,33 @@ static int rockchip_vpu_probe(struct platform_device *pdev) strscpy(vpu->mdev.bus_info, "platform: " DRIVER_NAME, sizeof(vpu->mdev.model)); media_device_init(&vpu->mdev); + vpu->mdev.ops = &rockchip_m2m_media_ops; vpu->v4l2_dev.mdev = &vpu->mdev; - ret = rockchip_vpu_video_device_register(vpu); + ret = rockchip_vpu_add_enc_func(vpu); if (ret) { dev_err(&pdev->dev, "Failed to register encoder\n"); goto err_m2m_rel; } + ret = rockchip_vpu_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_video_dev_unreg; + goto err_rm_dec_func; } + return 0; -err_video_dev_unreg: - if (vpu->vfd_enc) { - v4l2_m2m_unregister_media_controller(vpu->m2m_dev); - video_unregister_device(vpu->vfd_enc); - video_device_release(vpu->vfd_enc); - } + +err_rm_dec_func: + rockchip_vpu_remove_dec_func(vpu); +err_rm_enc_func: + rockchip_vpu_remove_enc_func(vpu); err_m2m_rel: media_device_cleanup(&vpu->mdev); v4l2_m2m_release(vpu->m2m_dev); @@ -505,11 +823,8 @@ static int rockchip_vpu_remove(struct platform_device *pdev) v4l2_info(&vpu->v4l2_dev, "Removing %s\n", pdev->name); media_device_unregister(&vpu->mdev); - if (vpu->vfd_enc) { - v4l2_m2m_unregister_media_controller(vpu->m2m_dev); - video_unregister_device(vpu->vfd_enc); - video_device_release(vpu->vfd_enc); - } + rockchip_vpu_remove_dec_func(vpu); + rockchip_vpu_remove_enc_func(vpu); media_device_cleanup(&vpu->mdev); v4l2_m2m_release(vpu->m2m_dev); v4l2_device_unregister(&vpu->v4l2_dev); diff --git a/drivers/staging/media/rockchip/vpu/rockchip_vpu_enc.c b/drivers/staging/media/rockchip/vpu/rockchip_vpu_enc.c deleted file mode 100644 index dcbfc3cbc9f3..000000000000 --- a/drivers/staging/media/rockchip/vpu/rockchip_vpu_enc.c +++ /dev/null @@ -1,671 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Rockchip 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 <media/videobuf2-core.h> -#include <media/videobuf2-dma-sg.h> - -#include "rockchip_vpu.h" -#include "rockchip_vpu_hw.h" -#include "rockchip_vpu_common.h" - -/** - * struct v4l2_format_info - information about a V4L2 format - * @format: 4CC format identifier (V4L2_PIX_FMT_*) - * @header_size: Size of header, optional and used by compressed formats - * @num_planes: Number of planes (1 to 3) - * @cpp: Number of bytes per pixel (per plane) - * @hsub: Horizontal chroma subsampling factor - * @vsub: Vertical chroma subsampling factor - * @is_compressed: Is it a compressed format? - * @multiplanar: Is it a multiplanar variant format? (e.g. NV12M) - */ -struct rockchip_vpu_v4l2_format_info { - u32 format; - u32 header_size; - u8 num_planes; - u8 cpp[3]; - u8 hsub; - u8 vsub; - u8 is_compressed; - u8 multiplanar; -}; - -static const struct rockchip_vpu_v4l2_format_info * -rockchip_vpu_v4l2_format_info(u32 format) -{ - static const struct rockchip_vpu_v4l2_format_info formats[] = { - { .format = V4L2_PIX_FMT_YUV420M, .num_planes = 3, .cpp = { 1, 1, 1 }, .hsub = 2, .vsub = 2, .multiplanar = 1 }, - { .format = V4L2_PIX_FMT_NV12M, .num_planes = 2, .cpp = { 1, 2, 0 }, .hsub = 2, .vsub = 2, .multiplanar = 1 }, - { .format = V4L2_PIX_FMT_YUYV, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 2, .vsub = 1 }, - { .format = V4L2_PIX_FMT_UYVY, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 2, .vsub = 1 }, - }; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(formats); ++i) { - if (formats[i].format == format) - return &formats[i]; - } - - vpu_err("Unsupported V4L 4CC format (%08x)\n", format); - return NULL; -} - -static void -fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt, - int pixelformat, int width, int height) -{ - const struct rockchip_vpu_v4l2_format_info *info; - struct v4l2_plane_pix_format *plane; - int i; - - info = rockchip_vpu_v4l2_format_info(pixelformat); - if (!info) - return; - - pixfmt->width = width; - pixfmt->height = height; - pixfmt->pixelformat = pixelformat; - - if (!info->multiplanar) { - pixfmt->num_planes = 1; - plane = &pixfmt->plane_fmt[0]; - plane->bytesperline = info->is_compressed ? - 0 : width * info->cpp[0]; - plane->sizeimage = info->header_size; - for (i = 0; i < info->num_planes; i++) { - unsigned int hsub = (i == 0) ? 1 : info->hsub; - unsigned int vsub = (i == 0) ? 1 : info->vsub; - - plane->sizeimage += info->cpp[i] * - DIV_ROUND_UP(width, hsub) * - DIV_ROUND_UP(height, vsub); - } - } else { - pixfmt->num_planes = info->num_planes; - for (i = 0; i < info->num_planes; i++) { - unsigned int hsub = (i == 0) ? 1 : info->hsub; - unsigned int vsub = (i == 0) ? 1 : info->vsub; - - plane = &pixfmt->plane_fmt[i]; - plane->bytesperline = - info->cpp[i] * DIV_ROUND_UP(width, hsub); - plane->sizeimage = - plane->bytesperline * DIV_ROUND_UP(height, vsub); - } - } -} - -static const struct rockchip_vpu_fmt * -rockchip_vpu_find_format(struct rockchip_vpu_ctx *ctx, u32 fourcc) -{ - struct rockchip_vpu_dev *dev = ctx->dev; - const struct rockchip_vpu_fmt *formats; - unsigned int num_fmts, i; - - formats = dev->variant->enc_fmts; - num_fmts = dev->variant->num_enc_fmts; - for (i = 0; i < num_fmts; i++) - if (formats[i].fourcc == fourcc) - return &formats[i]; - return NULL; -} - -static const struct rockchip_vpu_fmt * -rockchip_vpu_get_default_fmt(struct rockchip_vpu_ctx *ctx, bool bitstream) -{ - struct rockchip_vpu_dev *dev = ctx->dev; - const struct rockchip_vpu_fmt *formats; - unsigned int num_fmts, i; - - formats = dev->variant->enc_fmts; - num_fmts = dev->variant->num_enc_fmts; - for (i = 0; i < num_fmts; i++) { - if (bitstream == (formats[i].codec_mode != RK_VPU_MODE_NONE)) - return &formats[i]; - } - return NULL; -} - -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct rockchip_vpu_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 rockchip_vpu_ctx *ctx = fh_to_ctx(priv); - const struct rockchip_vpu_fmt *fmt; - - if (fsize->index != 0) { - vpu_debug(0, "invalid frame size index (expected 0, got %d)\n", - fsize->index); - return -EINVAL; - } - - fmt = rockchip_vpu_find_format(ctx, fsize->pixel_format); - if (!fmt) { - vpu_debug(0, "unsupported bitstream format (%08x)\n", - fsize->pixel_format); - return -EINVAL; - } - - /* This only makes sense for coded formats */ - if (fmt->codec_mode == RK_VPU_MODE_NONE) - return -EINVAL; - - fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; - fsize->stepwise = fmt->frmsize; - - return 0; -} - -static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct rockchip_vpu_dev *dev = video_drvdata(file); - const struct rockchip_vpu_fmt *fmt; - const struct rockchip_vpu_fmt *formats; - int num_fmts, i, j = 0; - - formats = dev->variant->enc_fmts; - num_fmts = dev->variant->num_enc_fmts; - for (i = 0; i < num_fmts; i++) { - /* Skip uncompressed formats */ - if (formats[i].codec_mode == RK_VPU_MODE_NONE) - continue; - if (j == f->index) { - fmt = &formats[i]; - f->pixelformat = fmt->fourcc; - return 0; - } - ++j; - } - return -EINVAL; -} - -static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct rockchip_vpu_dev *dev = video_drvdata(file); - const struct rockchip_vpu_fmt *formats; - const struct rockchip_vpu_fmt *fmt; - int num_fmts, i, j = 0; - - formats = dev->variant->enc_fmts; - num_fmts = dev->variant->num_enc_fmts; - for (i = 0; i < num_fmts; i++) { - if (formats[i].codec_mode != RK_VPU_MODE_NONE) - continue; - if (j == f->index) { - fmt = &formats[i]; - f->pixelformat = fmt->fourcc; - return 0; - } - ++j; - } - return -EINVAL; -} - -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 rockchip_vpu_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 rockchip_vpu_ctx *ctx = fh_to_ctx(priv); - - vpu_debug(4, "f->type = %d\n", f->type); - - *pix_mp = ctx->dst_fmt; - - return 0; -} - -static int -vidioc_try_fmt_cap_mplane(struct file *file, void *priv, struct v4l2_format *f) -{ - struct rockchip_vpu_ctx *ctx = fh_to_ctx(priv); - struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; - const struct rockchip_vpu_fmt *fmt; - - vpu_debug(4, "%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 = rockchip_vpu_find_format(ctx, pix_mp->pixelformat); - if (!fmt) { - fmt = rockchip_vpu_get_default_fmt(ctx, true); - f->fmt.pix.pixelformat = fmt->fourcc; - } - - pix_mp->num_planes = 1; - pix_mp->field = V4L2_FIELD_NONE; - pix_mp->width = clamp(pix_mp->width, - fmt->frmsize.min_width, - fmt->frmsize.max_width); - pix_mp->height = clamp(pix_mp->height, - fmt->frmsize.min_height, - fmt->frmsize.max_height); - /* Round up to macroblocks. */ - pix_mp->width = round_up(pix_mp->width, JPEG_MB_DIM); - pix_mp->height = round_up(pix_mp->height, JPEG_MB_DIM); - - /* - * For compressed formats the application can specify - * sizeimage. If the application passes a zero sizeimage, - * let's default to the maximum frame size. - */ - if (!pix_mp->plane_fmt[0].sizeimage) - pix_mp->plane_fmt[0].sizeimage = fmt->header_size + - pix_mp->width * pix_mp->height * fmt->max_depth; - memset(pix_mp->plane_fmt[0].reserved, 0, - sizeof(pix_mp->plane_fmt[0].reserved)); - return 0; -} - -static int -vidioc_try_fmt_out_mplane(struct file *file, void *priv, struct v4l2_format *f) -{ - struct rockchip_vpu_ctx *ctx = fh_to_ctx(priv); - struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; - const struct rockchip_vpu_fmt *fmt; - unsigned int width, height; - int i; - - vpu_debug(4, "%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 = rockchip_vpu_find_format(ctx, pix_mp->pixelformat); - if (!fmt) { - fmt = rockchip_vpu_get_default_fmt(ctx, false); - f->fmt.pix.pixelformat = fmt->fourcc; - } - - pix_mp->field = V4L2_FIELD_NONE; - width = clamp(pix_mp->width, - ctx->vpu_dst_fmt->frmsize.min_width, - ctx->vpu_dst_fmt->frmsize.max_width); - height = clamp(pix_mp->height, - ctx->vpu_dst_fmt->frmsize.min_height, - ctx->vpu_dst_fmt->frmsize.max_height); - /* Round up to macroblocks. */ - width = round_up(width, JPEG_MB_DIM); - height = round_up(height, JPEG_MB_DIM); - - /* Fill remaining fields */ - fill_pixfmt_mp(pix_mp, fmt->fourcc, width, height); - - for (i = 0; i < pix_mp->num_planes; i++) { - memset(pix_mp->plane_fmt[i].reserved, 0, - sizeof(pix_mp->plane_fmt[i].reserved)); - } - return 0; -} - -void rockchip_vpu_enc_reset_dst_fmt(struct rockchip_vpu_dev *vpu, - struct rockchip_vpu_ctx *ctx) -{ - struct v4l2_pix_format_mplane *fmt = &ctx->dst_fmt; - - ctx->vpu_dst_fmt = rockchip_vpu_get_default_fmt(ctx, true); - - memset(fmt, 0, sizeof(*fmt)); - - fmt->num_planes = 1; - fmt->width = clamp(fmt->width, ctx->vpu_dst_fmt->frmsize.min_width, - ctx->vpu_dst_fmt->frmsize.max_width); - fmt->height = clamp(fmt->height, ctx->vpu_dst_fmt->frmsize.min_height, - ctx->vpu_dst_fmt->frmsize.max_height); - fmt->pixelformat = ctx->vpu_dst_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; - - fmt->plane_fmt[0].sizeimage = ctx->vpu_dst_fmt->header_size + - fmt->width * fmt->height * ctx->vpu_dst_fmt->max_depth; -} - -void rockchip_vpu_enc_reset_src_fmt(struct rockchip_vpu_dev *vpu, - struct rockchip_vpu_ctx *ctx) -{ - struct v4l2_pix_format_mplane *fmt = &ctx->src_fmt; - unsigned int width, height; - - ctx->vpu_src_fmt = rockchip_vpu_get_default_fmt(ctx, false); - - memset(fmt, 0, sizeof(*fmt)); - - width = clamp(fmt->width, ctx->vpu_dst_fmt->frmsize.min_width, - ctx->vpu_dst_fmt->frmsize.max_width); - height = clamp(fmt->height, ctx->vpu_dst_fmt->frmsize.min_height, - ctx->vpu_dst_fmt->frmsize.max_height); - 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; - - fill_pixfmt_mp(fmt, ctx->vpu_src_fmt->fourcc, width, height); -} - -static int -vidioc_s_fmt_out_mplane(struct file *file, void *priv, struct v4l2_format *f) -{ - struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; - struct rockchip_vpu_ctx *ctx = fh_to_ctx(priv); - struct vb2_queue *vq; - int ret; - - /* Change not allowed if queue is streaming. */ - vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); - if (vb2_is_streaming(vq)) - return -EBUSY; - - ret = vidioc_try_fmt_out_mplane(file, priv, f); - if (ret) - return ret; - - ctx->vpu_src_fmt = rockchip_vpu_find_format(ctx, pix_mp->pixelformat); - ctx->src_fmt = *pix_mp; - - /* Propagate to the CAPTURE format */ - 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; - ctx->dst_fmt.width = pix_mp->width; - ctx->dst_fmt.height = pix_mp->height; - - vpu_debug(0, "OUTPUT codec mode: %d\n", ctx->vpu_src_fmt->codec_mode); - vpu_debug(0, "fmt - w: %d, h: %d, mb - w: %d, h: %d\n", - pix_mp->width, pix_mp->height, - JPEG_MB_WIDTH(pix_mp->width), - JPEG_MB_HEIGHT(pix_mp->height)); - return 0; -} - -static int -vidioc_s_fmt_cap_mplane(struct file *file, void *priv, struct v4l2_format *f) -{ - struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; - struct rockchip_vpu_ctx *ctx = fh_to_ctx(priv); - struct rockchip_vpu_dev *vpu = ctx->dev; - struct vb2_queue *vq, *peer_vq; - int ret; - - /* Change not allowed if queue is streaming. */ - vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); - if (vb2_is_streaming(vq)) - return -EBUSY; - - /* - * 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 = vidioc_try_fmt_cap_mplane(file, priv, f); - if (ret) - return ret; - - ctx->vpu_dst_fmt = rockchip_vpu_find_format(ctx, pix_mp->pixelformat); - ctx->dst_fmt = *pix_mp; - - vpu_debug(0, "CAPTURE codec mode: %d\n", ctx->vpu_dst_fmt->codec_mode); - vpu_debug(0, "fmt - w: %d, h: %d, mb - w: %d, h: %d\n", - pix_mp->width, pix_mp->height, - JPEG_MB_WIDTH(pix_mp->width), - JPEG_MB_HEIGHT(pix_mp->height)); - - /* - * 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. - */ - rockchip_vpu_enc_reset_src_fmt(vpu, ctx); - return 0; -} - -const struct v4l2_ioctl_ops rockchip_vpu_enc_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_mplane = vidioc_enum_fmt_vid_out_mplane, - .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane, - - .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, -}; - -static int -rockchip_vpu_queue_setup(struct vb2_queue *vq, - unsigned int *num_buffers, - unsigned int *num_planes, - unsigned int sizes[], - struct device *alloc_devs[]) -{ - struct rockchip_vpu_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 rockchip_vpu_buf_prepare(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - struct vb2_queue *vq = vb->vb2_queue; - struct rockchip_vpu_ctx *ctx = vb2_get_drv_priv(vq); - struct v4l2_pix_format_mplane *pixfmt; - unsigned int sz; - int ret = 0; - 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; - - if (vbuf->field == V4L2_FIELD_ANY) - vbuf->field = V4L2_FIELD_NONE; - if (vbuf->field != V4L2_FIELD_NONE) { - vpu_debug(4, "field %d not supported\n", - vbuf->field); - return -EINVAL; - } - break; - default: - vpu_err("invalid queue type: %d\n", vq->type); - return -EINVAL; - } - - 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\n", i); - ret = -EINVAL; - break; - } - } - - return ret; -} - -static void rockchip_vpu_buf_queue(struct vb2_buffer *vb) -{ - struct rockchip_vpu_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - - v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); -} - -static int rockchip_vpu_start_streaming(struct vb2_queue *q, unsigned int count) -{ - struct rockchip_vpu_ctx *ctx = vb2_get_drv_priv(q); - enum rockchip_vpu_codec_mode codec_mode; - - if (V4L2_TYPE_IS_OUTPUT(q->type)) - ctx->sequence_out = 0; - else - ctx->sequence_cap = 0; - - /* Set codec_ops for the chosen destination format */ - 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]; - - /* A bounce buffer is needed for the JPEG payload */ - if (!V4L2_TYPE_IS_OUTPUT(q->type)) { - ctx->bounce_size = ctx->dst_fmt.plane_fmt[0].sizeimage - - ctx->vpu_dst_fmt->header_size; - ctx->bounce_buf = dma_alloc_attrs(ctx->dev->dev, - ctx->bounce_size, - &ctx->bounce_dma_addr, - GFP_KERNEL, - DMA_ATTR_ALLOC_SINGLE_PAGES); - } - return 0; -} - -static void rockchip_vpu_stop_streaming(struct vb2_queue *q) -{ - struct rockchip_vpu_ctx *ctx = vb2_get_drv_priv(q); - - if (!V4L2_TYPE_IS_OUTPUT(q->type)) - dma_free_attrs(ctx->dev->dev, - ctx->bounce_size, - ctx->bounce_buf, - ctx->bounce_dma_addr, - DMA_ATTR_ALLOC_SINGLE_PAGES); - - /* - * 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. - */ - for (;;) { - struct vb2_v4l2_buffer *vbuf; - - if (V4L2_TYPE_IS_OUTPUT(q->type)) - vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); - else - vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); - if (!vbuf) - break; - v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR); - } -} - -const struct vb2_ops rockchip_vpu_enc_queue_ops = { - .queue_setup = rockchip_vpu_queue_setup, - .buf_prepare = rockchip_vpu_buf_prepare, - .buf_queue = rockchip_vpu_buf_queue, - .start_streaming = rockchip_vpu_start_streaming, - .stop_streaming = rockchip_vpu_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; diff --git a/drivers/staging/media/rockchip/vpu/rockchip_vpu_hw.h b/drivers/staging/media/rockchip/vpu/rockchip_vpu_hw.h index 2b955da1be1a..6cecb528f994 100644 --- a/drivers/staging/media/rockchip/vpu/rockchip_vpu_hw.h +++ b/drivers/staging/media/rockchip/vpu/rockchip_vpu_hw.h @@ -11,6 +11,7 @@ #include <linux/interrupt.h> #include <linux/v4l2-controls.h> +#include <media/mpeg2-ctrls.h> #include <media/videobuf2-core.h> struct rockchip_vpu_dev; @@ -19,8 +20,40 @@ struct rockchip_vpu_buf; struct rockchip_vpu_variant; /** + * struct rockchip_vpu_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. + */ +struct rockchip_vpu_aux_buf { + void *cpu; + dma_addr_t dma; + size_t size; +}; + +/** + * struct rockchip_vpu_jpeg_enc_hw_ctx + * @bounce_buffer: Bounce buffer + */ +struct rockchip_vpu_jpeg_enc_hw_ctx { + struct rockchip_vpu_aux_buf bounce_buffer; +}; + +/** + * struct rockchip_vpu_mpeg2_dec_hw_ctx + * @qtable: Quantization table + */ +struct rockchip_vpu_mpeg2_dec_hw_ctx { + struct rockchip_vpu_aux_buf qtable; +}; + +/** * struct rockchip_vpu_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. @@ -28,6 +61,8 @@ struct rockchip_vpu_variant; * @reset: Reset the hardware in case of a timeout. */ struct rockchip_vpu_codec_ops { + int (*init)(struct rockchip_vpu_ctx *ctx); + void (*exit)(struct rockchip_vpu_ctx *ctx); void (*run)(struct rockchip_vpu_ctx *ctx); void (*done)(struct rockchip_vpu_ctx *ctx, enum vb2_buffer_state); void (*reset)(struct rockchip_vpu_ctx *ctx); @@ -54,5 +89,14 @@ void rockchip_vpu_irq_done(struct rockchip_vpu_dev *vpu, void rk3288_vpu_jpeg_enc_run(struct rockchip_vpu_ctx *ctx); void rk3399_vpu_jpeg_enc_run(struct rockchip_vpu_ctx *ctx); +int rockchip_vpu_jpeg_enc_init(struct rockchip_vpu_ctx *ctx); +void rockchip_vpu_jpeg_enc_exit(struct rockchip_vpu_ctx *ctx); + +void rk3288_vpu_mpeg2_dec_run(struct rockchip_vpu_ctx *ctx); +void rk3399_vpu_mpeg2_dec_run(struct rockchip_vpu_ctx *ctx); +void rockchip_vpu_mpeg2_dec_copy_qtable(u8 *qtable, + const struct v4l2_ctrl_mpeg2_quantization *ctrl); +int rockchip_vpu_mpeg2_dec_init(struct rockchip_vpu_ctx *ctx); +void rockchip_vpu_mpeg2_dec_exit(struct rockchip_vpu_ctx *ctx); #endif /* ROCKCHIP_VPU_HW_H_ */ diff --git a/drivers/staging/media/rockchip/vpu/rockchip_vpu_jpeg.c b/drivers/staging/media/rockchip/vpu/rockchip_vpu_jpeg.c index 0ff0badc1f7a..30b97d207dc5 100644 --- a/drivers/staging/media/rockchip/vpu/rockchip_vpu_jpeg.c +++ b/drivers/staging/media/rockchip/vpu/rockchip_vpu_jpeg.c @@ -6,9 +6,11 @@ * Copyright (C) Jean-Francois Moine (http://moinejf.free.fr) * Copyright (C) 2014 Philipp Zabel, Pengutronix */ +#include <linux/dma-mapping.h> #include <linux/kernel.h> #include <linux/string.h> #include "rockchip_vpu_jpeg.h" +#include "rockchip_vpu.h" #define LUMA_QUANT_OFF 7 #define CHROMA_QUANT_OFF 72 @@ -288,3 +290,30 @@ void rockchip_vpu_jpeg_header_assemble(struct rockchip_vpu_jpeg_ctx *ctx) jpeg_set_quality(buf, ctx->quality); } + +int rockchip_vpu_jpeg_enc_init(struct rockchip_vpu_ctx *ctx) +{ + ctx->jpeg_enc.bounce_buffer.size = + ctx->dst_fmt.plane_fmt[0].sizeimage - + ctx->vpu_dst_fmt->header_size; + + ctx->jpeg_enc.bounce_buffer.cpu = + dma_alloc_attrs(ctx->dev->dev, + ctx->jpeg_enc.bounce_buffer.size, + &ctx->jpeg_enc.bounce_buffer.dma, + GFP_KERNEL, + DMA_ATTR_ALLOC_SINGLE_PAGES); + if (!ctx->jpeg_enc.bounce_buffer.cpu) + return -ENOMEM; + + return 0; +} + +void rockchip_vpu_jpeg_enc_exit(struct rockchip_vpu_ctx *ctx) +{ + dma_free_attrs(ctx->dev->dev, + ctx->jpeg_enc.bounce_buffer.size, + ctx->jpeg_enc.bounce_buffer.cpu, + ctx->jpeg_enc.bounce_buffer.dma, + DMA_ATTR_ALLOC_SINGLE_PAGES); +} diff --git a/drivers/staging/media/rockchip/vpu/rockchip_vpu_mpeg2.c b/drivers/staging/media/rockchip/vpu/rockchip_vpu_mpeg2.c new file mode 100644 index 000000000000..5a5b9ea1f6b5 --- /dev/null +++ b/drivers/staging/media/rockchip/vpu/rockchip_vpu_mpeg2.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip VPU codec driver + * + * Copyright (C) 2018 Rockchip Electronics Co., Ltd. + */ + +#include "rockchip_vpu.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 rockchip_vpu_mpeg2_dec_copy_qtable(u8 *qtable, + const struct v4l2_ctrl_mpeg2_quantization *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 rockchip_vpu_mpeg2_dec_init(struct rockchip_vpu_ctx *ctx) +{ + struct rockchip_vpu_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 rockchip_vpu_mpeg2_dec_exit(struct rockchip_vpu_ctx *ctx) +{ + struct rockchip_vpu_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/rockchip/vpu/rockchip_vpu_v4l2.c b/drivers/staging/media/rockchip/vpu/rockchip_vpu_v4l2.c new file mode 100644 index 000000000000..8bc709ab13be --- /dev/null +++ b/drivers/staging/media/rockchip/vpu/rockchip_vpu_v4l2.c @@ -0,0 +1,692 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip 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 <media/videobuf2-core.h> +#include <media/videobuf2-dma-sg.h> + +#include "rockchip_vpu.h" +#include "rockchip_vpu_hw.h" +#include "rockchip_vpu_v4l2.h" + +static const struct rockchip_vpu_fmt * +rockchip_vpu_get_formats(const struct rockchip_vpu_ctx *ctx, + unsigned int *num_fmts) +{ + const struct rockchip_vpu_fmt *formats; + + if (rockchip_vpu_is_encoder_ctx(ctx)) { + 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 rockchip_vpu_fmt * +rockchip_vpu_find_format(const struct rockchip_vpu_fmt *formats, + unsigned int num_fmts, u32 fourcc) +{ + unsigned int i; + + for (i = 0; i < num_fmts; i++) + if (formats[i].fourcc == fourcc) + return &formats[i]; + return NULL; +} + +static const struct rockchip_vpu_fmt * +rockchip_vpu_get_default_fmt(const struct rockchip_vpu_fmt *formats, + unsigned int num_fmts, bool bitstream) +{ + unsigned int i; + + for (i = 0; i < num_fmts; i++) { + if (bitstream == (formats[i].codec_mode != RK_VPU_MODE_NONE)) + return &formats[i]; + } + return NULL; +} + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct rockchip_vpu_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 rockchip_vpu_ctx *ctx = fh_to_ctx(priv); + const struct rockchip_vpu_fmt *formats, *fmt; + unsigned int num_fmts; + + if (fsize->index != 0) { + vpu_debug(0, "invalid frame size index (expected 0, got %d)\n", + fsize->index); + return -EINVAL; + } + + formats = rockchip_vpu_get_formats(ctx, &num_fmts); + fmt = rockchip_vpu_find_format(formats, num_fmts, fsize->pixel_format); + if (!fmt) { + vpu_debug(0, "unsupported bitstream format (%08x)\n", + fsize->pixel_format); + return -EINVAL; + } + + /* This only makes sense for coded formats */ + if (fmt->codec_mode == RK_VPU_MODE_NONE) + 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 rockchip_vpu_ctx *ctx = fh_to_ctx(priv); + const struct rockchip_vpu_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 == rockchip_vpu_is_encoder_ctx(ctx); + + formats = rockchip_vpu_get_formats(ctx, &num_fmts); + for (i = 0; i < num_fmts; i++) { + bool mode_none = formats[i].codec_mode == RK_VPU_MODE_NONE; + + if (skip_mode_none == mode_none) + continue; + if (j == f->index) { + fmt = &formats[i]; + 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 rockchip_vpu_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 rockchip_vpu_ctx *ctx = fh_to_ctx(priv); + + vpu_debug(4, "f->type = %d\n", f->type); + + *pix_mp = ctx->dst_fmt; + + return 0; +} + +static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f, + bool capture) +{ + struct rockchip_vpu_ctx *ctx = fh_to_ctx(priv); + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + const struct rockchip_vpu_fmt *formats, *fmt, *vpu_fmt; + unsigned int num_fmts; + bool coded; + + coded = capture == rockchip_vpu_is_encoder_ctx(ctx); + + 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); + + formats = rockchip_vpu_get_formats(ctx, &num_fmts); + fmt = rockchip_vpu_find_format(formats, num_fmts, pix_mp->pixelformat); + if (!fmt) { + fmt = rockchip_vpu_get_default_fmt(formats, num_fmts, coded); + f->fmt.pix_mp.pixelformat = fmt->fourcc; + } + + if (coded) { + pix_mp->num_planes = 1; + vpu_fmt = fmt; + } else if (rockchip_vpu_is_encoder_ctx(ctx)) { + vpu_fmt = ctx->vpu_dst_fmt; + } else { + vpu_fmt = ctx->vpu_src_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); + } 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 vidioc_try_fmt(file, priv, f, true); +} + +static int vidioc_try_fmt_out_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + return vidioc_try_fmt(file, priv, f, false); +} + +static void +rockchip_vpu_reset_fmt(struct v4l2_pix_format_mplane *fmt, + const struct rockchip_vpu_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 +rockchip_vpu_reset_encoded_fmt(struct rockchip_vpu_ctx *ctx) +{ + const struct rockchip_vpu_fmt *vpu_fmt, *formats; + struct v4l2_pix_format_mplane *fmt; + unsigned int num_fmts; + + formats = rockchip_vpu_get_formats(ctx, &num_fmts); + vpu_fmt = rockchip_vpu_get_default_fmt(formats, num_fmts, true); + + if (rockchip_vpu_is_encoder_ctx(ctx)) { + ctx->vpu_dst_fmt = vpu_fmt; + fmt = &ctx->dst_fmt; + } else { + ctx->vpu_src_fmt = vpu_fmt; + fmt = &ctx->src_fmt; + } + + rockchip_vpu_reset_fmt(fmt, vpu_fmt); + fmt->num_planes = 1; + fmt->width = vpu_fmt->frmsize.min_width; + fmt->height = vpu_fmt->frmsize.min_height; + fmt->plane_fmt[0].sizeimage = vpu_fmt->header_size + + fmt->width * fmt->height * vpu_fmt->max_depth; +} + +static void +rockchip_vpu_reset_raw_fmt(struct rockchip_vpu_ctx *ctx) +{ + const struct rockchip_vpu_fmt *raw_vpu_fmt, *formats; + struct v4l2_pix_format_mplane *raw_fmt, *encoded_fmt; + unsigned int num_fmts; + + formats = rockchip_vpu_get_formats(ctx, &num_fmts); + raw_vpu_fmt = rockchip_vpu_get_default_fmt(formats, num_fmts, false); + + if (rockchip_vpu_is_encoder_ctx(ctx)) { + 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; + } + + rockchip_vpu_reset_fmt(raw_fmt, raw_vpu_fmt); + v4l2_fill_pixfmt_mp(raw_fmt, raw_vpu_fmt->fourcc, + encoded_fmt->width, + encoded_fmt->height); +} + +void rockchip_vpu_reset_fmts(struct rockchip_vpu_ctx *ctx) +{ + rockchip_vpu_reset_encoded_fmt(ctx); + rockchip_vpu_reset_raw_fmt(ctx); +} + +static void +rockchip_vpu_update_requires_request(struct rockchip_vpu_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: + ctx->fh.m2m_ctx->out_q_ctx.q.requires_requests = true; + break; + default: + break; + } +} + +static int +vidioc_s_fmt_out_mplane(struct file *file, void *priv, struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + struct rockchip_vpu_ctx *ctx = fh_to_ctx(priv); + const struct rockchip_vpu_fmt *formats; + unsigned int num_fmts; + struct vb2_queue *vq; + int ret; + + /* Change not allowed if queue is busy. */ + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (vb2_is_busy(vq)) + return -EBUSY; + + if (!rockchip_vpu_is_encoder_ctx(ctx)) { + struct vb2_queue *peer_vq; + + /* + * 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; + } + + ret = vidioc_try_fmt_out_mplane(file, priv, f); + if (ret) + return ret; + + formats = rockchip_vpu_get_formats(ctx, &num_fmts); + ctx->vpu_src_fmt = rockchip_vpu_find_format(formats, num_fmts, + 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 rockchip_vpu_reset_raw_fmt() also propagates size + * changes to the raw format. + */ + if (!rockchip_vpu_is_encoder_ctx(ctx)) + rockchip_vpu_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; + + rockchip_vpu_update_requires_request(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 vidioc_s_fmt_cap_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + struct rockchip_vpu_ctx *ctx = fh_to_ctx(priv); + const struct rockchip_vpu_fmt *formats; + struct vb2_queue *vq; + unsigned int num_fmts; + int ret; + + /* Change not allowed if queue is busy. */ + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (vb2_is_busy(vq)) + return -EBUSY; + + if (rockchip_vpu_is_encoder_ctx(ctx)) { + 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 = vidioc_try_fmt_cap_mplane(file, priv, f); + if (ret) + return ret; + + formats = rockchip_vpu_get_formats(ctx, &num_fmts); + ctx->vpu_dst_fmt = rockchip_vpu_find_format(formats, num_fmts, + 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 rockchip_vpu_reset_raw_fmt() also propagates size + * changes to the raw format. + */ + if (rockchip_vpu_is_encoder_ctx(ctx)) + rockchip_vpu_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); + + rockchip_vpu_update_requires_request(ctx, pix_mp->pixelformat); + + return 0; +} + +const struct v4l2_ioctl_ops rockchip_vpu_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, +}; + +static int +rockchip_vpu_queue_setup(struct vb2_queue *vq, + unsigned int *num_buffers, + unsigned int *num_planes, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct rockchip_vpu_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 +rockchip_vpu_buf_plane_check(struct vb2_buffer *vb, + const struct rockchip_vpu_fmt *vpu_fmt, + 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 rockchip_vpu_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct rockchip_vpu_ctx *ctx = vb2_get_drv_priv(vq); + + if (V4L2_TYPE_IS_OUTPUT(vq->type)) + return rockchip_vpu_buf_plane_check(vb, ctx->vpu_src_fmt, + &ctx->src_fmt); + + return rockchip_vpu_buf_plane_check(vb, ctx->vpu_dst_fmt, + &ctx->dst_fmt); +} + +static void rockchip_vpu_buf_queue(struct vb2_buffer *vb) +{ + struct rockchip_vpu_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); +} + +static bool rockchip_vpu_vq_is_coded(struct vb2_queue *q) +{ + struct rockchip_vpu_ctx *ctx = vb2_get_drv_priv(q); + + return rockchip_vpu_is_encoder_ctx(ctx) != V4L2_TYPE_IS_OUTPUT(q->type); +} + +static int rockchip_vpu_start_streaming(struct vb2_queue *q, + unsigned int count) +{ + struct rockchip_vpu_ctx *ctx = vb2_get_drv_priv(q); + int ret = 0; + + if (V4L2_TYPE_IS_OUTPUT(q->type)) + ctx->sequence_out = 0; + else + ctx->sequence_cap = 0; + + if (rockchip_vpu_vq_is_coded(q)) { + enum rockchip_vpu_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 && ctx->codec_ops->init) + ret = ctx->codec_ops->init(ctx); + } + + return ret; +} + +static void +rockchip_vpu_return_bufs(struct vb2_queue *q, + struct vb2_v4l2_buffer *(*buf_remove)(struct v4l2_m2m_ctx *)) +{ + struct rockchip_vpu_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 rockchip_vpu_stop_streaming(struct vb2_queue *q) +{ + struct rockchip_vpu_ctx *ctx = vb2_get_drv_priv(q); + + if (rockchip_vpu_vq_is_coded(q)) { + 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)) + rockchip_vpu_return_bufs(q, v4l2_m2m_src_buf_remove); + else + rockchip_vpu_return_bufs(q, v4l2_m2m_dst_buf_remove); +} + +static void rockchip_vpu_buf_request_complete(struct vb2_buffer *vb) +{ + struct rockchip_vpu_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + + v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->ctrl_handler); +} + +static int rockchip_vpu_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 rockchip_vpu_queue_ops = { + .queue_setup = rockchip_vpu_queue_setup, + .buf_prepare = rockchip_vpu_buf_prepare, + .buf_queue = rockchip_vpu_buf_queue, + .buf_out_validate = rockchip_vpu_buf_out_validate, + .buf_request_complete = rockchip_vpu_buf_request_complete, + .start_streaming = rockchip_vpu_start_streaming, + .stop_streaming = rockchip_vpu_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; diff --git a/drivers/staging/media/rockchip/vpu/rockchip_vpu_common.h b/drivers/staging/media/rockchip/vpu/rockchip_vpu_v4l2.h index ca77668d9579..493e8751d22d 100644 --- a/drivers/staging/media/rockchip/vpu/rockchip_vpu_common.h +++ b/drivers/staging/media/rockchip/vpu/rockchip_vpu_v4l2.h @@ -13,17 +13,14 @@ * Copyright (C) 2011 Samsung Electronics Co., Ltd. */ -#ifndef ROCKCHIP_VPU_COMMON_H_ -#define ROCKCHIP_VPU_COMMON_H_ +#ifndef ROCKCHIP_VPU_V4L2_H_ +#define ROCKCHIP_VPU_V4L2_H_ #include "rockchip_vpu.h" -extern const struct v4l2_ioctl_ops rockchip_vpu_enc_ioctl_ops; -extern const struct vb2_ops rockchip_vpu_enc_queue_ops; +extern const struct v4l2_ioctl_ops rockchip_vpu_ioctl_ops; +extern const struct vb2_ops rockchip_vpu_queue_ops; -void rockchip_vpu_enc_reset_src_fmt(struct rockchip_vpu_dev *vpu, - struct rockchip_vpu_ctx *ctx); -void rockchip_vpu_enc_reset_dst_fmt(struct rockchip_vpu_dev *vpu, - struct rockchip_vpu_ctx *ctx); +void rockchip_vpu_reset_fmts(struct rockchip_vpu_ctx *ctx); -#endif /* ROCKCHIP_VPU_COMMON_H_ */ +#endif /* ROCKCHIP_VPU_V4L2_H_ */ diff --git a/drivers/staging/media/sunxi/cedrus/Makefile b/drivers/staging/media/sunxi/cedrus/Makefile index 808842f0119e..c85ac6db0302 100644 --- a/drivers/staging/media/sunxi/cedrus/Makefile +++ b/drivers/staging/media/sunxi/cedrus/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_VIDEO_SUNXI_CEDRUS) += sunxi-cedrus.o -sunxi-cedrus-y = cedrus.o cedrus_video.o cedrus_hw.o cedrus_dec.o cedrus_mpeg2.o +sunxi-cedrus-y = cedrus.o cedrus_video.o cedrus_hw.o cedrus_dec.o \ + cedrus_mpeg2.o cedrus_h264.o diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c index d0429c0e6b6b..370937edfc14 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus.c @@ -40,6 +40,36 @@ static const struct cedrus_control cedrus_controls[] = { .codec = CEDRUS_CODEC_MPEG2, .required = false, }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_DECODE_PARAMS, + .elem_size = sizeof(struct v4l2_ctrl_h264_decode_params), + .codec = CEDRUS_CODEC_H264, + .required = true, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_SLICE_PARAMS, + .elem_size = sizeof(struct v4l2_ctrl_h264_slice_params), + .codec = CEDRUS_CODEC_H264, + .required = true, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_SPS, + .elem_size = sizeof(struct v4l2_ctrl_h264_sps), + .codec = CEDRUS_CODEC_H264, + .required = true, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_PPS, + .elem_size = sizeof(struct v4l2_ctrl_h264_pps), + .codec = CEDRUS_CODEC_H264, + .required = true, + }, + { + .id = V4L2_CID_MPEG_VIDEO_H264_SCALING_MATRIX, + .elem_size = sizeof(struct v4l2_ctrl_h264_scaling_matrix), + .codec = CEDRUS_CODEC_H264, + .required = true, + }, }; #define CEDRUS_CONTROLS_COUNT ARRAY_SIZE(cedrus_controls) @@ -278,6 +308,7 @@ static int cedrus_probe(struct platform_device *pdev) } dev->dec_ops[CEDRUS_CODEC_MPEG2] = &cedrus_dec_ops_mpeg2; + dev->dec_ops[CEDRUS_CODEC_H264] = &cedrus_dec_ops_h264; mutex_init(&dev->dev_mutex); @@ -369,36 +400,41 @@ static int cedrus_remove(struct platform_device *pdev) } static const struct cedrus_variant sun4i_a10_cedrus_variant = { - /* No particular capability. */ + .mod_rate = 320000000, }; static const struct cedrus_variant sun5i_a13_cedrus_variant = { - /* No particular capability. */ + .mod_rate = 320000000, }; static const struct cedrus_variant sun7i_a20_cedrus_variant = { - /* No particular capability. */ + .mod_rate = 320000000, }; static const struct cedrus_variant sun8i_a33_cedrus_variant = { .capabilities = CEDRUS_CAPABILITY_UNTILED, + .mod_rate = 320000000, }; static const struct cedrus_variant sun8i_h3_cedrus_variant = { .capabilities = CEDRUS_CAPABILITY_UNTILED, + .mod_rate = 402000000, }; static const struct cedrus_variant sun50i_a64_cedrus_variant = { .capabilities = CEDRUS_CAPABILITY_UNTILED, + .mod_rate = 402000000, }; static const struct cedrus_variant sun50i_h5_cedrus_variant = { .capabilities = CEDRUS_CAPABILITY_UNTILED, + .mod_rate = 402000000, }; static const struct cedrus_variant sun50i_h6_cedrus_variant = { .capabilities = CEDRUS_CAPABILITY_UNTILED, .quirks = CEDRUS_QUIRK_NO_DMA_OFFSET, + .mod_rate = 600000000, }; static const struct of_device_id cedrus_dt_match[] = { diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.h b/drivers/staging/media/sunxi/cedrus/cedrus.h index c57c04b41d2e..3f476d0fd981 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.h +++ b/drivers/staging/media/sunxi/cedrus/cedrus.h @@ -32,7 +32,7 @@ enum cedrus_codec { CEDRUS_CODEC_MPEG2, - + CEDRUS_CODEC_H264, CEDRUS_CODEC_LAST, }; @@ -42,6 +42,12 @@ enum cedrus_irq_status { CEDRUS_IRQ_OK, }; +enum cedrus_h264_pic_type { + CEDRUS_H264_PIC_TYPE_FRAME = 0, + CEDRUS_H264_PIC_TYPE_FIELD, + CEDRUS_H264_PIC_TYPE_MBAFF, +}; + struct cedrus_control { u32 id; u32 elem_size; @@ -49,6 +55,14 @@ struct cedrus_control { unsigned char required:1; }; +struct cedrus_h264_run { + const struct v4l2_ctrl_h264_decode_params *decode_params; + const struct v4l2_ctrl_h264_pps *pps; + const struct v4l2_ctrl_h264_scaling_matrix *scaling_matrix; + const struct v4l2_ctrl_h264_slice_params *slice_params; + const struct v4l2_ctrl_h264_sps *sps; +}; + struct cedrus_mpeg2_run { const struct v4l2_ctrl_mpeg2_slice_params *slice_params; const struct v4l2_ctrl_mpeg2_quantization *quantization; @@ -59,12 +73,20 @@ struct cedrus_run { struct vb2_v4l2_buffer *dst; union { + struct cedrus_h264_run h264; struct cedrus_mpeg2_run mpeg2; }; }; struct cedrus_buffer { struct v4l2_m2m_buffer m2m_buf; + + union { + struct { + unsigned int position; + enum cedrus_h264_pic_type pic_type; + } h264; + } codec; }; struct cedrus_ctx { @@ -79,6 +101,19 @@ struct cedrus_ctx { struct v4l2_ctrl **ctrls; struct vb2_buffer *dst_bufs[VIDEO_MAX_FRAME]; + + union { + struct { + void *mv_col_buf; + dma_addr_t mv_col_buf_dma; + ssize_t mv_col_buf_field_size; + ssize_t mv_col_buf_size; + void *pic_info_buf; + dma_addr_t pic_info_buf_dma; + void *neighbor_info_buf; + dma_addr_t neighbor_info_buf_dma; + } h264; + } codec; }; struct cedrus_dec_ops { @@ -94,6 +129,7 @@ struct cedrus_dec_ops { struct cedrus_variant { unsigned int capabilities; unsigned int quirks; + unsigned int mod_rate; }; struct cedrus_dev { @@ -121,6 +157,7 @@ struct cedrus_dev { }; extern struct cedrus_dec_ops cedrus_dec_ops_mpeg2; +extern struct cedrus_dec_ops cedrus_dec_ops_h264; static inline void cedrus_write(struct cedrus_dev *dev, u32 reg, u32 val) { diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c index 4d6d602cdde6..bdad87eb9d79 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_dec.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_dec.c @@ -46,6 +46,19 @@ void cedrus_device_run(void *priv) V4L2_CID_MPEG_VIDEO_MPEG2_QUANTIZATION); break; + case V4L2_PIX_FMT_H264_SLICE_RAW: + run.h264.decode_params = cedrus_find_control_data(ctx, + V4L2_CID_MPEG_VIDEO_H264_DECODE_PARAMS); + run.h264.pps = cedrus_find_control_data(ctx, + V4L2_CID_MPEG_VIDEO_H264_PPS); + run.h264.scaling_matrix = cedrus_find_control_data(ctx, + V4L2_CID_MPEG_VIDEO_H264_SCALING_MATRIX); + run.h264.slice_params = cedrus_find_control_data(ctx, + V4L2_CID_MPEG_VIDEO_H264_SLICE_PARAMS); + run.h264.sps = cedrus_find_control_data(ctx, + V4L2_CID_MPEG_VIDEO_H264_SPS); + break; + default: break; } diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c new file mode 100644 index 000000000000..a30bb283f69f --- /dev/null +++ b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c @@ -0,0 +1,576 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Cedrus VPU driver + * + * Copyright (c) 2013 Jens Kuske <jenskuske@gmail.com> + * Copyright (c) 2018 Bootlin + */ + +#include <linux/types.h> + +#include <media/videobuf2-dma-contig.h> + +#include "cedrus.h" +#include "cedrus_hw.h" +#include "cedrus_regs.h" + +enum cedrus_h264_sram_off { + CEDRUS_SRAM_H264_PRED_WEIGHT_TABLE = 0x000, + CEDRUS_SRAM_H264_FRAMEBUFFER_LIST = 0x100, + CEDRUS_SRAM_H264_REF_LIST_0 = 0x190, + CEDRUS_SRAM_H264_REF_LIST_1 = 0x199, + CEDRUS_SRAM_H264_SCALING_LIST_8x8_0 = 0x200, + CEDRUS_SRAM_H264_SCALING_LIST_8x8_1 = 0x210, + CEDRUS_SRAM_H264_SCALING_LIST_4x4 = 0x220, +}; + +struct cedrus_h264_sram_ref_pic { + __le32 top_field_order_cnt; + __le32 bottom_field_order_cnt; + __le32 frame_info; + __le32 luma_ptr; + __le32 chroma_ptr; + __le32 mv_col_top_ptr; + __le32 mv_col_bot_ptr; + __le32 reserved; +} __packed; + +#define CEDRUS_H264_FRAME_NUM 18 + +#define CEDRUS_NEIGHBOR_INFO_BUF_SIZE (16 * SZ_1K) +#define CEDRUS_PIC_INFO_BUF_SIZE (128 * SZ_1K) + +static void cedrus_h264_write_sram(struct cedrus_dev *dev, + enum cedrus_h264_sram_off off, + const void *data, size_t len) +{ + const u32 *buffer = data; + size_t count = DIV_ROUND_UP(len, 4); + + cedrus_write(dev, VE_AVC_SRAM_PORT_OFFSET, off << 2); + + while (count--) + cedrus_write(dev, VE_AVC_SRAM_PORT_DATA, *buffer++); +} + +static dma_addr_t cedrus_h264_mv_col_buf_addr(struct cedrus_ctx *ctx, + unsigned int position, + unsigned int field) +{ + dma_addr_t addr = ctx->codec.h264.mv_col_buf_dma; + + /* Adjust for the position */ + addr += position * ctx->codec.h264.mv_col_buf_field_size * 2; + + /* Adjust for the field */ + addr += field * ctx->codec.h264.mv_col_buf_field_size; + + return addr; +} + +static void cedrus_fill_ref_pic(struct cedrus_ctx *ctx, + struct cedrus_buffer *buf, + unsigned int top_field_order_cnt, + unsigned int bottom_field_order_cnt, + struct cedrus_h264_sram_ref_pic *pic) +{ + struct vb2_buffer *vbuf = &buf->m2m_buf.vb.vb2_buf; + unsigned int position = buf->codec.h264.position; + + pic->top_field_order_cnt = cpu_to_le32(top_field_order_cnt); + pic->bottom_field_order_cnt = cpu_to_le32(bottom_field_order_cnt); + pic->frame_info = cpu_to_le32(buf->codec.h264.pic_type << 8); + + pic->luma_ptr = cpu_to_le32(cedrus_buf_addr(vbuf, &ctx->dst_fmt, 0)); + pic->chroma_ptr = cpu_to_le32(cedrus_buf_addr(vbuf, &ctx->dst_fmt, 1)); + pic->mv_col_top_ptr = + cpu_to_le32(cedrus_h264_mv_col_buf_addr(ctx, position, 0)); + pic->mv_col_bot_ptr = + cpu_to_le32(cedrus_h264_mv_col_buf_addr(ctx, position, 1)); +} + +static void cedrus_write_frame_list(struct cedrus_ctx *ctx, + struct cedrus_run *run) +{ + struct cedrus_h264_sram_ref_pic pic_list[CEDRUS_H264_FRAME_NUM]; + const struct v4l2_ctrl_h264_decode_params *decode = run->h264.decode_params; + const struct v4l2_ctrl_h264_slice_params *slice = run->h264.slice_params; + const struct v4l2_ctrl_h264_sps *sps = run->h264.sps; + struct vb2_queue *cap_q = &ctx->fh.m2m_ctx->cap_q_ctx.q; + struct cedrus_buffer *output_buf; + struct cedrus_dev *dev = ctx->dev; + unsigned long used_dpbs = 0; + unsigned int position; + unsigned int output = 0; + unsigned int i; + + memset(pic_list, 0, sizeof(pic_list)); + + 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; + + 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) + continue; + + cedrus_buf = vb2_to_cedrus_buffer(ctx->dst_bufs[buf_idx]); + position = cedrus_buf->codec.h264.position; + used_dpbs |= BIT(position); + + if (!(dpb->flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) + continue; + + cedrus_fill_ref_pic(ctx, cedrus_buf, + dpb->top_field_order_cnt, + dpb->bottom_field_order_cnt, + &pic_list[position]); + + output = max(position, output); + } + + position = find_next_zero_bit(&used_dpbs, CEDRUS_H264_FRAME_NUM, + output); + if (position >= CEDRUS_H264_FRAME_NUM) + position = find_first_zero_bit(&used_dpbs, CEDRUS_H264_FRAME_NUM); + + output_buf = vb2_to_cedrus_buffer(&run->dst->vb2_buf); + output_buf->codec.h264.position = position; + + if (slice->flags & V4L2_H264_SLICE_FLAG_FIELD_PIC) + output_buf->codec.h264.pic_type = CEDRUS_H264_PIC_TYPE_FIELD; + else if (sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD) + output_buf->codec.h264.pic_type = CEDRUS_H264_PIC_TYPE_MBAFF; + else + output_buf->codec.h264.pic_type = CEDRUS_H264_PIC_TYPE_FRAME; + + cedrus_fill_ref_pic(ctx, output_buf, + decode->top_field_order_cnt, + decode->bottom_field_order_cnt, + &pic_list[position]); + + cedrus_h264_write_sram(dev, CEDRUS_SRAM_H264_FRAMEBUFFER_LIST, + pic_list, sizeof(pic_list)); + + cedrus_write(dev, VE_H264_OUTPUT_FRAME_IDX, position); +} + +#define CEDRUS_MAX_REF_IDX 32 + +static void _cedrus_write_ref_list(struct cedrus_ctx *ctx, + struct cedrus_run *run, + const u8 *ref_list, u8 num_ref, + enum cedrus_h264_sram_off sram) +{ + const struct v4l2_ctrl_h264_decode_params *decode = run->h264.decode_params; + struct vb2_queue *cap_q = &ctx->fh.m2m_ctx->cap_q_ctx.q; + struct cedrus_dev *dev = ctx->dev; + u8 sram_array[CEDRUS_MAX_REF_IDX]; + unsigned int i; + size_t size; + + memset(sram_array, 0, sizeof(sram_array)); + + for (i = 0; i < num_ref; i++) { + const struct v4l2_h264_dpb_entry *dpb; + const struct cedrus_buffer *cedrus_buf; + const struct vb2_v4l2_buffer *ref_buf; + unsigned int position; + int buf_idx; + u8 dpb_idx; + + dpb_idx = ref_list[i]; + dpb = &decode->dpb[dpb_idx]; + + 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) + continue; + + ref_buf = to_vb2_v4l2_buffer(ctx->dst_bufs[buf_idx]); + cedrus_buf = vb2_v4l2_to_cedrus_buffer(ref_buf); + position = cedrus_buf->codec.h264.position; + + sram_array[i] |= position << 1; + if (ref_buf->field == V4L2_FIELD_BOTTOM) + sram_array[i] |= BIT(0); + } + + size = min_t(size_t, ALIGN(num_ref, 4), sizeof(sram_array)); + cedrus_h264_write_sram(dev, sram, &sram_array, size); +} + +static void cedrus_write_ref_list0(struct cedrus_ctx *ctx, + struct cedrus_run *run) +{ + const struct v4l2_ctrl_h264_slice_params *slice = run->h264.slice_params; + + _cedrus_write_ref_list(ctx, run, + slice->ref_pic_list0, + slice->num_ref_idx_l0_active_minus1 + 1, + CEDRUS_SRAM_H264_REF_LIST_0); +} + +static void cedrus_write_ref_list1(struct cedrus_ctx *ctx, + struct cedrus_run *run) +{ + const struct v4l2_ctrl_h264_slice_params *slice = run->h264.slice_params; + + _cedrus_write_ref_list(ctx, run, + slice->ref_pic_list1, + slice->num_ref_idx_l1_active_minus1 + 1, + CEDRUS_SRAM_H264_REF_LIST_1); +} + +static void cedrus_write_scaling_lists(struct cedrus_ctx *ctx, + struct cedrus_run *run) +{ + const struct v4l2_ctrl_h264_scaling_matrix *scaling = + run->h264.scaling_matrix; + struct cedrus_dev *dev = ctx->dev; + + cedrus_h264_write_sram(dev, CEDRUS_SRAM_H264_SCALING_LIST_8x8_0, + scaling->scaling_list_8x8[0], + sizeof(scaling->scaling_list_8x8[0])); + + cedrus_h264_write_sram(dev, CEDRUS_SRAM_H264_SCALING_LIST_8x8_1, + scaling->scaling_list_8x8[3], + sizeof(scaling->scaling_list_8x8[3])); + + cedrus_h264_write_sram(dev, CEDRUS_SRAM_H264_SCALING_LIST_4x4, + scaling->scaling_list_4x4, + sizeof(scaling->scaling_list_4x4)); +} + +static void cedrus_write_pred_weight_table(struct cedrus_ctx *ctx, + struct cedrus_run *run) +{ + const struct v4l2_ctrl_h264_slice_params *slice = + run->h264.slice_params; + const struct v4l2_h264_pred_weight_table *pred_weight = + &slice->pred_weight_table; + struct cedrus_dev *dev = ctx->dev; + int i, j, k; + + cedrus_write(dev, VE_H264_SHS_WP, + ((pred_weight->chroma_log2_weight_denom & 0x7) << 4) | + ((pred_weight->luma_log2_weight_denom & 0x7) << 0)); + + cedrus_write(dev, VE_AVC_SRAM_PORT_OFFSET, + CEDRUS_SRAM_H264_PRED_WEIGHT_TABLE << 2); + + for (i = 0; i < ARRAY_SIZE(pred_weight->weight_factors); i++) { + const struct v4l2_h264_weight_factors *factors = + &pred_weight->weight_factors[i]; + + for (j = 0; j < ARRAY_SIZE(factors->luma_weight); j++) { + u32 val; + + val = (((u32)factors->luma_offset[j] & 0x1ff) << 16) | + (factors->luma_weight[j] & 0x1ff); + cedrus_write(dev, VE_AVC_SRAM_PORT_DATA, val); + } + + for (j = 0; j < ARRAY_SIZE(factors->chroma_weight); j++) { + for (k = 0; k < ARRAY_SIZE(factors->chroma_weight[0]); k++) { + u32 val; + + val = (((u32)factors->chroma_offset[j][k] & 0x1ff) << 16) | + (factors->chroma_weight[j][k] & 0x1ff); + cedrus_write(dev, VE_AVC_SRAM_PORT_DATA, val); + } + } + } +} + +static void cedrus_set_params(struct cedrus_ctx *ctx, + struct cedrus_run *run) +{ + const struct v4l2_ctrl_h264_decode_params *decode = run->h264.decode_params; + const struct v4l2_ctrl_h264_slice_params *slice = run->h264.slice_params; + const struct v4l2_ctrl_h264_pps *pps = run->h264.pps; + const struct v4l2_ctrl_h264_sps *sps = run->h264.sps; + struct vb2_buffer *src_buf = &run->src->vb2_buf; + struct cedrus_dev *dev = ctx->dev; + dma_addr_t src_buf_addr; + u32 offset = slice->header_bit_size; + u32 len = (slice->size * 8) - offset; + u32 reg; + + cedrus_write(dev, VE_H264_VLD_LEN, len); + cedrus_write(dev, VE_H264_VLD_OFFSET, offset); + + src_buf_addr = vb2_dma_contig_plane_dma_addr(src_buf, 0); + cedrus_write(dev, VE_H264_VLD_END, + src_buf_addr + vb2_get_plane_payload(src_buf, 0)); + cedrus_write(dev, VE_H264_VLD_ADDR, + VE_H264_VLD_ADDR_VAL(src_buf_addr) | + VE_H264_VLD_ADDR_FIRST | VE_H264_VLD_ADDR_VALID | + VE_H264_VLD_ADDR_LAST); + + /* + * FIXME: Since the bitstream parsing is done in software, and + * in userspace, this shouldn't be needed anymore. But it + * turns out that removing it breaks the decoding process, + * without any clear indication why. + */ + cedrus_write(dev, VE_H264_TRIGGER_TYPE, + VE_H264_TRIGGER_TYPE_INIT_SWDEC); + + if (((pps->flags & V4L2_H264_PPS_FLAG_WEIGHTED_PRED) && + (slice->slice_type == V4L2_H264_SLICE_TYPE_P || + slice->slice_type == V4L2_H264_SLICE_TYPE_SP)) || + (pps->weighted_bipred_idc == 1 && + slice->slice_type == V4L2_H264_SLICE_TYPE_B)) + cedrus_write_pred_weight_table(ctx, run); + + if ((slice->slice_type == V4L2_H264_SLICE_TYPE_P) || + (slice->slice_type == V4L2_H264_SLICE_TYPE_SP) || + (slice->slice_type == V4L2_H264_SLICE_TYPE_B)) + cedrus_write_ref_list0(ctx, run); + + if (slice->slice_type == V4L2_H264_SLICE_TYPE_B) + cedrus_write_ref_list1(ctx, run); + + // picture parameters + reg = 0; + /* + * FIXME: the kernel headers are allowing the default value to + * be passed, but the libva doesn't give us that. + */ + reg |= (slice->num_ref_idx_l0_active_minus1 & 0x1f) << 10; + reg |= (slice->num_ref_idx_l1_active_minus1 & 0x1f) << 5; + reg |= (pps->weighted_bipred_idc & 0x3) << 2; + if (pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE) + reg |= VE_H264_PPS_ENTROPY_CODING_MODE; + if (pps->flags & V4L2_H264_PPS_FLAG_WEIGHTED_PRED) + reg |= VE_H264_PPS_WEIGHTED_PRED; + if (pps->flags & V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED) + reg |= VE_H264_PPS_CONSTRAINED_INTRA_PRED; + if (pps->flags & V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE) + reg |= VE_H264_PPS_TRANSFORM_8X8_MODE; + cedrus_write(dev, VE_H264_PPS, reg); + + // sequence parameters + reg = 0; + reg |= (sps->chroma_format_idc & 0x7) << 19; + reg |= (sps->pic_width_in_mbs_minus1 & 0xff) << 8; + reg |= sps->pic_height_in_map_units_minus1 & 0xff; + if (sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY) + reg |= VE_H264_SPS_MBS_ONLY; + if (sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD) + reg |= VE_H264_SPS_MB_ADAPTIVE_FRAME_FIELD; + if (sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE) + reg |= VE_H264_SPS_DIRECT_8X8_INFERENCE; + cedrus_write(dev, VE_H264_SPS, reg); + + // slice parameters + reg = 0; + reg |= decode->nal_ref_idc ? BIT(12) : 0; + reg |= (slice->slice_type & 0xf) << 8; + reg |= slice->cabac_init_idc & 0x3; + reg |= VE_H264_SHS_FIRST_SLICE_IN_PIC; + if (slice->flags & V4L2_H264_SLICE_FLAG_FIELD_PIC) + reg |= VE_H264_SHS_FIELD_PIC; + if (slice->flags & V4L2_H264_SLICE_FLAG_BOTTOM_FIELD) + reg |= VE_H264_SHS_BOTTOM_FIELD; + if (slice->flags & V4L2_H264_SLICE_FLAG_DIRECT_SPATIAL_MV_PRED) + reg |= VE_H264_SHS_DIRECT_SPATIAL_MV_PRED; + cedrus_write(dev, VE_H264_SHS, reg); + + reg = 0; + reg |= VE_H264_SHS2_NUM_REF_IDX_ACTIVE_OVRD; + reg |= (slice->num_ref_idx_l0_active_minus1 & 0x1f) << 24; + reg |= (slice->num_ref_idx_l1_active_minus1 & 0x1f) << 16; + reg |= (slice->disable_deblocking_filter_idc & 0x3) << 8; + reg |= (slice->slice_alpha_c0_offset_div2 & 0xf) << 4; + reg |= slice->slice_beta_offset_div2 & 0xf; + cedrus_write(dev, VE_H264_SHS2, reg); + + reg = 0; + reg |= (pps->second_chroma_qp_index_offset & 0x3f) << 16; + reg |= (pps->chroma_qp_index_offset & 0x3f) << 8; + reg |= (pps->pic_init_qp_minus26 + 26 + slice->slice_qp_delta) & 0x3f; + cedrus_write(dev, VE_H264_SHS_QP, reg); + + // clear status flags + cedrus_write(dev, VE_H264_STATUS, cedrus_read(dev, VE_H264_STATUS)); + + // enable int + cedrus_write(dev, VE_H264_CTRL, + VE_H264_CTRL_SLICE_DECODE_INT | + VE_H264_CTRL_DECODE_ERR_INT | + VE_H264_CTRL_VLD_DATA_REQ_INT); +} + +static enum cedrus_irq_status +cedrus_h264_irq_status(struct cedrus_ctx *ctx) +{ + struct cedrus_dev *dev = ctx->dev; + u32 reg = cedrus_read(dev, VE_H264_STATUS); + + if (reg & (VE_H264_STATUS_DECODE_ERR_INT | + VE_H264_STATUS_VLD_DATA_REQ_INT)) + return CEDRUS_IRQ_ERROR; + + if (reg & VE_H264_CTRL_SLICE_DECODE_INT) + return CEDRUS_IRQ_OK; + + return CEDRUS_IRQ_NONE; +} + +static void cedrus_h264_irq_clear(struct cedrus_ctx *ctx) +{ + struct cedrus_dev *dev = ctx->dev; + + cedrus_write(dev, VE_H264_STATUS, + VE_H264_STATUS_INT_MASK); +} + +static void cedrus_h264_irq_disable(struct cedrus_ctx *ctx) +{ + struct cedrus_dev *dev = ctx->dev; + u32 reg = cedrus_read(dev, VE_H264_CTRL); + + cedrus_write(dev, VE_H264_CTRL, + reg & ~VE_H264_CTRL_INT_MASK); +} + +static void cedrus_h264_setup(struct cedrus_ctx *ctx, + struct cedrus_run *run) +{ + struct cedrus_dev *dev = ctx->dev; + + cedrus_engine_enable(dev, CEDRUS_CODEC_H264); + + cedrus_write(dev, VE_H264_SDROT_CTRL, 0); + cedrus_write(dev, VE_H264_EXTRA_BUFFER1, + ctx->codec.h264.pic_info_buf_dma); + cedrus_write(dev, VE_H264_EXTRA_BUFFER2, + ctx->codec.h264.neighbor_info_buf_dma); + + cedrus_write_scaling_lists(ctx, run); + cedrus_write_frame_list(ctx, run); + + cedrus_set_params(ctx, run); +} + +static int cedrus_h264_start(struct cedrus_ctx *ctx) +{ + struct cedrus_dev *dev = ctx->dev; + unsigned int field_size; + unsigned int mv_col_size; + int ret; + + /* + * FIXME: It seems that the H6 cedarX code is using a formula + * here based on the size of the frame, while all the older + * code is using a fixed size, so that might need to be + * changed at some point. + */ + ctx->codec.h264.pic_info_buf = + dma_alloc_coherent(dev->dev, CEDRUS_PIC_INFO_BUF_SIZE, + &ctx->codec.h264.pic_info_buf_dma, + GFP_KERNEL); + if (!ctx->codec.h264.pic_info_buf) + return -ENOMEM; + + /* + * That buffer is supposed to be 16kiB in size, and be aligned + * on 16kiB as well. However, dma_alloc_coherent provides the + * guarantee that we'll have a CPU and DMA address aligned on + * the smallest page order that is greater to the requested + * size, so we don't have to overallocate. + */ + ctx->codec.h264.neighbor_info_buf = + dma_alloc_coherent(dev->dev, CEDRUS_NEIGHBOR_INFO_BUF_SIZE, + &ctx->codec.h264.neighbor_info_buf_dma, + GFP_KERNEL); + if (!ctx->codec.h264.neighbor_info_buf) { + ret = -ENOMEM; + goto err_pic_buf; + } + + field_size = DIV_ROUND_UP(ctx->src_fmt.width, 16) * + DIV_ROUND_UP(ctx->src_fmt.height, 16) * 16; + + /* + * FIXME: This is actually conditional to + * V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE not being set, we + * might have to rework this if memory efficiency ever is + * something we need to work on. + */ + field_size = field_size * 2; + + /* + * FIXME: This is actually conditional to + * V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY not being set, we might + * have to rework this if memory efficiency ever is something + * we need to work on. + */ + field_size = field_size * 2; + ctx->codec.h264.mv_col_buf_field_size = field_size; + + mv_col_size = field_size * 2 * CEDRUS_H264_FRAME_NUM; + ctx->codec.h264.mv_col_buf_size = mv_col_size; + ctx->codec.h264.mv_col_buf = dma_alloc_coherent(dev->dev, + ctx->codec.h264.mv_col_buf_size, + &ctx->codec.h264.mv_col_buf_dma, + GFP_KERNEL); + if (!ctx->codec.h264.mv_col_buf) { + ret = -ENOMEM; + goto err_neighbor_buf; + } + + return 0; + +err_neighbor_buf: + dma_free_coherent(dev->dev, CEDRUS_NEIGHBOR_INFO_BUF_SIZE, + ctx->codec.h264.neighbor_info_buf, + ctx->codec.h264.neighbor_info_buf_dma); + +err_pic_buf: + dma_free_coherent(dev->dev, CEDRUS_PIC_INFO_BUF_SIZE, + ctx->codec.h264.pic_info_buf, + ctx->codec.h264.pic_info_buf_dma); + return ret; +} + +static void cedrus_h264_stop(struct cedrus_ctx *ctx) +{ + struct cedrus_dev *dev = ctx->dev; + + dma_free_coherent(dev->dev, ctx->codec.h264.mv_col_buf_size, + ctx->codec.h264.mv_col_buf, + ctx->codec.h264.mv_col_buf_dma); + dma_free_coherent(dev->dev, CEDRUS_NEIGHBOR_INFO_BUF_SIZE, + ctx->codec.h264.neighbor_info_buf, + ctx->codec.h264.neighbor_info_buf_dma); + dma_free_coherent(dev->dev, CEDRUS_PIC_INFO_BUF_SIZE, + ctx->codec.h264.pic_info_buf, + ctx->codec.h264.pic_info_buf_dma); +} + +static void cedrus_h264_trigger(struct cedrus_ctx *ctx) +{ + struct cedrus_dev *dev = ctx->dev; + + cedrus_write(dev, VE_H264_TRIGGER_TYPE, + VE_H264_TRIGGER_TYPE_AVC_SLICE_DECODE); +} + +struct cedrus_dec_ops cedrus_dec_ops_h264 = { + .irq_clear = cedrus_h264_irq_clear, + .irq_disable = cedrus_h264_irq_disable, + .irq_status = cedrus_h264_irq_status, + .setup = cedrus_h264_setup, + .start = cedrus_h264_start, + .stop = cedrus_h264_stop, + .trigger = cedrus_h264_trigger, +}; diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c index fbfff7c1c771..c34aec7c6e40 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c @@ -46,6 +46,10 @@ int cedrus_engine_enable(struct cedrus_dev *dev, enum cedrus_codec codec) reg |= VE_MODE_DEC_MPEG; break; + case CEDRUS_CODEC_H264: + reg |= VE_MODE_DEC_H264; + break; + default: return -EINVAL; } @@ -236,7 +240,7 @@ int cedrus_hw_probe(struct cedrus_dev *dev) goto err_sram; } - ret = clk_set_rate(dev->mod_clk, CEDRUS_CLOCK_RATE_DEFAULT); + ret = clk_set_rate(dev->mod_clk, variant->mod_rate); if (ret) { dev_err(dev->dev, "Failed to set clock rate\n"); diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_hw.h b/drivers/staging/media/sunxi/cedrus/cedrus_hw.h index b43c77d54b95..27d0882397aa 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_hw.h +++ b/drivers/staging/media/sunxi/cedrus/cedrus_hw.h @@ -16,8 +16,6 @@ #ifndef _CEDRUS_HW_H_ #define _CEDRUS_HW_H_ -#define CEDRUS_CLOCK_RATE_DEFAULT 320000000 - int cedrus_engine_enable(struct cedrus_dev *dev, enum cedrus_codec codec); void cedrus_engine_disable(struct cedrus_dev *dev); diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h index de2d6b6f64bf..3e9931416e45 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h +++ b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h @@ -232,4 +232,95 @@ #define VE_DEC_MPEG_ROT_LUMA (VE_ENGINE_DEC_MPEG + 0xcc) #define VE_DEC_MPEG_ROT_CHROMA (VE_ENGINE_DEC_MPEG + 0xd0) +#define VE_H264_SPS 0x200 +#define VE_H264_SPS_MBS_ONLY BIT(18) +#define VE_H264_SPS_MB_ADAPTIVE_FRAME_FIELD BIT(17) +#define VE_H264_SPS_DIRECT_8X8_INFERENCE BIT(16) + +#define VE_H264_PPS 0x204 +#define VE_H264_PPS_ENTROPY_CODING_MODE BIT(15) +#define VE_H264_PPS_WEIGHTED_PRED BIT(4) +#define VE_H264_PPS_CONSTRAINED_INTRA_PRED BIT(1) +#define VE_H264_PPS_TRANSFORM_8X8_MODE BIT(0) + +#define VE_H264_SHS 0x208 +#define VE_H264_SHS_FIRST_SLICE_IN_PIC BIT(5) +#define VE_H264_SHS_FIELD_PIC BIT(4) +#define VE_H264_SHS_BOTTOM_FIELD BIT(3) +#define VE_H264_SHS_DIRECT_SPATIAL_MV_PRED BIT(2) + +#define VE_H264_SHS2 0x20c +#define VE_H264_SHS2_NUM_REF_IDX_ACTIVE_OVRD BIT(12) + +#define VE_H264_SHS_WP 0x210 + +#define VE_H264_SHS_QP 0x21c +#define VE_H264_SHS_QP_SCALING_MATRIX_DEFAULT BIT(24) + +#define VE_H264_CTRL 0x220 +#define VE_H264_CTRL_VLD_DATA_REQ_INT BIT(2) +#define VE_H264_CTRL_DECODE_ERR_INT BIT(1) +#define VE_H264_CTRL_SLICE_DECODE_INT BIT(0) + +#define VE_H264_CTRL_INT_MASK (VE_H264_CTRL_VLD_DATA_REQ_INT | \ + VE_H264_CTRL_DECODE_ERR_INT | \ + VE_H264_CTRL_SLICE_DECODE_INT) + +#define VE_H264_TRIGGER_TYPE 0x224 +#define VE_H264_TRIGGER_TYPE_AVC_SLICE_DECODE (8 << 0) +#define VE_H264_TRIGGER_TYPE_INIT_SWDEC (7 << 0) + +#define VE_H264_STATUS 0x228 +#define VE_H264_STATUS_VLD_DATA_REQ_INT VE_H264_CTRL_VLD_DATA_REQ_INT +#define VE_H264_STATUS_DECODE_ERR_INT VE_H264_CTRL_DECODE_ERR_INT +#define VE_H264_STATUS_SLICE_DECODE_INT VE_H264_CTRL_SLICE_DECODE_INT + +#define VE_H264_STATUS_INT_MASK VE_H264_CTRL_INT_MASK + +#define VE_H264_CUR_MB_NUM 0x22c + +#define VE_H264_VLD_ADDR 0x230 +#define VE_H264_VLD_ADDR_FIRST BIT(30) +#define VE_H264_VLD_ADDR_LAST BIT(29) +#define VE_H264_VLD_ADDR_VALID BIT(28) +#define VE_H264_VLD_ADDR_VAL(x) (((x) & 0x0ffffff0) | ((x) >> 28)) + +#define VE_H264_VLD_OFFSET 0x234 +#define VE_H264_VLD_LEN 0x238 +#define VE_H264_VLD_END 0x23c +#define VE_H264_SDROT_CTRL 0x240 +#define VE_H264_OUTPUT_FRAME_IDX 0x24c +#define VE_H264_EXTRA_BUFFER1 0x250 +#define VE_H264_EXTRA_BUFFER2 0x254 +#define VE_H264_BASIC_BITS 0x2dc +#define VE_AVC_SRAM_PORT_OFFSET 0x2e0 +#define VE_AVC_SRAM_PORT_DATA 0x2e4 + +#define VE_ISP_INPUT_SIZE 0xa00 +#define VE_ISP_INPUT_STRIDE 0xa04 +#define VE_ISP_CTRL 0xa08 +#define VE_ISP_INPUT_LUMA 0xa78 +#define VE_ISP_INPUT_CHROMA 0xa7c + +#define VE_AVC_PARAM 0xb04 +#define VE_AVC_QP 0xb08 +#define VE_AVC_MOTION_EST 0xb10 +#define VE_AVC_CTRL 0xb14 +#define VE_AVC_TRIGGER 0xb18 +#define VE_AVC_STATUS 0xb1c +#define VE_AVC_BASIC_BITS 0xb20 +#define VE_AVC_UNK_BUF 0xb60 +#define VE_AVC_VLE_ADDR 0xb80 +#define VE_AVC_VLE_END 0xb84 +#define VE_AVC_VLE_OFFSET 0xb88 +#define VE_AVC_VLE_MAX 0xb8c +#define VE_AVC_VLE_LENGTH 0xb90 +#define VE_AVC_REF_LUMA 0xba0 +#define VE_AVC_REF_CHROMA 0xba4 +#define VE_AVC_REC_LUMA 0xbb0 +#define VE_AVC_REC_CHROMA 0xbb4 +#define VE_AVC_REF_SLUMA 0xbb8 +#define VE_AVC_REC_SLUMA 0xbbc +#define VE_AVC_MB_INFO 0xbc0 + #endif diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.c b/drivers/staging/media/sunxi/cedrus/cedrus_video.c index 9673874ece10..e2b530b1a956 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_video.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.c @@ -38,6 +38,10 @@ static struct cedrus_format cedrus_formats[] = { .directions = CEDRUS_DECODE_SRC, }, { + .pixelformat = V4L2_PIX_FMT_H264_SLICE_RAW, + .directions = CEDRUS_DECODE_SRC, + }, + { .pixelformat = V4L2_PIX_FMT_SUNXI_TILED_NV12, .directions = CEDRUS_DECODE_DST, }, @@ -100,6 +104,7 @@ static void cedrus_prepare_format(struct v4l2_pix_format *pix_fmt) switch (pix_fmt->pixelformat) { case V4L2_PIX_FMT_MPEG2_SLICE: + case V4L2_PIX_FMT_H264_SLICE_RAW: /* Zero bytes per line for encoded source. */ bytesperline = 0; @@ -464,6 +469,10 @@ static int cedrus_start_streaming(struct vb2_queue *vq, unsigned int count) ctx->current_codec = CEDRUS_CODEC_MPEG2; break; + case V4L2_PIX_FMT_H264_SLICE_RAW: + ctx->current_codec = CEDRUS_CODEC_H264; + break; + default: return -EINVAL; } diff --git a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c index 68f08dc18da9..49d0470f9a7e 100644 --- a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c +++ b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c @@ -864,10 +864,6 @@ static int vidioc_querycap(struct file *file, void *priv, snprintf((char *)cap->bus_info, sizeof(cap->bus_info), "platform:%s", dev->v4l2_dev.name); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | - V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - return 0; } @@ -1446,6 +1442,8 @@ static const struct video_device vdev_template = { .fops = &camera0_fops, .ioctl_ops = &camera0_ioctl_ops, .release = video_device_release_empty, + .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | + V4L2_CAP_STREAMING | V4L2_CAP_READWRITE, }; /* Returns the number of cameras, and also the max resolution supported diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 8c99392df593..fb0a892687c0 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -423,6 +423,7 @@ uvc_register_video(struct uvc_device *uvc) uvc->vdev.release = video_device_release_empty; uvc->vdev.vfl_dir = VFL_DIR_TX; uvc->vdev.lock = &uvc->video.mutex; + uvc->vdev.device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; strlcpy(uvc->vdev.name, cdev->gadget->name, sizeof(uvc->vdev.name)); video_set_drvdata(&uvc->vdev, uvc); diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index a1183eccee22..495f0ec663ea 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -71,10 +71,6 @@ uvc_v4l2_querycap(struct file *file, void *fh, struct v4l2_capability *cap) strlcpy(cap->card, cdev->gadget->name, sizeof(cap->card)); strlcpy(cap->bus_info, dev_name(&cdev->gadget->dev), sizeof(cap->bus_info)); - - cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - return 0; } diff --git a/include/media/drv-intf/cx25840.h b/include/media/drv-intf/cx25840.h index 328ddb359fdf..ba69bc525382 100644 --- a/include/media/drv-intf/cx25840.h +++ b/include/media/drv-intf/cx25840.h @@ -1,25 +1,31 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - cx25840.h - definition for cx25840/1/2/3 inputs - - Copyright (C) 2006 Hans Verkuil (hverkuil@xs4all.nl) -*/ +/* + * cx25840.h - definition for cx25840/1/2/3 inputs + * + * Copyright (C) 2006 Hans Verkuil (hverkuil@xs4all.nl) + */ #ifndef _CX25840_H_ #define _CX25840_H_ -/* Note that the cx25840 driver requires that the bridge driver calls the - v4l2_subdev's init operation in order to load the driver's firmware. - Without this the audio standard detection will fail and you will - only get mono. - - Since loading the firmware is often problematic when the driver is - compiled into the kernel I recommend postponing calling this function - until the first open of the video device. Another reason for - postponing it is that loading this firmware takes a long time (seconds) - due to the slow i2c bus speed. So it will speed up the boot process if - you can avoid loading the fw as long as the video device isn't used. */ +/* + * Note that the cx25840 driver requires that the bridge driver calls the + * v4l2_subdev's load_fw operation in order to load the driver's firmware. + * This will load the firmware on the first invocation (further ones are NOP). + * Without this the audio standard detection will fail and you will + * only get mono. + * Alternatively, you can call the reset operation (this can be done + * multiple times if needed, each invocation will fully reinitialize + * the device). + * + * Since loading the firmware is often problematic when the driver is + * compiled into the kernel I recommend postponing calling this function + * until the first open of the video device. Another reason for + * postponing it is that loading this firmware takes a long time (seconds) + * due to the slow i2c bus speed. So it will speed up the boot process if + * you can avoid loading the fw as long as the video device isn't used. + */ enum cx25840_video_input { /* Composite video inputs In1-In8 */ @@ -32,8 +38,10 @@ enum cx25840_video_input { CX25840_COMPOSITE7, CX25840_COMPOSITE8, - /* S-Video inputs consist of one luma input (In1-In8) ORed with one - chroma input (In5-In8) */ + /* + * S-Video inputs consist of one luma input (In1-In8) ORed with one + * chroma input (In5-In8) + */ CX25840_SVIDEO_LUMA1 = 0x10, CX25840_SVIDEO_LUMA2 = 0x20, CX25840_SVIDEO_LUMA3 = 0x30, @@ -76,6 +84,81 @@ enum cx25840_video_input { CX25840_DIF_ON = 0x80000400, }; +/* + * The defines below are used to set the chip video output settings + * in the generic mode that can be enabled by calling the subdevice + * init core op. + * + * The requested settings can be passed to the init core op as + * @val parameter and to the s_routing video op as @config parameter. + * + * For details please refer to the section 3.7 Video Output Formatting and + * to Video Out Control 1 to 4 registers in the section 5.6 Video Decoder Core + * of the chip datasheet. + */ +#define CX25840_VCONFIG_FMT_SHIFT 0 +#define CX25840_VCONFIG_FMT_MASK GENMASK(2, 0) +#define CX25840_VCONFIG_FMT_BT601 BIT(0) +#define CX25840_VCONFIG_FMT_BT656 BIT(1) +#define CX25840_VCONFIG_FMT_VIP11 GENMASK(1, 0) +#define CX25840_VCONFIG_FMT_VIP2 BIT(2) + +#define CX25840_VCONFIG_RES_SHIFT 3 +#define CX25840_VCONFIG_RES_MASK GENMASK(4, 3) +#define CX25840_VCONFIG_RES_8BIT BIT(3) +#define CX25840_VCONFIG_RES_10BIT BIT(4) + +#define CX25840_VCONFIG_VBIRAW_SHIFT 5 +#define CX25840_VCONFIG_VBIRAW_MASK GENMASK(6, 5) +#define CX25840_VCONFIG_VBIRAW_DISABLED BIT(5) +#define CX25840_VCONFIG_VBIRAW_ENABLED BIT(6) + +#define CX25840_VCONFIG_ANCDATA_SHIFT 7 +#define CX25840_VCONFIG_ANCDATA_MASK GENMASK(8, 7) +#define CX25840_VCONFIG_ANCDATA_DISABLED BIT(7) +#define CX25840_VCONFIG_ANCDATA_ENABLED BIT(8) + +#define CX25840_VCONFIG_TASKBIT_SHIFT 9 +#define CX25840_VCONFIG_TASKBIT_MASK GENMASK(10, 9) +#define CX25840_VCONFIG_TASKBIT_ZERO BIT(9) +#define CX25840_VCONFIG_TASKBIT_ONE BIT(10) + +#define CX25840_VCONFIG_ACTIVE_SHIFT 11 +#define CX25840_VCONFIG_ACTIVE_MASK GENMASK(12, 11) +#define CX25840_VCONFIG_ACTIVE_COMPOSITE BIT(11) +#define CX25840_VCONFIG_ACTIVE_HORIZONTAL BIT(12) + +#define CX25840_VCONFIG_VALID_SHIFT 13 +#define CX25840_VCONFIG_VALID_MASK GENMASK(14, 13) +#define CX25840_VCONFIG_VALID_NORMAL BIT(13) +#define CX25840_VCONFIG_VALID_ANDACTIVE BIT(14) + +#define CX25840_VCONFIG_HRESETW_SHIFT 15 +#define CX25840_VCONFIG_HRESETW_MASK GENMASK(16, 15) +#define CX25840_VCONFIG_HRESETW_NORMAL BIT(15) +#define CX25840_VCONFIG_HRESETW_PIXCLK BIT(16) + +#define CX25840_VCONFIG_CLKGATE_SHIFT 17 +#define CX25840_VCONFIG_CLKGATE_MASK GENMASK(18, 17) +#define CX25840_VCONFIG_CLKGATE_NONE BIT(17) +#define CX25840_VCONFIG_CLKGATE_VALID BIT(18) +#define CX25840_VCONFIG_CLKGATE_VALIDACTIVE GENMASK(18, 17) + +#define CX25840_VCONFIG_DCMODE_SHIFT 19 +#define CX25840_VCONFIG_DCMODE_MASK GENMASK(20, 19) +#define CX25840_VCONFIG_DCMODE_DWORDS BIT(19) +#define CX25840_VCONFIG_DCMODE_BYTES BIT(20) + +#define CX25840_VCONFIG_IDID0S_SHIFT 21 +#define CX25840_VCONFIG_IDID0S_MASK GENMASK(22, 21) +#define CX25840_VCONFIG_IDID0S_NORMAL BIT(21) +#define CX25840_VCONFIG_IDID0S_LINECNT BIT(22) + +#define CX25840_VCONFIG_VIPCLAMP_SHIFT 23 +#define CX25840_VCONFIG_VIPCLAMP_MASK GENMASK(24, 23) +#define CX25840_VCONFIG_VIPCLAMP_ENABLED BIT(23) +#define CX25840_VCONFIG_VIPCLAMP_DISABLED BIT(24) + enum cx25840_audio_input { /* Audio inputs: serial or In4-In8 */ CX25840_AUDIO_SERIAL, @@ -103,7 +186,7 @@ enum cx25840_io_pin { }; enum cx25840_io_pad { - /* Output pads */ + /* Output pads, these must match the actual chip register values */ CX25840_PAD_DEFAULT = 0, CX25840_PAD_ACTIVE, CX25840_PAD_VACTIVE, @@ -162,13 +245,16 @@ enum cx23885_io_pad { CX23885_PAD_GPIO16, }; -/* pvr150_workaround activates a workaround for a hardware bug that is - present in Hauppauge PVR-150 (and possibly PVR-500) cards that have - certain NTSC tuners (tveeprom tuner model numbers 85, 99 and 112). The - audio autodetect fails on some channels for these models and the workaround - is to select the audio standard explicitly. Many thanks to Hauppauge for - providing this information. - This platform data only needs to be supplied by the ivtv driver. */ +/* + * pvr150_workaround activates a workaround for a hardware bug that is + * present in Hauppauge PVR-150 (and possibly PVR-500) cards that have + * certain NTSC tuners (tveeprom tuner model numbers 85, 99 and 112). The + * audio autodetect fails on some channels for these models and the workaround + * is to select the audio standard explicitly. Many thanks to Hauppauge for + * providing this information. + * + * This platform data only needs to be supplied by the ivtv driver. + */ struct cx25840_platform_data { int pvr150_workaround; }; diff --git a/include/media/dvbdev.h b/include/media/dvbdev.h index 881ca461b7bb..551325858de3 100644 --- a/include/media/dvbdev.h +++ b/include/media/dvbdev.h @@ -86,8 +86,8 @@ struct dvb_frontend; * @priv: private data * @device: pointer to struct device * @module: pointer to struct module - * @mfe_shared: mfe shared: indicates mutually exclusive frontends - * Thie usage of this flag is currently deprecated + * @mfe_shared: indicates mutually exclusive frontends. + * Use of this flag is currently deprecated. * @mfe_dvbdev: Frontend device in use, in the case of MFE * @mfe_lock: Lock to prevent using the other frontends when MFE is * used. diff --git a/include/media/h264-ctrls.h b/include/media/h264-ctrls.h new file mode 100644 index 000000000000..e1404d78d6ff --- /dev/null +++ b/include/media/h264-ctrls.h @@ -0,0 +1,197 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * These are the H.264 state controls for use with stateless H.264 + * codec drivers. + * + * It turns out that these structs are not stable yet and will undergo + * more changes. So keep them private until they are stable and ready to + * become part of the official public API. + */ + +#ifndef _H264_CTRLS_H_ +#define _H264_CTRLS_H_ + +#include <linux/videodev2.h> + +/* Our pixel format isn't stable at the moment */ +#define V4L2_PIX_FMT_H264_SLICE_RAW v4l2_fourcc('S', '2', '6', '4') /* H264 parsed slices */ + +/* + * This is put insanely high to avoid conflicting with controls that + * would be added during the phase where those controls are not + * stable. It should be fixed eventually. + */ +#define V4L2_CID_MPEG_VIDEO_H264_SPS (V4L2_CID_MPEG_BASE+1000) +#define V4L2_CID_MPEG_VIDEO_H264_PPS (V4L2_CID_MPEG_BASE+1001) +#define V4L2_CID_MPEG_VIDEO_H264_SCALING_MATRIX (V4L2_CID_MPEG_BASE+1002) +#define V4L2_CID_MPEG_VIDEO_H264_SLICE_PARAMS (V4L2_CID_MPEG_BASE+1003) +#define V4L2_CID_MPEG_VIDEO_H264_DECODE_PARAMS (V4L2_CID_MPEG_BASE+1004) + +/* enum v4l2_ctrl_type type values */ +#define V4L2_CTRL_TYPE_H264_SPS 0x0110 +#define V4L2_CTRL_TYPE_H264_PPS 0x0111 +#define V4L2_CTRL_TYPE_H264_SCALING_MATRIX 0x0112 +#define V4L2_CTRL_TYPE_H264_SLICE_PARAMS 0x0113 +#define V4L2_CTRL_TYPE_H264_DECODE_PARAMS 0x0114 + +#define V4L2_H264_SPS_CONSTRAINT_SET0_FLAG 0x01 +#define V4L2_H264_SPS_CONSTRAINT_SET1_FLAG 0x02 +#define V4L2_H264_SPS_CONSTRAINT_SET2_FLAG 0x04 +#define V4L2_H264_SPS_CONSTRAINT_SET3_FLAG 0x08 +#define V4L2_H264_SPS_CONSTRAINT_SET4_FLAG 0x10 +#define V4L2_H264_SPS_CONSTRAINT_SET5_FLAG 0x20 + +#define V4L2_H264_SPS_FLAG_SEPARATE_COLOUR_PLANE 0x01 +#define V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS 0x02 +#define V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO 0x04 +#define V4L2_H264_SPS_FLAG_GAPS_IN_FRAME_NUM_VALUE_ALLOWED 0x08 +#define V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY 0x10 +#define V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD 0x20 +#define V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE 0x40 + +struct v4l2_ctrl_h264_sps { + __u8 profile_idc; + __u8 constraint_set_flags; + __u8 level_idc; + __u8 seq_parameter_set_id; + __u8 chroma_format_idc; + __u8 bit_depth_luma_minus8; + __u8 bit_depth_chroma_minus8; + __u8 log2_max_frame_num_minus4; + __u8 pic_order_cnt_type; + __u8 log2_max_pic_order_cnt_lsb_minus4; + __u8 max_num_ref_frames; + __u8 num_ref_frames_in_pic_order_cnt_cycle; + __s32 offset_for_ref_frame[255]; + __s32 offset_for_non_ref_pic; + __s32 offset_for_top_to_bottom_field; + __u16 pic_width_in_mbs_minus1; + __u16 pic_height_in_map_units_minus1; + __u32 flags; +}; + +#define V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE 0x0001 +#define V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT 0x0002 +#define V4L2_H264_PPS_FLAG_WEIGHTED_PRED 0x0004 +#define V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT 0x0008 +#define V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED 0x0010 +#define V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT 0x0020 +#define V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE 0x0040 +#define V4L2_H264_PPS_FLAG_PIC_SCALING_MATRIX_PRESENT 0x0080 + +struct v4l2_ctrl_h264_pps { + __u8 pic_parameter_set_id; + __u8 seq_parameter_set_id; + __u8 num_slice_groups_minus1; + __u8 num_ref_idx_l0_default_active_minus1; + __u8 num_ref_idx_l1_default_active_minus1; + __u8 weighted_bipred_idc; + __s8 pic_init_qp_minus26; + __s8 pic_init_qs_minus26; + __s8 chroma_qp_index_offset; + __s8 second_chroma_qp_index_offset; + __u16 flags; +}; + +struct v4l2_ctrl_h264_scaling_matrix { + __u8 scaling_list_4x4[6][16]; + __u8 scaling_list_8x8[6][64]; +}; + +struct v4l2_h264_weight_factors { + __s16 luma_weight[32]; + __s16 luma_offset[32]; + __s16 chroma_weight[32][2]; + __s16 chroma_offset[32][2]; +}; + +struct v4l2_h264_pred_weight_table { + __u16 luma_log2_weight_denom; + __u16 chroma_log2_weight_denom; + struct v4l2_h264_weight_factors weight_factors[2]; +}; + +#define V4L2_H264_SLICE_TYPE_P 0 +#define V4L2_H264_SLICE_TYPE_B 1 +#define V4L2_H264_SLICE_TYPE_I 2 +#define V4L2_H264_SLICE_TYPE_SP 3 +#define V4L2_H264_SLICE_TYPE_SI 4 + +#define V4L2_H264_SLICE_FLAG_FIELD_PIC 0x01 +#define V4L2_H264_SLICE_FLAG_BOTTOM_FIELD 0x02 +#define V4L2_H264_SLICE_FLAG_DIRECT_SPATIAL_MV_PRED 0x04 +#define V4L2_H264_SLICE_FLAG_SP_FOR_SWITCH 0x08 + +struct v4l2_ctrl_h264_slice_params { + /* Size in bytes, including header */ + __u32 size; + /* Offset in bits to slice_data() from the beginning of this slice. */ + __u32 header_bit_size; + + __u16 first_mb_in_slice; + __u8 slice_type; + __u8 pic_parameter_set_id; + __u8 colour_plane_id; + __u8 redundant_pic_cnt; + __u16 frame_num; + __u16 idr_pic_id; + __u16 pic_order_cnt_lsb; + __s32 delta_pic_order_cnt_bottom; + __s32 delta_pic_order_cnt0; + __s32 delta_pic_order_cnt1; + + struct v4l2_h264_pred_weight_table pred_weight_table; + /* Size in bits of dec_ref_pic_marking() syntax element. */ + __u32 dec_ref_pic_marking_bit_size; + /* Size in bits of pic order count syntax. */ + __u32 pic_order_cnt_bit_size; + + __u8 cabac_init_idc; + __s8 slice_qp_delta; + __s8 slice_qs_delta; + __u8 disable_deblocking_filter_idc; + __s8 slice_alpha_c0_offset_div2; + __s8 slice_beta_offset_div2; + __u8 num_ref_idx_l0_active_minus1; + __u8 num_ref_idx_l1_active_minus1; + __u32 slice_group_change_cycle; + + /* + * Entries on each list are indices into + * v4l2_ctrl_h264_decode_params.dpb[]. + */ + __u8 ref_pic_list0[32]; + __u8 ref_pic_list1[32]; + + __u32 flags; +}; + +#define V4L2_H264_DPB_ENTRY_FLAG_VALID 0x01 +#define V4L2_H264_DPB_ENTRY_FLAG_ACTIVE 0x02 +#define V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM 0x04 + +struct v4l2_h264_dpb_entry { + __u64 reference_ts; + __u16 frame_num; + __u16 pic_num; + /* Note that field is indicated by v4l2_buffer.field */ + __s32 top_field_order_cnt; + __s32 bottom_field_order_cnt; + __u32 flags; /* V4L2_H264_DPB_ENTRY_FLAG_* */ +}; + +#define V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC 0x01 + +struct v4l2_ctrl_h264_decode_params { + struct v4l2_h264_dpb_entry dpb[16]; + __u16 num_slices; + __u16 nal_ref_idc; + __u8 ref_pic_list_p0[32]; + __u8 ref_pic_list_b0[32]; + __u8 ref_pic_list_b1[32]; + __s32 top_field_order_cnt; + __s32 bottom_field_order_cnt; + __u32 flags; /* V4L2_H264_DECODE_PARAM_FLAG_* */ +}; + +#endif diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index 3a1ef141ec07..6b319d0d73ad 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -408,9 +408,11 @@ struct v4l2_format_info { const struct v4l2_format_info *v4l2_format_info(u32 format); -int v4l2_fill_pixfmt(struct v4l2_pix_format *pixfmt, int pixelformat, - int width, int height); -int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt, int pixelformat, - int width, int height); +void v4l2_apply_frmsize_constraints(u32 *width, u32 *height, + const struct v4l2_frmsize_stepwise *frmsize); +int v4l2_fill_pixfmt(struct v4l2_pix_format *pixfmt, u32 pixelformat, + u32 width, u32 height); +int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt, u32 pixelformat, + u32 width, u32 height); #endif /* V4L2_COMMON_H_ */ diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index bfa2a4527040..b4433483af23 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -14,11 +14,12 @@ #include <media/media-request.h> /* - * Include the mpeg2 and fwht stateless codec compound control definitions. + * Include the stateless codec compound control definitions. * This will move to the public headers once this API is fully stable. */ #include <media/mpeg2-ctrls.h> #include <media/fwht-ctrls.h> +#include <media/h264-ctrls.h> /* forward references */ struct file; @@ -42,6 +43,11 @@ struct poll_table_struct; * @p_mpeg2_slice_params: Pointer to a MPEG2 slice parameters structure. * @p_mpeg2_quantization: Pointer to a MPEG2 quantization data structure. * @p_fwht_params: Pointer to a FWHT stateless parameters structure. + * @p_h264_sps: Pointer to a struct v4l2_ctrl_h264_sps. + * @p_h264_pps: Pointer to a struct v4l2_ctrl_h264_pps. + * @p_h264_scaling_matrix: Pointer to a struct v4l2_ctrl_h264_scaling_matrix. + * @p_h264_slice_params: Pointer to a struct v4l2_ctrl_h264_slice_params. + * @p_h264_decode_params: Pointer to a struct v4l2_ctrl_h264_decode_params. * @p: Pointer to a compound value. */ union v4l2_ctrl_ptr { @@ -54,6 +60,11 @@ union v4l2_ctrl_ptr { struct v4l2_ctrl_mpeg2_slice_params *p_mpeg2_slice_params; struct v4l2_ctrl_mpeg2_quantization *p_mpeg2_quantization; struct v4l2_ctrl_fwht_params *p_fwht_params; + struct v4l2_ctrl_h264_sps *p_h264_sps; + struct v4l2_ctrl_h264_pps *p_h264_pps; + struct v4l2_ctrl_h264_scaling_matrix *p_h264_scaling_matrix; + struct v4l2_ctrl_h264_slice_params *p_h264_slice_params; + struct v4l2_ctrl_h264_decode_params *p_h264_decode_params; void *p; }; diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index 8533ece5026e..400f2e46c108 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -26,19 +26,13 @@ struct v4l2_fh; * :ref:`VIDIOC_QUERYCAP <vidioc_querycap>` ioctl * @vidioc_enum_fmt_vid_cap: pointer to the function that implements * :ref:`VIDIOC_ENUM_FMT <vidioc_enum_fmt>` ioctl logic - * for video capture in single plane mode + * for video capture in single and multi plane mode * @vidioc_enum_fmt_vid_overlay: pointer to the function that implements * :ref:`VIDIOC_ENUM_FMT <vidioc_enum_fmt>` ioctl logic * for video overlay * @vidioc_enum_fmt_vid_out: pointer to the function that implements * :ref:`VIDIOC_ENUM_FMT <vidioc_enum_fmt>` ioctl logic - * for video output in single plane mode - * @vidioc_enum_fmt_vid_cap_mplane: pointer to the function that implements - * :ref:`VIDIOC_ENUM_FMT <vidioc_enum_fmt>` ioctl logic - * for video capture in multiplane mode - * @vidioc_enum_fmt_vid_out_mplane: pointer to the function that implements - * :ref:`VIDIOC_ENUM_FMT <vidioc_enum_fmt>` ioctl logic - * for video output in multiplane mode + * for video output in single and multi plane mode * @vidioc_enum_fmt_sdr_cap: pointer to the function that implements * :ref:`VIDIOC_ENUM_FMT <vidioc_enum_fmt>` ioctl logic * for Software Defined Radio capture @@ -313,10 +307,6 @@ struct v4l2_ioctl_ops { struct v4l2_fmtdesc *f); int (*vidioc_enum_fmt_vid_out)(struct file *file, void *fh, struct v4l2_fmtdesc *f); - int (*vidioc_enum_fmt_vid_cap_mplane)(struct file *file, void *fh, - struct v4l2_fmtdesc *f); - int (*vidioc_enum_fmt_vid_out_mplane)(struct file *file, void *fh, - struct v4l2_fmtdesc *f); int (*vidioc_enum_fmt_sdr_cap)(struct file *file, void *fh, struct v4l2_fmtdesc *f); int (*vidioc_enum_fmt_sdr_out)(struct file *file, void *fh, diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h index 864c26dfb854..0b9c3a287061 100644 --- a/include/media/v4l2-mem2mem.h +++ b/include/media/v4l2-mem2mem.h @@ -668,6 +668,10 @@ int v4l2_m2m_ioctl_streamon(struct file *file, void *fh, enum v4l2_buf_type type); int v4l2_m2m_ioctl_streamoff(struct file *file, void *fh, enum v4l2_buf_type type); +int v4l2_m2m_ioctl_try_encoder_cmd(struct file *file, void *fh, + struct v4l2_encoder_cmd *ec); +int v4l2_m2m_ioctl_try_decoder_cmd(struct file *file, void *fh, + struct v4l2_decoder_cmd *dc); int v4l2_m2m_fop_mmap(struct file *file, struct vm_area_struct *vma); __poll_t v4l2_m2m_fop_poll(struct file *file, poll_table *wait); diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 22f3ff76a8b5..c03ef7cc5071 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -54,7 +54,8 @@ struct vb2_threadio_data; * will then be passed as @buf_priv argument to other ops in this * structure. Additional gfp_flags to use when allocating the * are also passed to this operation. These flags are from the - * gfp_flags field of vb2_queue. + * gfp_flags field of vb2_queue. The size argument to this function + * shall be *page aligned*. * @put: inform the allocator that the buffer will no longer be used; * usually will result in the allocator freeing the buffer (if * no other users of this buffer are present); the @buf_priv diff --git a/include/media/videobuf2-memops.h b/include/media/videobuf2-memops.h index 4b5b84f93538..cd4a46331531 100644 --- a/include/media/videobuf2-memops.h +++ b/include/media/videobuf2-memops.h @@ -34,8 +34,7 @@ struct vb2_vmarea_handler { extern const struct vm_operations_struct vb2_common_vm_ops; struct frame_vector *vb2_create_framevec(unsigned long start, - unsigned long length, - bool write); + unsigned long length); void vb2_destroy_framevec(struct frame_vector *vec); #endif diff --git a/include/uapi/linux/cec.h b/include/uapi/linux/cec.h index 3094af68b6e7..5704fa0292b5 100644 --- a/include/uapi/linux/cec.h +++ b/include/uapi/linux/cec.h @@ -144,6 +144,7 @@ static inline void cec_msg_set_reply_to(struct cec_msg *msg, /* cec_msg flags field */ #define CEC_MSG_FL_REPLY_TO_FOLLOWERS (1 << 0) +#define CEC_MSG_FL_RAW (1 << 1) /* cec_msg tx/rx_status field */ #define CEC_TX_STATUS_OK (1 << 0) diff --git a/include/uapi/linux/dvb/audio.h b/include/uapi/linux/dvb/audio.h index afeae063e640..2f869da69171 100644 --- a/include/uapi/linux/dvb/audio.h +++ b/include/uapi/linux/dvb/audio.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ WITH Linux-syscall-note */ /* - * audio.h + * audio.h - DEPRECATED MPEG-TS audio decoder API + * + * NOTE: should not be used on future drivers * * Copyright (C) 2000 Ralph Metzler <ralph@convergence.de> * & Marcus Metzler <marcus@convergence.de> @@ -52,7 +54,7 @@ typedef enum { typedef struct audio_mixer { unsigned int volume_left; unsigned int volume_right; - // what else do we need? bass, pass-through, ... + /* what else do we need? bass, pass-through, ... */ } audio_mixer_t; diff --git a/include/uapi/linux/dvb/osd.h b/include/uapi/linux/dvb/osd.h index e163508b9ae8..858997c74043 100644 --- a/include/uapi/linux/dvb/osd.h +++ b/include/uapi/linux/dvb/osd.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ WITH Linux-syscall-note */ /* - * osd.h + * osd.h - DEPRECATED On Screen Display API + * + * NOTE: should not be used on future drivers * * Copyright (C) 2001 Ralph Metzler <ralph@convergence.de> * & Marcus Metzler <marcus@convergence.de> @@ -28,74 +30,108 @@ #include <linux/compiler.h> typedef enum { - // All functions return -2 on "not open" - OSD_Close=1, // () - // Disables OSD and releases the buffers - // returns 0 on success - OSD_Open, // (x0,y0,x1,y1,BitPerPixel[2/4/8](color&0x0F),mix[0..15](color&0xF0)) - // Opens OSD with this size and bit depth - // returns 0 on success, -1 on DRAM allocation error, -2 on "already open" - OSD_Show, // () - // enables OSD mode - // returns 0 on success - OSD_Hide, // () - // disables OSD mode - // returns 0 on success - OSD_Clear, // () - // Sets all pixel to color 0 - // returns 0 on success - OSD_Fill, // (color) - // Sets all pixel to color <col> - // returns 0 on success - OSD_SetColor, // (color,R{x0},G{y0},B{x1},opacity{y1}) - // set palette entry <num> to <r,g,b>, <mix> and <trans> apply - // R,G,B: 0..255 - // R=Red, G=Green, B=Blue - // opacity=0: pixel opacity 0% (only video pixel shows) - // opacity=1..254: pixel opacity as specified in header - // opacity=255: pixel opacity 100% (only OSD pixel shows) - // returns 0 on success, -1 on error - OSD_SetPalette, // (firstcolor{color},lastcolor{x0},data) - // Set a number of entries in the palette - // sets the entries "firstcolor" through "lastcolor" from the array "data" - // data has 4 byte for each color: - // R,G,B, and a opacity value: 0->transparent, 1..254->mix, 255->pixel - OSD_SetTrans, // (transparency{color}) - // Sets transparency of mixed pixel (0..15) - // returns 0 on success - OSD_SetPixel, // (x0,y0,color) - // sets pixel <x>,<y> to color number <col> - // returns 0 on success, -1 on error - OSD_GetPixel, // (x0,y0) - // returns color number of pixel <x>,<y>, or -1 - OSD_SetRow, // (x0,y0,x1,data) - // fills pixels x0,y through x1,y with the content of data[] - // returns 0 on success, -1 on clipping all pixel (no pixel drawn) - OSD_SetBlock, // (x0,y0,x1,y1,increment{color},data) - // fills pixels x0,y0 through x1,y1 with the content of data[] - // inc contains the width of one line in the data block, - // inc<=0 uses blockwidth as linewidth - // returns 0 on success, -1 on clipping all pixel - OSD_FillRow, // (x0,y0,x1,color) - // fills pixels x0,y through x1,y with the color <col> - // returns 0 on success, -1 on clipping all pixel - OSD_FillBlock, // (x0,y0,x1,y1,color) - // fills pixels x0,y0 through x1,y1 with the color <col> - // returns 0 on success, -1 on clipping all pixel - OSD_Line, // (x0,y0,x1,y1,color) - // draw a line from x0,y0 to x1,y1 with the color <col> - // returns 0 on success - OSD_Query, // (x0,y0,x1,y1,xasp{color}}), yasp=11 - // fills parameters with the picture dimensions and the pixel aspect ratio - // returns 0 on success - OSD_Test, // () - // draws a test picture. for debugging purposes only - // returns 0 on success -// TODO: remove "test" in final version - OSD_Text, // (x0,y0,size,color,text) - OSD_SetWindow, // (x0) set window with number 0<x0<8 as current - OSD_MoveWindow, // move current window to (x0, y0) - OSD_OpenRaw, // Open other types of OSD windows + /* All functions return -2 on "not open" */ + OSD_Close = 1, /* () */ + /* + * Disables OSD and releases the buffers + * returns 0 on success + */ + OSD_Open, /* (x0,y0,x1,y1,BitPerPixel[2/4/8](color&0x0F),mix[0..15](color&0xF0)) */ + /* + * Opens OSD with this size and bit depth + * returns 0 on success, -1 on DRAM allocation error, -2 on "already open" + */ + OSD_Show, /* () */ + /* + * enables OSD mode + * returns 0 on success + */ + OSD_Hide, /* () */ + /* + * disables OSD mode + * returns 0 on success + */ + OSD_Clear, /* () */ + /* + * Sets all pixel to color 0 + * returns 0 on success + */ + OSD_Fill, /* (color) */ + /* + * Sets all pixel to color <col> + * returns 0 on success + */ + OSD_SetColor, /* (color,R{x0},G{y0},B{x1},opacity{y1}) */ + /* + * set palette entry <num> to <r,g,b>, <mix> and <trans> apply + * R,G,B: 0..255 + * R=Red, G=Green, B=Blue + * opacity=0: pixel opacity 0% (only video pixel shows) + * opacity=1..254: pixel opacity as specified in header + * opacity=255: pixel opacity 100% (only OSD pixel shows) + * returns 0 on success, -1 on error + */ + OSD_SetPalette, /* (firstcolor{color},lastcolor{x0},data) */ + /* + * Set a number of entries in the palette + * sets the entries "firstcolor" through "lastcolor" from the array "data" + * data has 4 byte for each color: + * R,G,B, and a opacity value: 0->transparent, 1..254->mix, 255->pixel + */ + OSD_SetTrans, /* (transparency{color}) */ + /* + * Sets transparency of mixed pixel (0..15) + * returns 0 on success + */ + OSD_SetPixel, /* (x0,y0,color) */ + /* + * sets pixel <x>,<y> to color number <col> + * returns 0 on success, -1 on error + */ + OSD_GetPixel, /* (x0,y0) */ + /* returns color number of pixel <x>,<y>, or -1 */ + OSD_SetRow, /* (x0,y0,x1,data) */ + /* + * fills pixels x0,y through x1,y with the content of data[] + * returns 0 on success, -1 on clipping all pixel (no pixel drawn) + */ + OSD_SetBlock, /* (x0,y0,x1,y1,increment{color},data) */ + /* + * fills pixels x0,y0 through x1,y1 with the content of data[] + * inc contains the width of one line in the data block, + * inc<=0 uses blockwidth as linewidth + * returns 0 on success, -1 on clipping all pixel + */ + OSD_FillRow, /* (x0,y0,x1,color) */ + /* + * fills pixels x0,y through x1,y with the color <col> + * returns 0 on success, -1 on clipping all pixel + */ + OSD_FillBlock, /* (x0,y0,x1,y1,color) */ + /* + * fills pixels x0,y0 through x1,y1 with the color <col> + * returns 0 on success, -1 on clipping all pixel + */ + OSD_Line, /* (x0,y0,x1,y1,color) */ + /* + * draw a line from x0,y0 to x1,y1 with the color <col> + * returns 0 on success + */ + OSD_Query, /* (x0,y0,x1,y1,xasp{color}}), yasp=11 */ + /* + * fills parameters with the picture dimensions and the pixel aspect ratio + * returns 0 on success + */ + OSD_Test, /* () */ + /* + * draws a test picture. for debugging purposes only + * returns 0 on success + * TODO: remove "test" in final version + */ + OSD_Text, /* (x0,y0,size,color,text) */ + OSD_SetWindow, /* (x0) set window with number 0<x0<8 as current */ + OSD_MoveWindow, /* move current window to (x0, y0) */ + OSD_OpenRaw, /* Open other types of OSD windows */ } OSD_Command; typedef struct osd_cmd_s { diff --git a/include/uapi/linux/dvb/video.h b/include/uapi/linux/dvb/video.h index 43ba8b0a3d14..179f1ec60af6 100644 --- a/include/uapi/linux/dvb/video.h +++ b/include/uapi/linux/dvb/video.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ WITH Linux-syscall-note */ /* - * video.h + * video.h - DEPRECATED MPEG-TS video decoder API + * + * NOTE: should not be used on future drivers * * Copyright (C) 2000 Marcus Metzler <marcus@convergence.de> * & Ralph Metzler <ralph@convergence.de> diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 37807f23231e..a2669b79b294 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -392,8 +392,13 @@ enum v4l2_mpeg_video_header_mode { #define V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE (V4L2_CID_MPEG_BASE+221) enum v4l2_mpeg_video_multi_slice_mode { V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE = 0, + V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_MB = 1, + V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_MAX_BYTES = 2, +#ifndef __KERNEL__ + /* Kept for backwards compatibility reasons. Stupid typo... */ V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB = 1, V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES = 2, +#endif }; #define V4L2_CID_MPEG_VIDEO_VBV_SIZE (V4L2_CID_MPEG_BASE+222) #define V4L2_CID_MPEG_VIDEO_DEC_PTS (V4L2_CID_MPEG_BASE+223) @@ -404,6 +409,24 @@ enum v4l2_mpeg_video_multi_slice_mode { #define V4L2_CID_MPEG_VIDEO_MV_V_SEARCH_RANGE (V4L2_CID_MPEG_BASE+228) #define V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME (V4L2_CID_MPEG_BASE+229) +/* CIDs for the MPEG-2 Part 2 (H.262) codec */ +#define V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL (V4L2_CID_MPEG_BASE+270) +enum v4l2_mpeg_video_mpeg2_level { + V4L2_MPEG_VIDEO_MPEG2_LEVEL_LOW = 0, + V4L2_MPEG_VIDEO_MPEG2_LEVEL_MAIN = 1, + V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH_1440 = 2, + V4L2_MPEG_VIDEO_MPEG2_LEVEL_HIGH = 3, +}; +#define V4L2_CID_MPEG_VIDEO_MPEG2_PROFILE (V4L2_CID_MPEG_BASE+271) +enum v4l2_mpeg_video_mpeg2_profile { + V4L2_MPEG_VIDEO_MPEG2_PROFILE_SIMPLE = 0, + V4L2_MPEG_VIDEO_MPEG2_PROFILE_MAIN = 1, + V4L2_MPEG_VIDEO_MPEG2_PROFILE_SNR_SCALABLE = 2, + V4L2_MPEG_VIDEO_MPEG2_PROFILE_SPATIALLY_SCALABLE = 3, + V4L2_MPEG_VIDEO_MPEG2_PROFILE_HIGH = 4, + V4L2_MPEG_VIDEO_MPEG2_PROFILE_MULTIVIEW = 5, +}; + /* CIDs for the FWHT codec as used by the vicodec driver. */ #define V4L2_CID_FWHT_I_FRAME_QP (V4L2_CID_MPEG_BASE + 290) #define V4L2_CID_FWHT_P_FRAME_QP (V4L2_CID_MPEG_BASE + 291) diff --git a/samples/v4l/v4l2-pci-skeleton.c b/samples/v4l/v4l2-pci-skeleton.c index 758ced8c3d06..f6a551bd57ef 100644 --- a/samples/v4l/v4l2-pci-skeleton.c +++ b/samples/v4l/v4l2-pci-skeleton.c @@ -58,6 +58,7 @@ MODULE_LICENSE("GPL v2"); * @queue: vb2 video capture queue * @qlock: spinlock controlling access to buf_list and sequence * @buf_list: list of buffers queued for DMA + * @field: the field (TOP/BOTTOM/other) of the current buffer * @sequence: frame sequence counter */ struct skeleton { |