mirror of
https://github.com/brain-hackers/linux-brain.git
synced 2024-06-09 23:36:23 +09:00
Merge branch 'capture/next' into next
* capture/next: (68 commits) LF-237 media: ov5640_mipi: fix regulator dump when i2c access for camera fail staging: media: imx: add g_parm/s_parm for imx8 capture device staging: media: imx: enable ISI for imx8mn platform media: staging: imx: add media device driver support for IMX8 LF-115: media: i2c: ov5640: fix enumerate capture mode issue ...
This commit is contained in:
commit
1e188020fb
33
Documentation/devicetree/bindings/media/imx8-isi.txt
Normal file
33
Documentation/devicetree/bindings/media/imx8-isi.txt
Normal file
|
@ -0,0 +1,33 @@
|
|||
NXP Image Sensor Interface
|
||||
========================
|
||||
|
||||
The Image Sensor Interface (ISI) is used to obtain the image data for
|
||||
processing in its pipeline channels. Each pipeline processes the image
|
||||
line from a configured source and performs one or more functions that
|
||||
are configured by software, such as down scaling, color space conversion,
|
||||
de-interlacing, alpha insertion, cropping and rotation (horizontal and
|
||||
vertical). The processed image is stored into programmable memory locations.
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "fsl,imx8-isi", where SoC can be one of imx8qxp, imx8qm
|
||||
- reg: the register base and size for the device registers
|
||||
- interrupts: the ISI interrupt, high level active
|
||||
- clock-names: should be "per"
|
||||
- clocks: the ISI AXI clock
|
||||
- interface: specify ISI input, virtual channel and output,
|
||||
<Input MIPI_VCx Output>
|
||||
Input : 0-DC0, 1-DC1, 2-MIPI CSI0, 3-MIPI CSI1, 4-HDMI, 5-MEM
|
||||
VCx : 0-VC0, 1-VC1, 2-VC2, 3-VC3, MIPI CSI only
|
||||
Output: 0-DC0, 1-DC1, 2-MEM
|
||||
|
||||
Example:
|
||||
isi_0: isi@58100000 {
|
||||
compatible = "fsl,imx8-isi";
|
||||
reg = <0x58100000 0x10000>;
|
||||
interrupts = <GIC_SPI 297 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-parent = <&gic>;
|
||||
clocks = <&img_lpcg IMX_IMG_LPCG_PDMA0_CLK>;
|
||||
clock-names = "per";
|
||||
power-domains = <&pd IMX_SC_R_ISI_CH0>;
|
||||
interface = <2 0 2>;
|
||||
};
|
107
Documentation/devicetree/bindings/media/imx8-jpeg.txt
Normal file
107
Documentation/devicetree/bindings/media/imx8-jpeg.txt
Normal file
|
@ -0,0 +1,107 @@
|
|||
Freescale i.MX8QXP/QM JPEG encoder/decoder
|
||||
=========================
|
||||
|
||||
jpegdec node
|
||||
--------------
|
||||
|
||||
This is the device node for the JPEG decoder in i.MXQXP/QM SoC, an
|
||||
ISO/IEC 10918-1 JPEG standard compliant decoder, for Baseline
|
||||
and Extended Sequential DCT modes.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "fsl,imx8-jpgdec";
|
||||
- reg : base address and length of the register set for the device;
|
||||
- interrupts : list of interrupts for jpeg decoder
|
||||
- clocks : list of clock specifiers, see
|
||||
Documentation/devicetree/bindings/clock/clock-bindings.txt for details;
|
||||
- assigned-clock-rates : the value should be 200MHz;
|
||||
- power-domains : a list of phandle to the power domain, see
|
||||
Documentation/devicetree/bindings/power/power_domain.txt for details;
|
||||
|
||||
|
||||
Optional properties:
|
||||
- clock-names : must contain clock names to match entries in the
|
||||
clock property;
|
||||
- power-domain-name : must contain matching names for entries in the
|
||||
the power-domains property.
|
||||
|
||||
|
||||
example:
|
||||
|
||||
jpegdec: jpegdec@58400000 {
|
||||
compatible = "fsl,imx8-jpgdec";
|
||||
reg = <0x58400000 0x00050000 >;
|
||||
interrupts = <GIC_SPI 309 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 310 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 311 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 312 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&img_jpeg_dec_clk 0>,
|
||||
<&img_jpeg_dec_clk 1>;
|
||||
clock-names = "per", "ipg";
|
||||
assigned-clocks = <&img_jpeg_dec_clk 0>,
|
||||
<&img_jpeg_dec_clk 1>;
|
||||
assigned-clock-rates = <200000000>;
|
||||
power-domains = <&pd IMX_SC_R_ISI_CH0>,
|
||||
<&pd IMX_SC_R_MJPEG_DEC_MP>,
|
||||
<&pd IMX_SC_R_MJPEG_DEC_S0>,
|
||||
<&pd IMX_SC_R_MJPEG_DEC_S1>,
|
||||
<&pd IMX_SC_R_MJPEG_DEC_S2>,
|
||||
<&pd IMX_SC_R_MJPEG_DEC_S3>;
|
||||
power-domain-names = "pd_isi_ch0", "pd_dec_mp",
|
||||
"pd_dec_s0", "pd_dec_s1",
|
||||
"pd_dec_s2", "pd_dec_s3";
|
||||
status = "disabled";
|
||||
|
||||
|
||||
jpegenc node
|
||||
--------------
|
||||
|
||||
This is the device node for the JPEG encoder in i.MXQXP/QM SoC,
|
||||
similar with the JPEG decoder above.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "fsl,imx8-jpgenc";
|
||||
- reg : base address and length of the register set for the device;
|
||||
- interrupts : list of interrupts for jpeg encoder
|
||||
- clocks : list of clock specifiers, see
|
||||
Documentation/devicetree/bindings/clock/clock-bindings.txt for details;
|
||||
- assigned-clock-rates : the value should be 200MHz;
|
||||
- power-domains : a list of phandle to the power domain, see
|
||||
Documentation/devicetree/bindings/power/power_domain.txt for details;
|
||||
|
||||
|
||||
Optional properties:
|
||||
- clock-names : must contain clock names to match entries in the
|
||||
clock property;
|
||||
- power-domain-name : must contain matching names for entries in the
|
||||
the power-domains property.
|
||||
|
||||
|
||||
example:
|
||||
|
||||
jpegenc: jpegenc@58450000 {
|
||||
compatible = "fsl,imx8-jpgenc";
|
||||
reg = <0x58450000 0x00050000 >;
|
||||
interrupts = <GIC_SPI 305 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 306 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 307 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 308 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&img_jpeg_enc_clk 0>,
|
||||
<&img_jpeg_enc_clk 1>;
|
||||
clock-names = "per", "ipg";
|
||||
assigned-clocks = <&img_jpeg_enc_clk 0>,
|
||||
<&img_jpeg_enc_clk 1>;
|
||||
assigned-clock-rates = <200000000>;
|
||||
power-domains = <&pd IMX_SC_R_ISI_CH0>,
|
||||
<&pd IMX_SC_R_MJPEG_ENC_MP>,
|
||||
<&pd IMX_SC_R_MJPEG_ENC_S0>,
|
||||
<&pd IMX_SC_R_MJPEG_ENC_S1>,
|
||||
<&pd IMX_SC_R_MJPEG_ENC_S2>,
|
||||
<&pd IMX_SC_R_MJPEG_ENC_S3>;
|
||||
power-domain-names = "pd_isi_ch0", "pd_enc_mp",
|
||||
"pd_enc_s0", "pd_enc_s1",
|
||||
"pd_enc_s2", "pd_enc_s3";
|
||||
status = "disabled";
|
||||
};
|
38
Documentation/devicetree/bindings/media/imx8-media-dev.txt
Normal file
38
Documentation/devicetree/bindings/media/imx8-media-dev.txt
Normal file
|
@ -0,0 +1,38 @@
|
|||
Virtual Media device
|
||||
-------------------------------
|
||||
|
||||
Virtual Media device is used to manage all modules in image capture subsystem
|
||||
of imx8qxp/qm platform. ISI(Image Sensor Interface), MIPI CSI, Parallel CSI
|
||||
device node should be under it.
|
||||
|
||||
Required properties:
|
||||
- compatible : must be "fsl,mxc-md";
|
||||
- reg : Must contain an entry for each entry in reg-names;
|
||||
- #address-cells: should be <1>;
|
||||
- #size-cells : should be <1>;
|
||||
- ranges : use to handle address space
|
||||
|
||||
Optional properties:
|
||||
- parallel_csi: indicate that camera sensor use parallel interface
|
||||
|
||||
|
||||
|
||||
For example:
|
||||
|
||||
cameradev: camera {
|
||||
compatible = "fsl,mxc-md", "simple-bus";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
|
||||
isi@58100000 {
|
||||
compatible = "fsl,imx8-isi";
|
||||
reg = <0x58100000 0x10000>;
|
||||
...
|
||||
};
|
||||
csi@58227000 {
|
||||
compatible = "fsl,mxc-mipi-csi2";
|
||||
...
|
||||
};
|
||||
...
|
||||
};
|
73
Documentation/devicetree/bindings/media/imx8-mipi-csi.txt
Normal file
73
Documentation/devicetree/bindings/media/imx8-mipi-csi.txt
Normal file
|
@ -0,0 +1,73 @@
|
|||
Freescale i.MX8QXP/QM MIPI CSI2
|
||||
=========================
|
||||
|
||||
mipi_csi2 node
|
||||
--------------
|
||||
|
||||
This is the device node for the MIPI CSI-2 receiver core in i.MXQXP/QM SoC.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "fsl,mxc-mipi-csi2";
|
||||
- reg : base address and length of the register set for the device;
|
||||
- clocks : list of clock specifiers, see
|
||||
Documentation/devicetree/bindings/clock/clock-bindings.txt for details;
|
||||
- clock-names : must contain "clk_core", "clk_esc" and "clk_pxl" entries,
|
||||
matching entries in the clock property;
|
||||
- assigned-clock-rates : the value should be 360MHz and 72MHz;
|
||||
- power-domains : a phandle to the power domain, see
|
||||
Documentation/devicetree/bindings/power/power_domain.txt for details;
|
||||
- power-domain-name : must contain "pd_csi", "pd_isi_ch0".
|
||||
|
||||
Optional properties:
|
||||
- virtual-channel: whether use mipi csi virtual channel
|
||||
|
||||
The device node should contain one 'port' child nodes with one child 'endpoint'
|
||||
node, according to the bindings defined in:
|
||||
Documentation/devicetree/bindings/ media/video-interfaces.txt.
|
||||
The following are properties specific to those nodes.
|
||||
|
||||
port node
|
||||
---------
|
||||
|
||||
- reg : (required) can take the values 0 which mean the port is a
|
||||
sink port;
|
||||
|
||||
endpoint node
|
||||
-------------
|
||||
|
||||
- data-lanes : (required) an array specifying active physical MIPI-CSI2
|
||||
data input lanes and their mapping to logical lanes; this
|
||||
shall only be applied to port 0 (sink port), the array's
|
||||
content is unused only its length is meaningful,
|
||||
in this case the maximum length supported is 2;
|
||||
|
||||
example:
|
||||
|
||||
mipi_csi: csi@58227000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
compatible = "fsl,mxc-mipi-csi2";
|
||||
reg = <0x58227000 0x1000>,
|
||||
<0x58221000 0x1000>;
|
||||
clocks = <&csi_lpcg IMX_CSI_LPCG_CSI0_CORE_CLK>,
|
||||
<&csi_lpcg IMX_CSI_LPCG_CSI0_ESC_CLK>,
|
||||
<&img_lpcg IMX_IMG_LPCG_CSI0_PXL_LINK_CLK>;
|
||||
clock-names = "clk_core", "clk_esc", "clk_pxl";
|
||||
assigned-clocks = <&csi_lpcg IMX_CSI_LPCG_CSI0_CORE_CLK>,
|
||||
<&csi_lpcg IMX_CSI_LPCG_CSI0_ESC_CLK>;
|
||||
assigned-clock-rates = <360000000>, <72000000>;
|
||||
power-domains = <&pd IMX_SC_R_CSI_0>, <&pd IMX_SC_R_ISI_CH0>;
|
||||
power-domain-names = "pd_csi", "pd_isi_ch0";
|
||||
status = "okay";
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
mipi_csi0_ep: endpoint {
|
||||
remote-endpoint = <&ov5640_mipi_ep>;
|
||||
data-lanes = <1 2>;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
|
@ -0,0 +1,48 @@
|
|||
Freescale i.MX8QXP Parallel Capture Interface
|
||||
=========================
|
||||
|
||||
parallel interface node
|
||||
--------------
|
||||
|
||||
This is the device node for the parallel capture interface in i.MX8QXP SoC.
|
||||
|
||||
Required properties:
|
||||
- compatible : "fsl,mxc-parallel-csi";
|
||||
- reg : base address and length of the register set for the device;
|
||||
- clocks : list of clock specifiers
|
||||
- clock-names : must contain "pixel", "ipg", "div" and "dpll" entries,
|
||||
matching entries in the clock property;
|
||||
- assigned-clocks : need to set the parent of pixel clock;
|
||||
- assigned-clock-parent: set the pll as the parent of pixel clock;
|
||||
- assigned-clock-rates : the value should be 160MHz;
|
||||
- power-domains : a phandle to the power domain, see
|
||||
- power-domain-name : must contain "pd_pi", "pd_isi_ch0".
|
||||
|
||||
port node
|
||||
- reg : can take the values 0 which mean the port is a sink port
|
||||
|
||||
example:
|
||||
parallel_csi: pcsi@58261000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
compatible = "fsl,mxc-parallel-csi";
|
||||
reg = <0x58261000 0x1000>;
|
||||
clocks = <&pi_lpcg IMX_PI_LPCG_PI0_PIXEL_CLK>,
|
||||
<&pi_lpcg IMX_PI_LPCG_PI0_IPG_CLK>,
|
||||
<&clk IMX_PARALLEL_PER_DIV_CLK>,
|
||||
<&clk IMX_PARALLEL_DPLL_CLK>;
|
||||
clock-names = "pixel", "ipg", "div", "dpll";
|
||||
assigned-clocks = <&clk IMX_PARALLEL_PER_DIV_CLK>;
|
||||
assigned-clock-parents = <&clk IMX_PARALLEL_DPLL_CLK>;
|
||||
assigned-clock-rates = <160000000>; /* 160MHz */
|
||||
power-domains = <&pd IMX_SC_R_PI_0>, <&pd IMX_SC_R_ISI_CH0>;
|
||||
power-domain-names = "pd_pi", "pd_isi_ch0";
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
parallel_csi_ep: endpoint {
|
||||
remote-endpoint = <&ov5640_ep>;
|
||||
};
|
||||
};
|
||||
};
|
|
@ -17,7 +17,8 @@ Description
|
|||
===========
|
||||
|
||||
Similar to the packed RGB formats these formats store the Y, Cb and Cr
|
||||
component of each pixel in one 16 or 32 bit word.
|
||||
component of each pixel next to each other in memory. They occupy 16, 24 or 32
|
||||
bits per pixel.
|
||||
|
||||
|
||||
.. raw:: latex
|
||||
|
@ -154,9 +155,40 @@ component of each pixel in one 16 or 32 bit word.
|
|||
- Cb\ :sub:`5`
|
||||
- Cb\ :sub:`4`
|
||||
- Cb\ :sub:`3`
|
||||
|
||||
- :cspan:`15`
|
||||
-
|
||||
* .. _V4L2-PIX-FMT-YUV24:
|
||||
|
||||
- ``V4L2_PIX_FMT_YUV24``
|
||||
- 'YUV3'
|
||||
|
||||
- Y'\ :sub:`7`
|
||||
- Y'\ :sub:`6`
|
||||
- Y'\ :sub:`5`
|
||||
- Y'\ :sub:`4`
|
||||
- Y'\ :sub:`3`
|
||||
- Y'\ :sub:`2`
|
||||
- Y'\ :sub:`1`
|
||||
- Y'\ :sub:`0`
|
||||
|
||||
- Cb\ :sub:`7`
|
||||
- Cb\ :sub:`6`
|
||||
- Cb\ :sub:`5`
|
||||
- Cb\ :sub:`4`
|
||||
- Cb\ :sub:`3`
|
||||
- Cb\ :sub:`2`
|
||||
- Cb\ :sub:`1`
|
||||
- Cb\ :sub:`0`
|
||||
|
||||
- Cr\ :sub:`7`
|
||||
- Cr\ :sub:`6`
|
||||
- Cr\ :sub:`5`
|
||||
- Cr\ :sub:`4`
|
||||
- Cr\ :sub:`3`
|
||||
- Cr\ :sub:`2`
|
||||
- Cr\ :sub:`1`
|
||||
- Cr\ :sub:`0`
|
||||
-
|
||||
* .. _V4L2-PIX-FMT-YUV32:
|
||||
|
||||
- ``V4L2_PIX_FMT_YUV32``
|
||||
|
|
|
@ -190,7 +190,7 @@ config MEDIA_SUBDRV_AUTOSELECT
|
|||
depends on HAS_IOMEM
|
||||
select I2C
|
||||
select I2C_MUX
|
||||
default y if !EMBEDDED
|
||||
default n
|
||||
help
|
||||
By default, a media driver auto-selects all possible ancillary
|
||||
devices such as tuners, sensors, video encoders/decoders and
|
||||
|
|
|
@ -108,7 +108,8 @@ enum ov5640_mode_id {
|
|||
};
|
||||
|
||||
enum ov5640_frame_rate {
|
||||
OV5640_15_FPS = 0,
|
||||
OV5640_08_FPS = 0,
|
||||
OV5640_15_FPS,
|
||||
OV5640_30_FPS,
|
||||
OV5640_60_FPS,
|
||||
OV5640_NUM_FRAMERATES,
|
||||
|
@ -150,6 +151,7 @@ MODULE_PARM_DESC(virtual_channel,
|
|||
"MIPI CSI-2 virtual channel (0..3), default 0");
|
||||
|
||||
static const int ov5640_framerates[] = {
|
||||
[OV5640_08_FPS] = 8,
|
||||
[OV5640_15_FPS] = 15,
|
||||
[OV5640_30_FPS] = 30,
|
||||
[OV5640_60_FPS] = 60,
|
||||
|
@ -352,10 +354,12 @@ static const struct reg_value ov5640_init_setting_30fps_VGA[] = {
|
|||
{0x583b, 0x28, 0, 0}, {0x583c, 0x42, 0, 0}, {0x583d, 0xce, 0, 0},
|
||||
{0x5025, 0x00, 0, 0}, {0x3a0f, 0x30, 0, 0}, {0x3a10, 0x28, 0, 0},
|
||||
{0x3a1b, 0x30, 0, 0}, {0x3a1e, 0x26, 0, 0}, {0x3a11, 0x60, 0, 0},
|
||||
{0x3a1f, 0x14, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3c00, 0x04, 0, 300},
|
||||
{0x3a1f, 0x14, 0, 0}, {0x3008, 0x42, 0, 0}, {0x3c00, 0x04, 0, 300},
|
||||
{0x302c, 0xc2, 0, 0},
|
||||
};
|
||||
|
||||
static const struct reg_value ov5640_setting_VGA_640_480[] = {
|
||||
{0x3008, 0x42, 0, 0},
|
||||
{0x3c07, 0x08, 0, 0},
|
||||
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
|
||||
{0x3814, 0x31, 0, 0},
|
||||
|
@ -372,9 +376,11 @@ static const struct reg_value ov5640_setting_VGA_640_480[] = {
|
|||
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
|
||||
{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
|
||||
{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
|
||||
{0x3008, 0x02, 0, 10},
|
||||
};
|
||||
|
||||
static const struct reg_value ov5640_setting_XGA_1024_768[] = {
|
||||
{0x3008, 0x42, 0, 0},
|
||||
{0x3c07, 0x08, 0, 0},
|
||||
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
|
||||
{0x3814, 0x31, 0, 0},
|
||||
|
@ -389,11 +395,13 @@ static const struct reg_value ov5640_setting_XGA_1024_768[] = {
|
|||
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
|
||||
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
|
||||
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
|
||||
{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
|
||||
{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x20, 0, 0},
|
||||
{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
|
||||
{0x3008, 0x02, 0, 10}
|
||||
};
|
||||
|
||||
static const struct reg_value ov5640_setting_QVGA_320_240[] = {
|
||||
{0x3008, 0x42, 0, 0},
|
||||
{0x3c07, 0x08, 0, 0},
|
||||
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
|
||||
{0x3814, 0x31, 0, 0},
|
||||
|
@ -410,9 +418,11 @@ static const struct reg_value ov5640_setting_QVGA_320_240[] = {
|
|||
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
|
||||
{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
|
||||
{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
|
||||
{0x3008, 0x02, 0, 10},
|
||||
};
|
||||
|
||||
static const struct reg_value ov5640_setting_QCIF_176_144[] = {
|
||||
{0x3008, 0x42, 0, 0},
|
||||
{0x3c07, 0x08, 0, 0},
|
||||
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
|
||||
{0x3814, 0x31, 0, 0},
|
||||
|
@ -429,9 +439,11 @@ static const struct reg_value ov5640_setting_QCIF_176_144[] = {
|
|||
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
|
||||
{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
|
||||
{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
|
||||
{0x3008, 0x02, 0, 10},
|
||||
};
|
||||
|
||||
static const struct reg_value ov5640_setting_NTSC_720_480[] = {
|
||||
{0x3008, 0x42, 0, 0},
|
||||
{0x3c07, 0x08, 0, 0},
|
||||
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
|
||||
{0x3814, 0x31, 0, 0},
|
||||
|
@ -448,9 +460,11 @@ static const struct reg_value ov5640_setting_NTSC_720_480[] = {
|
|||
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
|
||||
{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
|
||||
{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
|
||||
{0x3008, 0x02, 0, 10},
|
||||
};
|
||||
|
||||
static const struct reg_value ov5640_setting_PAL_720_576[] = {
|
||||
{0x3008, 0x42, 0, 0},
|
||||
{0x3c07, 0x08, 0, 0},
|
||||
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
|
||||
{0x3814, 0x31, 0, 0},
|
||||
|
@ -467,9 +481,11 @@ static const struct reg_value ov5640_setting_PAL_720_576[] = {
|
|||
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
|
||||
{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
|
||||
{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
|
||||
{0x3008, 0x02, 0, 10},
|
||||
};
|
||||
|
||||
static const struct reg_value ov5640_setting_720P_1280_720[] = {
|
||||
{0x3008, 0x42, 0, 0},
|
||||
{0x3c07, 0x07, 0, 0},
|
||||
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
|
||||
{0x3814, 0x31, 0, 0},
|
||||
|
@ -486,6 +502,7 @@ static const struct reg_value ov5640_setting_720P_1280_720[] = {
|
|||
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
|
||||
{0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0},
|
||||
{0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0},
|
||||
{0x3008, 0x02, 0, 15},
|
||||
};
|
||||
|
||||
static const struct reg_value ov5640_setting_1080P_1920_1080[] = {
|
||||
|
@ -517,10 +534,11 @@ static const struct reg_value ov5640_setting_1080P_1920_1080[] = {
|
|||
{0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0},
|
||||
{0x3a15, 0x60, 0, 0}, {0x4407, 0x04, 0, 0},
|
||||
{0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0},
|
||||
{0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0},
|
||||
{0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 10},
|
||||
};
|
||||
|
||||
static const struct reg_value ov5640_setting_QSXGA_2592_1944[] = {
|
||||
{0x3008, 0x42, 0, 0},
|
||||
{0x3c07, 0x08, 0, 0},
|
||||
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
|
||||
{0x3814, 0x11, 0, 0},
|
||||
|
@ -535,8 +553,9 @@ static const struct reg_value ov5640_setting_QSXGA_2592_1944[] = {
|
|||
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
|
||||
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
|
||||
{0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0},
|
||||
{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
|
||||
{0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x20, 0, 0},
|
||||
{0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 70},
|
||||
{0x3008, 0x02, 0, 20},
|
||||
};
|
||||
|
||||
/* power-on sensor init reg table */
|
||||
|
@ -903,6 +922,51 @@ static unsigned long ov5640_calc_sys_clk(struct ov5640_dev *sensor,
|
|||
return best;
|
||||
}
|
||||
|
||||
static int ov5640_check_valid_mode(struct ov5640_dev *sensor,
|
||||
const struct ov5640_mode_info *mode,
|
||||
enum ov5640_frame_rate rate)
|
||||
{
|
||||
struct i2c_client *client = sensor->i2c_client;
|
||||
int ret = 0;
|
||||
|
||||
switch (mode->id) {
|
||||
case OV5640_MODE_QCIF_176_144:
|
||||
case OV5640_MODE_QVGA_320_240:
|
||||
case OV5640_MODE_NTSC_720_480:
|
||||
case OV5640_MODE_PAL_720_576 :
|
||||
case OV5640_MODE_XGA_1024_768:
|
||||
case OV5640_MODE_720P_1280_720:
|
||||
if ((rate != OV5640_15_FPS) &&
|
||||
(rate != OV5640_30_FPS))
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
case OV5640_MODE_VGA_640_480:
|
||||
if ((rate != OV5640_15_FPS) &&
|
||||
(rate != OV5640_30_FPS))
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
case OV5640_MODE_1080P_1920_1080:
|
||||
if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY) {
|
||||
if ((rate != OV5640_15_FPS) &&
|
||||
(rate != OV5640_30_FPS))
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
if ((rate != OV5640_15_FPS))
|
||||
ret = -EINVAL;
|
||||
}
|
||||
break;
|
||||
case OV5640_MODE_QSXGA_2592_1944:
|
||||
if (rate != OV5640_08_FPS)
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
default:
|
||||
dev_err(&client->dev, "Invalid mode (%d)\n", mode->id);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* ov5640_set_mipi_pclk() - Calculate the clock tree configuration values
|
||||
* for the MIPI CSI-2 output.
|
||||
|
@ -1260,7 +1324,7 @@ static int ov5640_set_stream_dvp(struct ov5640_dev *sensor, bool on)
|
|||
* 2: MIPI enable
|
||||
*/
|
||||
ret = ov5640_write_reg(sensor,
|
||||
OV5640_REG_IO_MIPI_CTRL00, on ? 0x18 : 0);
|
||||
OV5640_REG_IO_MIPI_CTRL00, on ? 0x58 : 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -2010,7 +2074,7 @@ static int ov5640_set_power(struct ov5640_dev *sensor, bool on)
|
|||
* [2] = 0 : MIPI interface disabled
|
||||
*/
|
||||
ret = ov5640_write_reg(sensor,
|
||||
OV5640_REG_IO_MIPI_CTRL00, 0x40);
|
||||
OV5640_REG_IO_MIPI_CTRL00, 0x45);
|
||||
if (ret)
|
||||
goto power_off;
|
||||
|
||||
|
@ -2101,11 +2165,11 @@ static int ov5640_try_frame_interval(struct ov5640_dev *sensor,
|
|||
u32 width, u32 height)
|
||||
{
|
||||
const struct ov5640_mode_info *mode;
|
||||
enum ov5640_frame_rate rate = OV5640_15_FPS;
|
||||
enum ov5640_frame_rate rate = OV5640_08_FPS;
|
||||
int minfps, maxfps, best_fps, fps;
|
||||
int i;
|
||||
|
||||
minfps = ov5640_framerates[OV5640_15_FPS];
|
||||
minfps = ov5640_framerates[OV5640_08_FPS];
|
||||
maxfps = ov5640_framerates[OV5640_60_FPS];
|
||||
|
||||
if (fi->numerator == 0) {
|
||||
|
@ -2154,10 +2218,10 @@ static int ov5640_get_fmt(struct v4l2_subdev *sd,
|
|||
else
|
||||
fmt = &sensor->fmt;
|
||||
|
||||
fmt->reserved[1] = (sensor->current_fr == OV5640_30_FPS) ? 30 : 15;
|
||||
format->format = *fmt;
|
||||
|
||||
mutex_unlock(&sensor->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2175,6 +2239,7 @@ static int ov5640_try_fmt_internal(struct v4l2_subdev *sd,
|
|||
return -EINVAL;
|
||||
fmt->width = mode->hact;
|
||||
fmt->height = mode->vact;
|
||||
memset(fmt->reserved, 0, sizeof(fmt->reserved));
|
||||
|
||||
if (new_mode)
|
||||
*new_mode = mode;
|
||||
|
@ -2233,6 +2298,8 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd,
|
|||
if (mbus_fmt->code != sensor->fmt.code)
|
||||
sensor->pending_fmt_change = true;
|
||||
|
||||
if (sensor->pending_mode_change || sensor->pending_fmt_change)
|
||||
sensor->fmt = *mbus_fmt;
|
||||
out:
|
||||
mutex_unlock(&sensor->lock);
|
||||
return ret;
|
||||
|
@ -2744,24 +2811,36 @@ static int ov5640_enum_frame_interval(
|
|||
struct v4l2_subdev_frame_interval_enum *fie)
|
||||
{
|
||||
struct ov5640_dev *sensor = to_ov5640_dev(sd);
|
||||
struct v4l2_fract tpf;
|
||||
int ret;
|
||||
int i, j, count;
|
||||
|
||||
if (fie->pad != 0)
|
||||
return -EINVAL;
|
||||
if (fie->index >= OV5640_NUM_FRAMERATES)
|
||||
return -EINVAL;
|
||||
|
||||
tpf.numerator = 1;
|
||||
tpf.denominator = ov5640_framerates[fie->index];
|
||||
|
||||
ret = ov5640_try_frame_interval(sensor, &tpf,
|
||||
fie->width, fie->height);
|
||||
if (ret < 0)
|
||||
if (fie->width == 0 || fie->height == 0 || fie->code == 0) {
|
||||
pr_warn("Please assign pixel format, width and height.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fie->interval = tpf;
|
||||
return 0;
|
||||
fie->interval.numerator = 1;
|
||||
|
||||
count = 0;
|
||||
for (i = 0; i < OV5640_NUM_FRAMERATES; i++) {
|
||||
for (j = 0; j < OV5640_NUM_MODES; j++) {
|
||||
if (fie->width == ov5640_mode_data[j].hact &&
|
||||
fie->height == ov5640_mode_data[j].vact &&
|
||||
!ov5640_check_valid_mode(sensor, &ov5640_mode_data[j], i))
|
||||
count++;
|
||||
|
||||
if (fie->index == (count - 1)) {
|
||||
fie->interval.denominator = ov5640_framerates[i];
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ov5640_g_frame_interval(struct v4l2_subdev *sd,
|
||||
|
@ -2838,11 +2917,23 @@ static int ov5640_enum_mbus_code(struct v4l2_subdev *sd,
|
|||
static int ov5640_s_stream(struct v4l2_subdev *sd, int enable)
|
||||
{
|
||||
struct ov5640_dev *sensor = to_ov5640_dev(sd);
|
||||
struct i2c_client *client = sensor->i2c_client;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&sensor->lock);
|
||||
|
||||
if (sensor->streaming == !enable) {
|
||||
ret = ov5640_check_valid_mode(sensor,
|
||||
sensor->current_mode,
|
||||
sensor->current_fr);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "Not support WxH@fps=%dx%d@%d\n",
|
||||
sensor->current_mode->hact,
|
||||
sensor->current_mode->vact,
|
||||
ov5640_framerates[sensor->current_fr]);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (enable && sensor->pending_mode_change) {
|
||||
ret = ov5640_set_mode(sensor);
|
||||
if (ret)
|
||||
|
@ -2896,6 +2987,17 @@ static const struct v4l2_subdev_ops ov5640_subdev_ops = {
|
|||
.pad = &ov5640_pad_ops,
|
||||
};
|
||||
|
||||
static int ov5640_link_setup(struct media_entity *entity,
|
||||
const struct media_pad *local,
|
||||
const struct media_pad *remote, u32 flags)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct media_entity_operations ov5640_sd_media_ops = {
|
||||
.link_setup = ov5640_link_setup,
|
||||
};
|
||||
|
||||
static int ov5640_get_regulators(struct ov5640_dev *sensor)
|
||||
{
|
||||
int i;
|
||||
|
@ -3035,6 +3137,7 @@ static int ov5640_probe(struct i2c_client *client)
|
|||
sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
|
||||
V4L2_SUBDEV_FL_HAS_EVENTS;
|
||||
sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
|
||||
sensor->sd.entity.ops = &ov5640_sd_media_ops;
|
||||
sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
|
||||
ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad);
|
||||
if (ret)
|
||||
|
|
|
@ -141,6 +141,20 @@ config VIDEO_RENESAS_CEU
|
|||
help
|
||||
This is a v4l2 driver for the Renesas CEU Interface
|
||||
|
||||
config VIDEO_MX8_CAPTURE
|
||||
tristate "MX8 Video For Linux Video Capture"
|
||||
depends on VIDEO_V4L2
|
||||
---help---
|
||||
This is the video4linux2 capture driver based on i.MX8 module.
|
||||
|
||||
config VIDEO_MXC_CAPTURE
|
||||
tristate "MXC Video For Linux Video Capture"
|
||||
depends on VIDEO_V4L2
|
||||
---help---
|
||||
This is the video4linux2 capture driver based on i.MX video-in module.
|
||||
|
||||
source "drivers/media/platform/imx8/Kconfig"
|
||||
source "drivers/media/platform/mxc/capture/Kconfig"
|
||||
source "drivers/media/platform/exynos4-is/Kconfig"
|
||||
source "drivers/media/platform/am437x/Kconfig"
|
||||
source "drivers/media/platform/xilinx/Kconfig"
|
||||
|
|
|
@ -84,6 +84,9 @@ obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel/
|
|||
|
||||
obj-$(CONFIG_VIDEO_STM32_DCMI) += stm32/
|
||||
|
||||
obj-$(CONFIG_VIDEO_MX8_CAPTURE) += imx8/
|
||||
obj-$(CONFIG_VIDEO_MXC_CAPTURE) += mxc/capture/
|
||||
|
||||
obj-$(CONFIG_VIDEO_MEDIATEK_VPU) += mtk-vpu/
|
||||
|
||||
obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC) += mtk-vcodec/
|
||||
|
|
17
drivers/media/platform/imx8/Kconfig
Normal file
17
drivers/media/platform/imx8/Kconfig
Normal file
|
@ -0,0 +1,17 @@
|
|||
if VIDEO_MX8_CAPTURE
|
||||
menu "IMX8 Camera ISI/MIPI Features support"
|
||||
|
||||
config IMX8_MIPI_CSI2_YAV
|
||||
tristate "IMX8 MIPI CSI2 Controller Yet Another Version"
|
||||
default y
|
||||
|
||||
config IMX8_JPEG
|
||||
tristate "IMX8 JPEG Encoder/Decoder"
|
||||
depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
||||
select V4L2_MEM2MEM_DEV
|
||||
select VIDEOBUF2_DMA_CONTIG
|
||||
default m
|
||||
|
||||
endmenu
|
||||
endif #VIDEO_MX8_CAPTURE
|
||||
|
3
drivers/media/platform/imx8/Makefile
Normal file
3
drivers/media/platform/imx8/Makefile
Normal file
|
@ -0,0 +1,3 @@
|
|||
obj-$(CONFIG_IMX8_MIPI_CSI2_YAV) += mxc-mipi-csi2_yav.o
|
||||
mxc-jpeg-encdec-objs := mxc-jpeg-hw.o mxc-jpeg.o
|
||||
obj-$(CONFIG_IMX8_JPEG) += mxc-jpeg-encdec.o
|
248
drivers/media/platform/imx8/mxc-jpeg-hw.c
Normal file
248
drivers/media/platform/imx8/mxc-jpeg-hw.c
Normal file
|
@ -0,0 +1,248 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* i.MX8QXP/i.MX8QM JPEG encoder/decoder v4l2 driver
|
||||
*
|
||||
* Copyright 2018-2019 NXP
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <media/videobuf2-core.h>
|
||||
#include "mxc-jpeg-hw.h"
|
||||
|
||||
#define print_wrapper_reg(dev, base_address, reg_offset)\
|
||||
internal_print_wrapper_reg(dev, (base_address), #reg_offset,\
|
||||
(reg_offset))
|
||||
#define internal_print_wrapper_reg(dev, base_address, reg_name, reg_offset) {\
|
||||
int val;\
|
||||
val = readl((base_address) + (reg_offset));\
|
||||
dev_dbg(dev, "Wrapper reg %s = 0x%x\n", reg_name, val);\
|
||||
}
|
||||
|
||||
void print_descriptor_info(struct device *dev, struct mxc_jpeg_desc *desc)
|
||||
{
|
||||
dev_dbg(dev, " MXC JPEG NEXT_DESCPT_PTR 0x%x\n",
|
||||
desc->next_descpt_ptr);
|
||||
dev_dbg(dev, " MXC JPEG BUF_BASE0 0x%x\n", desc->buf_base0);
|
||||
dev_dbg(dev, " MXC JPEG BUF_BASE1 0x%x\n", desc->buf_base1);
|
||||
dev_dbg(dev, " MXC JPEG LINE_PITCH %d\n", desc->line_pitch);
|
||||
dev_dbg(dev, " MXC JPEG STM_BUFBASE 0x%x\n", desc->stm_bufbase);
|
||||
dev_dbg(dev, " MXC JPEG STM_BUFSIZE %d\n", desc->stm_bufsize);
|
||||
dev_dbg(dev, " MXC JPEG IMGSIZE %x (%d x %d)\n", desc->imgsize,
|
||||
desc->imgsize >> 16, desc->imgsize & 0xFFFF);
|
||||
dev_dbg(dev, " MXC JPEG STM_CTRL 0x%x\n", desc->stm_ctrl);
|
||||
}
|
||||
|
||||
void print_cast_status(struct device *dev, void __iomem *reg,
|
||||
unsigned int mode)
|
||||
{
|
||||
dev_dbg(dev, "CAST IP status regs:\n");
|
||||
print_wrapper_reg(dev, reg, CAST_STATUS0);
|
||||
print_wrapper_reg(dev, reg, CAST_STATUS1);
|
||||
print_wrapper_reg(dev, reg, CAST_STATUS2);
|
||||
print_wrapper_reg(dev, reg, CAST_STATUS3);
|
||||
print_wrapper_reg(dev, reg, CAST_STATUS4);
|
||||
print_wrapper_reg(dev, reg, CAST_STATUS5);
|
||||
print_wrapper_reg(dev, reg, CAST_STATUS6);
|
||||
print_wrapper_reg(dev, reg, CAST_STATUS7);
|
||||
print_wrapper_reg(dev, reg, CAST_STATUS8);
|
||||
print_wrapper_reg(dev, reg, CAST_STATUS9);
|
||||
print_wrapper_reg(dev, reg, CAST_STATUS10);
|
||||
print_wrapper_reg(dev, reg, CAST_STATUS11);
|
||||
print_wrapper_reg(dev, reg, CAST_STATUS12);
|
||||
print_wrapper_reg(dev, reg, CAST_STATUS13);
|
||||
if (mode == MXC_JPEG_DECODE)
|
||||
return;
|
||||
print_wrapper_reg(dev, reg, CAST_STATUS14);
|
||||
print_wrapper_reg(dev, reg, CAST_STATUS15);
|
||||
print_wrapper_reg(dev, reg, CAST_STATUS16);
|
||||
print_wrapper_reg(dev, reg, CAST_STATUS17);
|
||||
print_wrapper_reg(dev, reg, CAST_STATUS18);
|
||||
print_wrapper_reg(dev, reg, CAST_STATUS19);
|
||||
}
|
||||
|
||||
void print_wrapper_info(struct device *dev, void __iomem *reg)
|
||||
{
|
||||
dev_dbg(dev, "Wrapper regs:\n");
|
||||
print_wrapper_reg(dev, reg, GLB_CTRL);
|
||||
print_wrapper_reg(dev, reg, COM_STATUS);
|
||||
print_wrapper_reg(dev, reg, BUF_BASE0);
|
||||
print_wrapper_reg(dev, reg, BUF_BASE1);
|
||||
print_wrapper_reg(dev, reg, LINE_PITCH);
|
||||
print_wrapper_reg(dev, reg, STM_BUFBASE);
|
||||
print_wrapper_reg(dev, reg, STM_BUFSIZE);
|
||||
print_wrapper_reg(dev, reg, IMGSIZE);
|
||||
print_wrapper_reg(dev, reg, STM_CTRL);
|
||||
}
|
||||
|
||||
void mxc_jpeg_enable_irq(void __iomem *reg, int slot)
|
||||
{
|
||||
writel(0xFFFFFFFF, reg + MXC_SLOT_OFFSET(slot, SLOT_IRQ_EN));
|
||||
}
|
||||
|
||||
void mxc_jpeg_sw_reset(void __iomem *reg)
|
||||
{
|
||||
/*
|
||||
* engine soft reset, internal state machine reset
|
||||
* this will not reset registers, however, it seems
|
||||
* the registers may remain inconsistent with the internal state
|
||||
* so, on purpose, at least let GLB_CTRL bits clear after this reset
|
||||
*/
|
||||
writel(GLB_CTRL_SFT_RST, reg + GLB_CTRL);
|
||||
}
|
||||
|
||||
u32 mxc_jpeg_get_offset(void __iomem *reg, int slot)
|
||||
{
|
||||
return readl(reg + MXC_SLOT_OFFSET(slot, SLOT_BUF_PTR));
|
||||
}
|
||||
|
||||
void mxc_jpeg_enc_mode_conf(struct device *dev, void __iomem *reg)
|
||||
{
|
||||
dev_dbg(dev, "CAST Encoder CONFIG...\n");
|
||||
/*
|
||||
* "Config_Mode" enabled, "Config_Mode auto clear enabled",
|
||||
*/
|
||||
writel(0xa0, reg + CAST_MODE);
|
||||
|
||||
/* all markers and segments */
|
||||
writel(0x3ff, reg + CAST_CFG_MODE);
|
||||
|
||||
/* quality factor */
|
||||
writel(0x4b, reg + CAST_QUALITY);
|
||||
}
|
||||
|
||||
void mxc_jpeg_enc_mode_go(struct device *dev, void __iomem *reg)
|
||||
{
|
||||
dev_dbg(dev, "CAST Encoder GO...\n");
|
||||
/*
|
||||
* "GO" enabled, "GO bit auto clear" enabled
|
||||
*/
|
||||
writel(0x140, reg + CAST_MODE);
|
||||
}
|
||||
|
||||
void wait_frmdone(struct device *dev, void __iomem *reg)
|
||||
{
|
||||
u32 regval = 0;
|
||||
|
||||
do {
|
||||
regval = readl(reg + MXC_SLOT_OFFSET(0, SLOT_STATUS));
|
||||
} while (!(regval & SLOTa_STATUS_FRMDONE));
|
||||
|
||||
writel(regval, reg + MXC_SLOT_OFFSET(0, SLOT_STATUS)); /* w1c */
|
||||
|
||||
dev_dbg(dev, "Received FRMDONE\n");
|
||||
if (regval & SLOTa_STATUS_ENC_CONFIG_ERR)
|
||||
dev_info(dev, "SLOTa_STATUS_ENC_CONFIG_ERR\n");
|
||||
}
|
||||
|
||||
int mxc_jpeg_enable(void __iomem *reg)
|
||||
{
|
||||
u32 regval;
|
||||
|
||||
writel(GLB_CTRL_JPG_EN, reg + GLB_CTRL);
|
||||
regval = readl(reg);
|
||||
return regval;
|
||||
}
|
||||
|
||||
void mxc_jpeg_dec_mode_go(struct device *dev, void __iomem *reg)
|
||||
{
|
||||
dev_dbg(dev, "CAST Decoder GO...\n");
|
||||
writel(MXC_DEC_EXIT_IDLE_MODE, reg + CAST_CTRL);
|
||||
}
|
||||
|
||||
int mxc_jpeg_get_slot(void __iomem *reg)
|
||||
{
|
||||
int slot_val;
|
||||
int i = 0;
|
||||
int tmp = GLB_CTRL_SLOT_EN(0);
|
||||
|
||||
/* currently enabled slots */
|
||||
slot_val = readl(reg + GLB_CTRL) & 0xF0;
|
||||
|
||||
for (; tmp != tmp << 4; tmp = tmp << 1) {
|
||||
if ((slot_val & tmp) == 0)
|
||||
/* first free slot */
|
||||
return i;
|
||||
++i;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
void mxc_jpeg_enable_slot(void __iomem *reg, int slot)
|
||||
{
|
||||
u32 regval;
|
||||
|
||||
regval = readl(reg + GLB_CTRL);
|
||||
writel(GLB_CTRL_SLOT_EN(slot) | regval, reg + GLB_CTRL);
|
||||
}
|
||||
|
||||
void mxc_jpeg_set_l_endian(void __iomem *reg, int le)
|
||||
{
|
||||
u32 regval;
|
||||
|
||||
regval = readl(reg + GLB_CTRL);
|
||||
regval &= ~GLB_CTRL_L_ENDIAN(1); /* clear */
|
||||
writel(GLB_CTRL_L_ENDIAN(le) | regval, reg + GLB_CTRL); /* set */
|
||||
}
|
||||
|
||||
void mxc_jpeg_set_config_mode(void __iomem *reg, int config_mode)
|
||||
{
|
||||
u32 regval;
|
||||
|
||||
regval = readl(reg + STM_CTRL);
|
||||
regval &= ~STM_CTRL_CONFIG_MOD(1);
|
||||
writel(STM_CTRL_CONFIG_MOD(config_mode) | regval, reg + STM_CTRL);
|
||||
}
|
||||
|
||||
int mxc_jpeg_set_params(struct mxc_jpeg_desc *desc, u32 bufsize,
|
||||
u16 out_pitch, u32 format)
|
||||
{
|
||||
desc->line_pitch = out_pitch;
|
||||
desc->stm_bufsize = bufsize;
|
||||
switch (format) {
|
||||
case V4L2_PIX_FMT_YUV32:
|
||||
desc->stm_ctrl |= MXC_JPEG_YUV444 << 3;
|
||||
break;
|
||||
case V4L2_PIX_FMT_YUYV:
|
||||
desc->stm_ctrl |= MXC_JPEG_YUV422 << 3;
|
||||
break;
|
||||
case V4L2_PIX_FMT_RGB32:
|
||||
desc->stm_ctrl |= MXC_JPEG_RGB << 3;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mxc_jpeg_set_bufsize(struct mxc_jpeg_desc *desc, u32 bufsize)
|
||||
{
|
||||
desc->stm_bufsize = bufsize;
|
||||
}
|
||||
|
||||
void mxc_jpeg_set_res(struct mxc_jpeg_desc *desc, u16 w, u16 h)
|
||||
{
|
||||
desc->imgsize = w << 16 | h;
|
||||
}
|
||||
|
||||
|
||||
void mxc_jpeg_set_line_pitch(struct mxc_jpeg_desc *desc, u32 line_pitch)
|
||||
{
|
||||
desc->line_pitch = line_pitch;
|
||||
}
|
||||
|
||||
void mxc_jpeg_set_desc(u32 desc, void __iomem *reg, int slot)
|
||||
{
|
||||
writel(desc | MXC_NXT_DESCPT_EN,
|
||||
reg + MXC_SLOT_OFFSET(slot, SLOT_NXT_DESCPT_PTR));
|
||||
}
|
||||
|
||||
void mxc_jpeg_set_regs_from_desc(struct mxc_jpeg_desc *desc, void __iomem *reg)
|
||||
{
|
||||
writel(desc->buf_base0, reg + BUF_BASE0);
|
||||
writel(desc->buf_base1, reg + BUF_BASE1);
|
||||
writel(desc->line_pitch, reg + LINE_PITCH);
|
||||
writel(desc->stm_bufbase, reg + STM_BUFBASE);
|
||||
writel(desc->stm_bufsize, reg + STM_BUFSIZE);
|
||||
writel(desc->imgsize, reg + IMGSIZE);
|
||||
writel(desc->stm_ctrl, reg + STM_CTRL);
|
||||
}
|
144
drivers/media/platform/imx8/mxc-jpeg-hw.h
Normal file
144
drivers/media/platform/imx8/mxc-jpeg-hw.h
Normal file
|
@ -0,0 +1,144 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* i.MX8QXP/i.MX8QM JPEG encoder/decoder v4l2 driver
|
||||
*
|
||||
* Copyright 2018-2019 NXP
|
||||
*/
|
||||
|
||||
#ifndef _MXC_JPEG_HW_H
|
||||
#define _MXC_JPEG_HW_H
|
||||
|
||||
#define MXC_JPEG_DECODE 0
|
||||
#define MXC_JPEG_ENCODE 1
|
||||
|
||||
/* JPEG Decoder/Encoder Wrapper Register Map */
|
||||
#define GLB_CTRL 0x0
|
||||
#define COM_STATUS 0x4
|
||||
#define BUF_BASE0 0x14
|
||||
#define BUF_BASE1 0x18
|
||||
#define LINE_PITCH 0x1C
|
||||
#define STM_BUFBASE 0x20
|
||||
#define STM_BUFSIZE 0x24
|
||||
#define IMGSIZE 0x28
|
||||
#define STM_CTRL 0x2C
|
||||
|
||||
/* CAST JPEG-Decoder/Encoder Status Register Map (read-only)*/
|
||||
#define CAST_STATUS0 0x100
|
||||
#define CAST_STATUS1 0x104
|
||||
#define CAST_STATUS2 0x108
|
||||
#define CAST_STATUS3 0x10c
|
||||
#define CAST_STATUS4 0x110
|
||||
#define CAST_STATUS5 0x114
|
||||
#define CAST_STATUS6 0x118
|
||||
#define CAST_STATUS7 0x11c
|
||||
#define CAST_STATUS8 0x120
|
||||
#define CAST_STATUS9 0x124
|
||||
#define CAST_STATUS10 0x128
|
||||
#define CAST_STATUS11 0x12c
|
||||
#define CAST_STATUS12 0x130
|
||||
#define CAST_STATUS13 0x134
|
||||
/* the following are for encoder only */
|
||||
#define CAST_STATUS14 0x138
|
||||
#define CAST_STATUS15 0x13c
|
||||
#define CAST_STATUS16 0x140
|
||||
#define CAST_STATUS17 0x144
|
||||
#define CAST_STATUS18 0x148
|
||||
#define CAST_STATUS19 0x14c
|
||||
|
||||
/* CAST JPEG-Decoder Control Register Map (write-only) */
|
||||
#define CAST_CTRL CAST_STATUS13
|
||||
|
||||
/* CAST JPEG-Encoder Control Register Map (write-only) */
|
||||
#define CAST_MODE CAST_STATUS0
|
||||
#define CAST_CFG_MODE CAST_STATUS1
|
||||
#define CAST_QUALITY CAST_STATUS2
|
||||
#define CAST_RSVD CAST_STATUS3
|
||||
#define CAST_REC_REGS_SEL CAST_STATUS4
|
||||
#define CAST_LUMTH CAST_STATUS5
|
||||
#define CAST_CHRTH CAST_STATUS6
|
||||
#define CAST_NOMFRSIZE_LO CAST_STATUS7
|
||||
#define CAST_NOMFRSIZE_HI CAST_STATUS8
|
||||
#define CAST_OFBSIZE_LO CAST_STATUS9
|
||||
#define CAST_OFBSIZE_HI CAST_STATUS10
|
||||
|
||||
#define MXC_MAX_SLOTS 1 /* TODO use all 4 slots*/
|
||||
/* JPEG-Decoder Wrapper Slot Registers 0..3 */
|
||||
#define SLOT_BASE 0x10000
|
||||
#define SLOT_STATUS 0x0
|
||||
#define SLOT_IRQ_EN 0x4
|
||||
#define SLOT_BUF_PTR 0x8
|
||||
#define SLOT_CUR_DESCPT_PTR 0xC
|
||||
#define SLOT_NXT_DESCPT_PTR 0x10
|
||||
#define MXC_SLOT_OFFSET(slot, offset) ((SLOT_BASE * (slot + 1)) + offset)
|
||||
|
||||
/* GLB_CTRL fields */
|
||||
#define GLB_CTRL_JPG_EN 0x1
|
||||
#define GLB_CTRL_SFT_RST (0x1 << 1)
|
||||
#define GLB_CTRL_DEC_GO (0x1 << 2)
|
||||
#define GLB_CTRL_L_ENDIAN(le) ((le) << 3)
|
||||
#define GLB_CTRL_SLOT_EN(slot) (0x1 << (slot + 4))
|
||||
|
||||
/* COM_STAUS fields */
|
||||
#define COM_STATUS_DEC_ONGOING(r) (((r) & (1 << 31)) >> 31)
|
||||
#define COM_STATUS_CUR_SLOT(r) (((r) & (0x3 << 29)) >> 29)
|
||||
|
||||
/* STM_CTRL fields */
|
||||
#define STM_CTRL_PIXEL_PRECISION (0x1 << 2)
|
||||
#define STM_CTRL_IMAGE_FORMAT(img_fmt) ((img_fmt) << 3)
|
||||
#define STM_CTRL_IMAGE_FORMAT_MASK (0xF << 3)
|
||||
#define STM_CTRL_BITBUF_PTR_CLR(clr) ((clr) << 7)
|
||||
#define STM_CTRL_AUTO_START(go) ((go) << 8)
|
||||
#define STM_CTRL_CONFIG_MOD(mod) ((mod) << 9)
|
||||
|
||||
/* SLOTa_STATUS fields TBD */
|
||||
#define SLOTa_STATUS_FRMDONE (0x1 << 3)
|
||||
#define SLOTa_STATUS_ENC_CONFIG_ERR (0x1 << 8)
|
||||
|
||||
/* SLOTa_IRQ_EN fields TBD */
|
||||
|
||||
#define MXC_NXT_DESCPT_EN 0x1
|
||||
#define MXC_DEC_EXIT_IDLE_MODE 0x4
|
||||
|
||||
/* JPEG-Decoder Wrapper - STM_CTRL Register Fields */
|
||||
#define MXC_PIXEL_PRECISION(precision) ((precision)/8 << 2)
|
||||
enum mxc_jpeg_image_format {
|
||||
MXC_JPEG_INVALID = -1,
|
||||
MXC_JPEG_YUV420 = 0x0, /* 2 Plannar, Y=1st plane UV=2nd plane */
|
||||
MXC_JPEG_YUV422 = 0x1, /* 1 Plannar, YUYV sequence */
|
||||
MXC_JPEG_RGB = 0x2, /* RGBRGB packed format */
|
||||
MXC_JPEG_YUV444 = 0x3, /* 1 Plannar, YUVYUV sequence */
|
||||
MXC_JPEG_GRAY = 0x4, /* Y8 or Y12 or Single Component */
|
||||
MXC_JPEG_RESERVED = 0x5,
|
||||
MXC_JPEG_ARGB = 0x6,
|
||||
};
|
||||
|
||||
|
||||
#include "mxc-jpeg.h"
|
||||
void print_descriptor_info(struct device *dev, struct mxc_jpeg_desc *desc);
|
||||
void print_cast_status(struct device *dev, void __iomem *reg,
|
||||
unsigned int mode);
|
||||
void print_wrapper_info(struct device *dev, void __iomem *reg);
|
||||
void mxc_jpeg_sw_reset(void __iomem *reg);
|
||||
int mxc_jpeg_enable(void __iomem *reg);
|
||||
void wait_frmdone(struct device *dev, void __iomem *reg);
|
||||
void mxc_jpeg_enc_mode_conf(struct device *dev, void __iomem *reg);
|
||||
void mxc_jpeg_enc_mode_go(struct device *dev, void __iomem *reg);
|
||||
void mxc_jpeg_dec_mode_go(struct device *dev, void __iomem *reg);
|
||||
int mxc_jpeg_get_slot(void __iomem *reg);
|
||||
u32 mxc_jpeg_get_offset(void __iomem *reg, int slot);
|
||||
void mxc_jpeg_enable_slot(void __iomem *reg, int slot);
|
||||
void mxc_jpeg_set_l_endian(void __iomem *reg, int le);
|
||||
void mxc_jpeg_enable_irq(void __iomem *reg, int slot);
|
||||
int mxc_jpeg_set_input(void __iomem *reg, u32 in_buf, u32 bufsize);
|
||||
int mxc_jpeg_set_output(void __iomem *reg, u16 out_pitch, u32 out_buf,
|
||||
u16 w, u16 h);
|
||||
void mxc_jpeg_set_config_mode(void __iomem *reg, int config_mode);
|
||||
int mxc_jpeg_set_params(struct mxc_jpeg_desc *desc, u32 bufsize, u16
|
||||
out_pitch, u32 format);
|
||||
void mxc_jpeg_set_bufsize(struct mxc_jpeg_desc *desc, u32 bufsize);
|
||||
void mxc_jpeg_set_res(struct mxc_jpeg_desc *desc, u16 w, u16 h);
|
||||
void mxc_jpeg_set_line_pitch(struct mxc_jpeg_desc *desc, u32 line_pitch);
|
||||
void mxc_jpeg_set_desc(u32 desc, void __iomem *reg, int slot);
|
||||
void mxc_jpeg_set_regs_from_desc(struct mxc_jpeg_desc *desc,
|
||||
void __iomem *reg);
|
||||
#endif
|
2284
drivers/media/platform/imx8/mxc-jpeg.c
Normal file
2284
drivers/media/platform/imx8/mxc-jpeg.c
Normal file
File diff suppressed because it is too large
Load Diff
170
drivers/media/platform/imx8/mxc-jpeg.h
Normal file
170
drivers/media/platform/imx8/mxc-jpeg.h
Normal file
|
@ -0,0 +1,170 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* i.MX8QXP/i.MX8QM JPEG encoder/decoder v4l2 driver
|
||||
*
|
||||
* Copyright 2018-2019 NXP
|
||||
*/
|
||||
|
||||
#include <media/v4l2-ctrls.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <media/v4l2-fh.h>
|
||||
|
||||
#ifndef _MXC_JPEG_CORE_H
|
||||
#define _MXC_JPEG_CORE_H
|
||||
|
||||
#define MXC_JPEG_M2M_NAME "mxc-jpeg"
|
||||
#define MXC_JPEG_NAME "mxc-jpeg"
|
||||
#define MXC_IN_FORMAT 0
|
||||
#define MXC_OUT_FORMAT 1
|
||||
#define MXC_JPEG_FMT_TYPE_ENC 0
|
||||
#define MXC_JPEG_FMT_TYPE_RAW 1
|
||||
#define MXC_JPEG_DEFAULT_WIDTH 1280
|
||||
#define MXC_JPEG_DEFAULT_HEIGHT 720
|
||||
#define MXC_JPEG_DEFAULT_PFMT V4L2_PIX_FMT_RGB24
|
||||
#define MXC_JPEG_MIN_WIDTH 64
|
||||
#define MXC_JPEG_MIN_HEIGHT 64
|
||||
#define MXC_JPEG_MAX_WIDTH 0x2000
|
||||
#define MXC_JPEG_MAX_HEIGHT 0x2000
|
||||
#define MXC_JPEG_MAX_CFG_STREAM 0x1000
|
||||
#define MXC_JPEG_H_ALIGN 3
|
||||
#define MXC_JPEG_W_ALIGN 3
|
||||
#define MXC_JPEG_DEFAULT_SIZEIMAGE (6 * MXC_JPEG_DEFAULT_WIDTH * \
|
||||
MXC_JPEG_DEFAULT_HEIGHT)
|
||||
#define MXC_JPEG_MAX_SIZEIMAGE 0xFFFFFC00
|
||||
#define MXC_JPEG_ENC_CONF 1
|
||||
#define MXC_JPEG_ENC_DONE 0
|
||||
#define SOF0 0xC0
|
||||
#define SOF1 0xC1
|
||||
#define SOF2 0xC2
|
||||
#define SOS 0xDA
|
||||
#define DHT 0xC4
|
||||
#define APP14 0xEE
|
||||
#define MXC_JPEG_ENC_CONF_DONE 1
|
||||
#define MXC_JPEG_MAX_PLANES 2
|
||||
#define MXC_JPEG_NUM_PD 6
|
||||
|
||||
|
||||
/**
|
||||
* struct jpeg_fmt - driver's internal color format data
|
||||
* @name: format description
|
||||
* @fourcc: fourcc code, 0 if not applicable
|
||||
* @depth: number of bits per pixel
|
||||
* @colplanes: number of color planes (1 for packed formats)
|
||||
* @h_align: horizontal alignment order (align to 2^h_align)
|
||||
* @v_align: vertical alignment order (align to 2^v_align)
|
||||
* @flags: flags describing format applicability
|
||||
*/
|
||||
struct mxc_jpeg_fmt {
|
||||
char *name;
|
||||
u32 fourcc;
|
||||
int depth;
|
||||
int colplanes;
|
||||
int memplanes;
|
||||
int h_align;
|
||||
int v_align;
|
||||
int subsampling;
|
||||
u32 flags;
|
||||
};
|
||||
struct mxc_jpeg_desc {
|
||||
u32 next_descpt_ptr;
|
||||
u32 buf_base0;
|
||||
u32 buf_base1;
|
||||
u32 line_pitch;
|
||||
u32 stm_bufbase;
|
||||
u32 stm_bufsize;
|
||||
u32 imgsize;
|
||||
u32 stm_ctrl;
|
||||
} __packed;
|
||||
|
||||
struct mxc_jpeg_q_data {
|
||||
struct mxc_jpeg_fmt *fmt;
|
||||
u32 sizeimage[MXC_JPEG_MAX_PLANES];
|
||||
u32 bytesperline[MXC_JPEG_MAX_PLANES];
|
||||
int w;
|
||||
int w_adjusted;
|
||||
int h;
|
||||
int h_adjusted;
|
||||
};
|
||||
struct mxc_jpeg_ctx {
|
||||
struct mxc_jpeg_dev *mxc_jpeg;
|
||||
struct mxc_jpeg_q_data out_q;
|
||||
struct mxc_jpeg_q_data cap_q;
|
||||
struct v4l2_rect crop_rect;
|
||||
unsigned long state;
|
||||
struct v4l2_fh fh;
|
||||
unsigned int mode;
|
||||
unsigned int enc_state;
|
||||
unsigned int aborting;
|
||||
unsigned int stopping;
|
||||
unsigned int dht_needed;
|
||||
unsigned int slot;
|
||||
enum v4l2_colorspace colorspace;
|
||||
enum v4l2_ycbcr_encoding ycbcr_enc;
|
||||
enum v4l2_quantization quantization;
|
||||
enum v4l2_xfer_func xfer_func;
|
||||
};
|
||||
|
||||
struct mxc_jpeg_slot_data {
|
||||
bool used;
|
||||
struct mxc_jpeg_desc *desc; // enc/dec descriptor
|
||||
struct mxc_jpeg_desc *cfg_desc; // configuration descriptor
|
||||
void *cfg_stream_vaddr; // configuration bitstream virtual address
|
||||
unsigned int cfg_stream_size;
|
||||
int flags;
|
||||
dma_addr_t desc_handle;
|
||||
dma_addr_t cfg_desc_handle; // configuration descriptor dma address
|
||||
dma_addr_t cfg_stream_handle; // configuration bitstream dma address
|
||||
};
|
||||
|
||||
struct mxc_jpeg_dev {
|
||||
spinlock_t hw_lock;
|
||||
unsigned int mode;
|
||||
struct mutex lock;
|
||||
bool enc;
|
||||
bool dec;
|
||||
struct clk *clk_ipg;
|
||||
struct clk *clk_per;
|
||||
struct platform_device *pdev;
|
||||
struct device *dev;
|
||||
void __iomem *base_reg;
|
||||
void __iomem *enc_reg;
|
||||
struct v4l2_device v4l2_dev;
|
||||
struct v4l2_m2m_dev *m2m_dev;
|
||||
struct video_device *dec_vdev;
|
||||
unsigned int irq;
|
||||
int id;
|
||||
struct mxc_jpeg_slot_data slot_data[MXC_MAX_SLOTS];
|
||||
struct device *pd_dev[MXC_JPEG_NUM_PD];
|
||||
struct device_link *pd_link[MXC_JPEG_NUM_PD];
|
||||
};
|
||||
|
||||
#define MXC_JPEG_MAX_COMPONENTS 4
|
||||
/* JPEG Start Of Frame marker fields*/
|
||||
struct mxc_jpeg_sof_comp {
|
||||
u8 id; /*component id*/
|
||||
u8 v :4; /* vertical sampling*/
|
||||
u8 h :4; /* horizontal sampling*/
|
||||
u8 quantization_table_no;
|
||||
} __packed;
|
||||
|
||||
struct mxc_jpeg_sof {
|
||||
u16 length;
|
||||
u8 precision;
|
||||
u16 height, width;
|
||||
u8 components_no;
|
||||
struct mxc_jpeg_sof_comp comp[MXC_JPEG_MAX_COMPONENTS];
|
||||
} __packed;
|
||||
|
||||
/* JPEG Start Of Scan marker fields*/
|
||||
struct mxc_jpeg_sos_comp {
|
||||
u8 id; /*component id*/
|
||||
u8 huffman_table_no;
|
||||
} __packed;
|
||||
struct mxc_jpeg_sos {
|
||||
u16 length;
|
||||
u8 components_no;
|
||||
struct mxc_jpeg_sos_comp comp[MXC_JPEG_MAX_COMPONENTS];
|
||||
u8 ignorable_bytes[3];
|
||||
} __packed;
|
||||
|
||||
#endif
|
272
drivers/media/platform/imx8/mxc-mipi-csi2.h
Normal file
272
drivers/media/platform/imx8/mxc-mipi-csi2.h
Normal file
|
@ -0,0 +1,272 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright 2017 NXP
|
||||
*/
|
||||
|
||||
#ifndef MXC_MIPI_CSI2_H_
|
||||
#define MXC_MIPI_CSI2_H_
|
||||
|
||||
#include <media/v4l2-device.h>
|
||||
|
||||
#define MXC_MIPI_CSI2_DRIVER_NAME "mxc-mipi-csi2"
|
||||
#define MXC_MIPI_CSI2_SUBDEV_NAME MXC_MIPI_CSI2_DRIVER_NAME
|
||||
#define MXC_MIPI_CSI2_MAX_DEVS 2
|
||||
#define MXC_MIPI_CSI2_MAX_LANES 4
|
||||
|
||||
#define MIPI_CSI2_OF_NODE_NAME "csi"
|
||||
|
||||
#define MXC_MIPI_CSI2_VC0_PAD_SINK 0
|
||||
#define MXC_MIPI_CSI2_VC1_PAD_SINK 1
|
||||
#define MXC_MIPI_CSI2_VC2_PAD_SINK 2
|
||||
#define MXC_MIPI_CSI2_VC3_PAD_SINK 3
|
||||
|
||||
#define MXC_MIPI_CSI2_VC0_PAD_SOURCE 4
|
||||
#define MXC_MIPI_CSI2_VC1_PAD_SOURCE 5
|
||||
#define MXC_MIPI_CSI2_VC2_PAD_SOURCE 6
|
||||
#define MXC_MIPI_CSI2_VC3_PAD_SOURCE 7
|
||||
#define MXC_MIPI_CSI2_VCX_PADS_NUM 8
|
||||
|
||||
/* Subsystem CSR */
|
||||
#define CSI2SS_BASE_OFFSET 0x0
|
||||
|
||||
#define CSI2SS_PLM_CTRL (CSI2SS_BASE_OFFSET + 0x0)
|
||||
#define CSI2SS_PLM_CTRL_PL_CLK_RUN 0x80000000
|
||||
#define CSI2SS_PLM_CTRL_VSYNC_OVERRIDE 0x200
|
||||
#define CSI2SS_PLM_CTRL_HSYNC_OVERRIDE 0x400
|
||||
#define CSI2SS_PLM_CTRL_VALID_OVERRIDE 0x800
|
||||
#define CSI2SS_PLM_CTRL_POLARITY_MASK 0x1000
|
||||
#define CSI2SS_PLM_CTRL_POLARITY_HIGH 0x1000
|
||||
#define CSI2SS_PLM_CTRL_POLARITY_LOW 0x0
|
||||
#define CSI2SS_PLM_CTRL_ENABLE_PL 1
|
||||
#define CSI2SS_PLM_CTRL_ENABLE_PL_OFFSET 0
|
||||
#define CSI2SS_PLM_CTRL_ENABLE_PL_MASK 1
|
||||
|
||||
#define CSI2SS_PHY_CTRL (CSI2SS_BASE_OFFSET + 0x4)
|
||||
#define CSI2SS_PHY_CTRL_PD 1
|
||||
#define CSI2SS_PHY_CTRL_PD_OFFSET 22
|
||||
#define CSI2SS_PHY_CTRL_PD_MASK 0x400000
|
||||
#define CSI2SS_PHY_CTRL_RTERM_SEL 1
|
||||
#define CSI2SS_PHY_CTRL_RTERM_SEL_OFFSET 21
|
||||
#define CSI2SS_PHY_CTRL_RTERM_SEL_MASK 0x200000
|
||||
#define CSI2SS_PHY_CTRL_RX_HS_SETTLE_OFFSET 4
|
||||
#define CSI2SS_PHY_CTRL_RX_HS_SETTLE_MASK 0x3F0
|
||||
#define CSI2SS_PHY_CTRL_CONT_CLK_MODE 1
|
||||
#define CSI2SS_PHY_CTRL_CONT_CLK_MODE_OFFSET 3
|
||||
#define CSI2SS_PHY_CTRL_CONT_CLK_MODE_MASK 0x8
|
||||
#define CSI2SS_PHY_CTRL_DDRCLK_EN 1
|
||||
#define CSI2SS_PHY_CTRL_DDRCLK_EN_OFFSET 2
|
||||
#define CSI2SS_PHY_CTRL_DDRCLK_EN_MASK 0x4
|
||||
#define CSI2SS_PHY_CTRL_AUTO_PD_EN 1
|
||||
#define CSI2SS_PHY_CTRL_AUTO_PD_EN_OFFSET 1
|
||||
#define CSI2SS_PHY_CTRL_AUTO_PD_EN_MASK 0x2
|
||||
#define CSI2SS_PHY_CTRL_RX_ENABLE 1
|
||||
#define CSI2SS_PHY_CTRL_RX_ENABLE_OFFSET 0
|
||||
#define CSI2SS_PHY_CTRL_RX_ENABLE_MASK 0x1
|
||||
|
||||
#define CSI2SS_PHY_STATUS (CSI2SS_BASE_OFFSET + 0x8)
|
||||
#define CSI2SS_PHY_TEST_STATUS (CSI2SS_BASE_OFFSET + 0x10)
|
||||
#define CSI2SS_PHY_TEST_STATUS_D0 (CSI2SS_BASE_OFFSET + 0x14)
|
||||
#define CSI2SS_PHY_TEST_STATUS_D1 (CSI2SS_BASE_OFFSET + 0x18)
|
||||
#define CSI2SS_PHY_TEST_STATUS_D2 (CSI2SS_BASE_OFFSET + 0x1C)
|
||||
#define CSI2SS_PHY_TEST_STATUS_D3 (CSI2SS_BASE_OFFSET + 0x20)
|
||||
|
||||
#define CSI2SS_VC_INTERLACED (CSI2SS_BASE_OFFSET + 0x30)
|
||||
#define CSI2SS_VC_INTERLACED_VC0 1
|
||||
#define CSI2SS_VC_INTERLACED_VC1 2
|
||||
#define CSI2SS_VC_INTERLACED_VC2 4
|
||||
#define CSI2SS_VC_INTERLACED_VC3 8
|
||||
#define CSI2SS_VC_INTERLACED_OFFSET 0
|
||||
#define CSI2SS_VC_INTERLACED_MASK 0xF
|
||||
|
||||
#define CSI2SS_DATA_TYPE (CSI2SS_BASE_OFFSET + 0x38)
|
||||
#define CSI2SS_DATA_TYPE_LEGACY_YUV420_8BIT (1 << 2)
|
||||
#define CSI2SS_DATA_TYPE_YUV422_8BIT (1 << 6)
|
||||
#define CSI2SS_DATA_TYPE_YUV422_10BIT (1 << 7)
|
||||
#define CSI2SS_DATA_TYPE_RGB444 (1 << 8)
|
||||
#define CSI2SS_DATA_TYPE_RGB555 (1 << 9)
|
||||
#define CSI2SS_DATA_TYPE_RGB565 (1 << 10)
|
||||
#define CSI2SS_DATA_TYPE_RGB666 (1 << 11)
|
||||
#define CSI2SS_DATA_TYPE_RGB888 (1 << 12)
|
||||
#define CSI2SS_DATA_TYPE_RAW6 (1 << 16)
|
||||
#define CSI2SS_DATA_TYPE_RAW8 (1 << 18)
|
||||
#define CSI2SS_DATA_TYPE_RAW10 (1 << 19)
|
||||
#define CSI2SS_DATA_TYPE_RAW12 (1 << 20)
|
||||
#define CSI2SS_DATA_TYPE_RAW14 (1 << 21)
|
||||
|
||||
#define CSI2SS_YUV420_1ST_LINE_DATA_TYPE (CSI2SS_BASE_OFFSET + 0x40)
|
||||
#define CSI2SS_YUV420_1ST_LINE_DATA_TYPE_ODD 0
|
||||
#define CSI2SS_YUV420_1ST_LINE_DATA_TYPE_EVEN 1
|
||||
#define CSI2SS_YUV420_1ST_LINE_DATA_TYPE_OFFSET 0
|
||||
#define CSI2SS_YUV420_1ST_LINE_DATA_TYPE_MASK 1
|
||||
|
||||
#define CSI2SS_CTRL_CLK_RESET (CSI2SS_BASE_OFFSET + 0x44)
|
||||
#define CSI2SS_CTRL_CLK_RESET_EN 1
|
||||
#define CSI2SS_CTRL_CLK_RESET_OFFSET 0
|
||||
#define CSI2SS_CTRL_CLK_RESET_MASK 1
|
||||
#define CSI2SS_CTRL_CLK_RESET_CLK_OFF 1
|
||||
#define CSI2SS_CTRL_CLK_RESET_CLK_OFFSET 1
|
||||
#define CSI2SS_CTRL_CLK_RESET_CLK_MASK 0x1
|
||||
|
||||
#define CSI2SS_STREAM_FENCE_CTRL (CSI2SS_BASE_OFFSET + 0x48)
|
||||
#define CSI2SS_STREAM_FENCE_VC0 1
|
||||
#define CSI2SS_STREAM_FENCE_VC1 2
|
||||
#define CSI2SS_STREAM_FENCE_VC2 4
|
||||
#define CSI2SS_STREAM_FENCE_VC3 8
|
||||
#define CSI2SS_STREAM_FENCE_CTRL_OFFSET 0
|
||||
#define CSI2SS_STREAM_FENCE_CTRL_MASK 0xF
|
||||
|
||||
#define CSI2SS_STREAM_FENCE_STATUS (CSI2SS_BASE_OFFSET + 0x4C)
|
||||
|
||||
/* CSI-2 controller CSR */
|
||||
#define CSI2RX_BASE_OFFSET (0x100)
|
||||
|
||||
#define CSI2RX_CFG_NUM_LANES (CSI2RX_BASE_OFFSET + 0x0)
|
||||
#define CSI2RX_CFG_NUM_LANES_OFFSET 0
|
||||
#define CSI2RX_CFG_NUM_LANES_MASK 0x3
|
||||
|
||||
#define CSI2RX_CFG_DISABLE_DATA_LANES (CSI2RX_BASE_OFFSET + 0x4)
|
||||
#define CSI2RX_CFG_DISABLE_DATA_LANES_3 8
|
||||
#define CSI2RX_CFG_DISABLE_DATA_LANES_2 4
|
||||
#define CSI2RX_CFG_DISABLE_DATA_LANES_1 2
|
||||
#define CSI2RX_CFG_DISABLE_DATA_LANES_0 1
|
||||
#define CSI2RX_CFG_DISABLE_DATA_LANES_OFFSET 0
|
||||
#define CSI2RX_CFG_DISABLE_DATA_LANES_MASK 0xF
|
||||
|
||||
#define CSI2RX_BIT_ERR (CSI2RX_BASE_OFFSET + 0x8)
|
||||
|
||||
#define CSI2RX_IRQ_STATUS (CSI2RX_BASE_OFFSET + 0xC)
|
||||
#define CSI2RX_IRQ_STATUS_CRC_ERROR 0x1
|
||||
#define CSI2RX_IRQ_STATUS_1BIT_CRC_ERROR 0x2
|
||||
#define CSI2RX_IRQ_STATUS_2BIT_CRC_ERROR 0x4
|
||||
#define CSI2RX_IRQ_STATUS_ULPS_CHANGE 0x8
|
||||
#define CSI2RX_IRQ_STATUS_DPHY_ERRSOTHS 0x10
|
||||
#define CSI2RX_IRQ_STATUS_DPHY_ERRSOTSYNC_HS 0x20
|
||||
#define CSI2RX_IRQ_STATUS_DPHY_ERRESC 0x40
|
||||
#define CSI2RX_IRQ_STATUS_DPHY_ERRSYNCESC 0x80
|
||||
#define CSI2RX_IRQ_STATUS_DPHY_ERRCTRL 0x100
|
||||
|
||||
#define CSI2RX_IRQ_MASK (CSI2RX_BASE_OFFSET + 0x10)
|
||||
#define CSI2RX_IRQ_MASK_CRC_ERROR 0x1
|
||||
#define CSI2RX_IRQ_MASK_1BIT_CRC_ERROR 0x2
|
||||
#define CSI2RX_IRQ_MASK_2BIT_CRC_ERROR 0x4
|
||||
#define CSI2RX_IRQ_MASK_ULPS_CHANGE 0x8
|
||||
#define CSI2RX_IRQ_MASK_DPHY_ERRSOTHS 0x10
|
||||
#define CSI2RX_IRQ_MASK_DPHY_ERRSOTSYNC_HS 0x20
|
||||
#define CSI2RX_IRQ_MASK_DPHY_ERRESC 0x40
|
||||
#define CSI2RX_IRQ_MASK_DPHY_ERRSYNCESC 0x80
|
||||
#define CSI2RX_IRQ_MASK_DPHY_ERRCTRL 0x100
|
||||
|
||||
#define CSI2RX_ULPS_STATUS (CSI2RX_BASE_OFFSET + 0x14)
|
||||
#define CSI2RX_ULPS_STATUS_CLK_LANE_ULPS 0x1
|
||||
#define CSI2RX_ULPS_STATUS_DAT_LANE0_ULPS 0x2
|
||||
#define CSI2RX_ULPS_STATUS_DAT_LANE1_ULPS 0x4
|
||||
#define CSI2RX_ULPS_STATUS_DAT_LANE2_ULPS 0x8
|
||||
#define CSI2RX_ULPS_STATUS_DAT_LANE3_ULPS 0x10
|
||||
#define CSI2RX_ULPS_STATUS_CLK_LANE_MARK 0x20
|
||||
#define CSI2RX_ULPS_STATUS_DAT_LANE0_MARK 0x40
|
||||
#define CSI2RX_ULPS_STATUS_DAT_LANE1_MARK 0x80
|
||||
#define CSI2RX_ULPS_STATUS_DAT_LANE2_MARK 0x100
|
||||
#define CSI2RX_ULPS_STATUS_DAT_LANE3_MARK 0x200
|
||||
|
||||
#define CSI2RX_PPI_ERRSOT_HS (CSI2RX_BASE_OFFSET + 0x18)
|
||||
#define CSI2RX_PPI_ERRSOT_HS_DAT_LANE0 0x1
|
||||
#define CSI2RX_PPI_ERRSOT_HS_DAT_LANE1 0x2
|
||||
#define CSI2RX_PPI_ERRSOT_HS_DAT_LANE2 0x4
|
||||
#define CSI2RX_PPI_ERRSOT_HS_DAT_LANE3 0x8
|
||||
|
||||
#define CSI2RX_PPI_ERRSOTSYNC_HS (CSI2RX_BASE_OFFSET + 0x1C)
|
||||
#define CSI2RX_PPI_ERRSOTSYNC_HS_DAT_LANE0 0x1
|
||||
#define CSI2RX_PPI_ERRSOTSYNC_HS_DAT_LANE1 0x2
|
||||
#define CSI2RX_PPI_ERRSOTSYNC_HS_DAT_LANE2 0x4
|
||||
#define CSI2RX_PPI_ERRSOTSYNC_HS_DAT_LANE3 0x8
|
||||
|
||||
#define CSI2RX_PPI_ERRESC (CSI2RX_BASE_OFFSET + 0x20)
|
||||
#define CSI2RX_PPI_ERRESC_DAT_LANE0 0x1
|
||||
#define CSI2RX_PPI_ERRESC_DAT_LANE1 0x2
|
||||
#define CSI2RX_PPI_ERRESC_DAT_LANE2 0x4
|
||||
#define CSI2RX_PPI_ERRESC_DAT_LANE3 0x8
|
||||
|
||||
#define CSI2RX_PPI_ERRSYNCESC (CSI2RX_BASE_OFFSET + 0x24)
|
||||
#define CSI2RX_PPI_ERRSYNCESC_DAT_LANE0 0x1
|
||||
#define CSI2RX_PPI_ERRSYNCESC_DAT_LANE1 0x2
|
||||
#define CSI2RX_PPI_ERRSYNCESC_DAT_LANE2 0x4
|
||||
#define CSI2RX_PPI_ERRSYNCESC_DAT_LANE3 0x8
|
||||
|
||||
#define CSI2RX_PPI_ERRCONTROL (CSI2RX_BASE_OFFSET + 0x28)
|
||||
#define CSI2RX_PPI_ERRCONTROL_DAT_LANE0 0x1
|
||||
#define CSI2RX_PPI_ERRCONTROL_DAT_LANE1 0x2
|
||||
#define CSI2RX_PPI_ERRCONTROL_DAT_LANE2 0x4
|
||||
#define CSI2RX_PPI_ERRCONTROL_DAT_LANE3 0x8
|
||||
|
||||
#define CSI2RX_CFG_DISABLE_PAYLOAD_0 (CSI2RX_BASE_OFFSET + 0x2C)
|
||||
#define CSI2RX_CFG_DISABLE_PAYLOAD_TYPE_LEGACY_YUV420_8BIT (1 << 10)
|
||||
#define CSI2RX_CFG_DISABLE_PAYLOAD_TYPE_YUV422_8BIT (1 << 14)
|
||||
#define CSI2RX_CFG_DISABLE_PAYLOAD_TYPE_YUV422_10BIT (1 << 15)
|
||||
#define CSI2RX_CFG_DISABLE_PAYLOAD_TYPE_RGB444 (1 << 16)
|
||||
#define CSI2RX_CFG_DISABLE_PAYLOAD_TYPE_RGB555 (1 << 17)
|
||||
#define CSI2RX_CFG_DISABLE_PAYLOAD_TYPE_RGB565 (1 << 18)
|
||||
#define CSI2RX_CFG_DISABLE_PAYLOAD_TYPE_RGB666 (1 << 19)
|
||||
#define CSI2RX_CFG_DISABLE_PAYLOAD_TYPE_RGB888 (1 << 20)
|
||||
#define CSI2RX_CFG_DISABLE_PAYLOAD_TYPE_RAW6 (1 << 24)
|
||||
#define CSI2RX_CFG_DISABLE_PAYLOAD_TYPE_RAW7 (1 << 25)
|
||||
#define CSI2RX_CFG_DISABLE_PAYLOAD_TYPE_RAW8 (1 << 26)
|
||||
#define CSI2RX_CFG_DISABLE_PAYLOAD_TYPE_RAW10 (1 << 27)
|
||||
#define CSI2RX_CFG_DISABLE_PAYLOAD_TYPE_RAW12 (1 << 28)
|
||||
#define CSI2RX_CFG_DISABLE_PAYLOAD_TYPE_RAW14 (1 << 29)
|
||||
|
||||
#define CSI2RX_CFG_DISABLE_PAYLOAD_1 (CSI2RX_BASE_OFFSET + 0x30)
|
||||
|
||||
struct csis_hw_reset {
|
||||
struct regmap *src;
|
||||
u8 req_src;
|
||||
u8 rst_val;
|
||||
};
|
||||
struct csis_phy_gpr {
|
||||
struct regmap *gpr;
|
||||
u8 req_src;
|
||||
};
|
||||
|
||||
struct mxc_mipi_csi2_dev {
|
||||
struct v4l2_device v4l2_dev;
|
||||
struct v4l2_subdev sd;
|
||||
struct v4l2_subdev *sensor_sd;
|
||||
|
||||
struct media_pad pads[MXC_MIPI_CSI2_VCX_PADS_NUM];
|
||||
struct v4l2_mbus_framefmt format;
|
||||
|
||||
void __iomem *csr_regs;
|
||||
void __iomem *base_regs;
|
||||
struct platform_device *pdev;
|
||||
u32 flags;
|
||||
int irq;
|
||||
|
||||
struct clk *clk_apb;
|
||||
struct clk *clk_core;
|
||||
struct clk *clk_esc;
|
||||
struct clk *clk_pxl;
|
||||
|
||||
struct csis_hw_reset hw_reset;
|
||||
struct csis_phy_gpr phy_gpr;
|
||||
|
||||
struct v4l2_async_subdev asd;
|
||||
struct v4l2_async_notifier subdev_notifier;
|
||||
struct v4l2_async_subdev *async_subdevs[2];
|
||||
|
||||
struct mutex lock;
|
||||
|
||||
int id;
|
||||
u32 hs_settle;
|
||||
u32 send_level;
|
||||
u32 num_lanes;
|
||||
u8 data_lanes[4];
|
||||
u8 vchannel;
|
||||
u8 running;
|
||||
};
|
||||
|
||||
enum mxc_mipi_csi2_pm_state {
|
||||
MXC_MIPI_CSI2_PM_POWERED = 0x1,
|
||||
MXC_MIPI_CSI2_PM_SUSPENDED = 0x2,
|
||||
MXC_MIPI_CSI2_RUNTIME_SUSPENDED = 0x4,
|
||||
};
|
||||
|
||||
#endif
|
737
drivers/media/platform/imx8/mxc-mipi-csi2_yav.c
Normal file
737
drivers/media/platform/imx8/mxc-mipi-csi2_yav.c
Normal file
|
@ -0,0 +1,737 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright 2017 NXP
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/memory.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/videodev2.h>
|
||||
#include <media/v4l2-fwnode.h>
|
||||
#include <media/v4l2-subdev.h>
|
||||
#include <media/v4l2-device.h>
|
||||
|
||||
#include "mxc-mipi-csi2.h"
|
||||
static int debug;
|
||||
module_param(debug, int, 0644);
|
||||
MODULE_PARM_DESC(debug, "Debug level (0-2)");
|
||||
|
||||
#define MXC_MIPI_CSI2_YAV_DRIVER_NAME "mxc-mipi-csi2_yav"
|
||||
#define MXC_MIPI_CSI2_YAV_SUBDEV_NAME MXC_MIPI_CSI2_DRIVER_NAME
|
||||
|
||||
#define GPR_CSI2_1_RX_ENABLE BIT(13)
|
||||
#define GPR_CSI2_1_VID_INTFC_ENB BIT(12)
|
||||
#define GPR_CSI2_1_PD_RX BIT(11)
|
||||
#define GPR_CSI2_1_HSEL BIT(10)
|
||||
#define GPR_CSI2_1_AUTO_PD_EN BIT(9)
|
||||
#define GPR_CSI2_1_CONT_CLK_MODE BIT(8)
|
||||
#define GPR_CSI2_1_S_PRG_RXHS_SETTLE(x) (((x) & 0x3F) << 2)
|
||||
#define GPR_CSI2_1_RX_RCAL (3)
|
||||
|
||||
static u8 rxhs_settle[2] = { 0x14, 0x9 };
|
||||
|
||||
static struct mxc_mipi_csi2_dev *sd_to_mxc_mipi_csi2_dev(struct v4l2_subdev
|
||||
*sdev)
|
||||
{
|
||||
return container_of(sdev, struct mxc_mipi_csi2_dev, sd);
|
||||
}
|
||||
|
||||
#ifdef debug
|
||||
static void mxc_mipi_csi2_reg_dump(struct mxc_mipi_csi2_dev *csi2dev)
|
||||
{
|
||||
printk("MIPI CSI2 HC register dump, mipi csi%d\n", csi2dev->id);
|
||||
printk("MIPI CSI2 HC num of lanes 0x100 = 0x%x\n",
|
||||
readl(csi2dev->base_regs + 0x100));
|
||||
printk("MIPI CSI2 HC dis lanes 0x104 = 0x%x\n",
|
||||
readl(csi2dev->base_regs + 0x104));
|
||||
printk("MIPI CSI2 HC BIT ERR 0x108 = 0x%x\n",
|
||||
readl(csi2dev->base_regs + 0x108));
|
||||
printk("MIPI CSI2 HC IRQ STATUS 0x10C = 0x%x\n",
|
||||
readl(csi2dev->base_regs + 0x10C));
|
||||
printk("MIPI CSI2 HC IRQ MASK 0x110 = 0x%x\n",
|
||||
readl(csi2dev->base_regs + 0x110));
|
||||
printk("MIPI CSI2 HC ULPS STATUS 0x114 = 0x%x\n",
|
||||
readl(csi2dev->base_regs + 0x114));
|
||||
printk("MIPI CSI2 HC DPHY ErrSotHS 0x118 = 0x%x\n",
|
||||
readl(csi2dev->base_regs + 0x118));
|
||||
printk("MIPI CSI2 HC DPHY ErrSotSync 0x11c = 0x%x\n",
|
||||
readl(csi2dev->base_regs + 0x11c));
|
||||
printk("MIPI CSI2 HC DPHY ErrEsc 0x120 = 0x%x\n",
|
||||
readl(csi2dev->base_regs + 0x120));
|
||||
printk("MIPI CSI2 HC DPHY ErrSyncEsc 0x124 = 0x%x\n",
|
||||
readl(csi2dev->base_regs + 0x124));
|
||||
printk("MIPI CSI2 HC DPHY ErrControl 0x128 = 0x%x\n",
|
||||
readl(csi2dev->base_regs + 0x128));
|
||||
printk("MIPI CSI2 HC DISABLE_PAYLOAD 0x12C = 0x%x\n",
|
||||
readl(csi2dev->base_regs + 0x12C));
|
||||
printk("MIPI CSI2 HC DISABLE_PAYLOAD 0x130 = 0x%x\n",
|
||||
readl(csi2dev->base_regs + 0x130));
|
||||
printk("MIPI CSI2 HC IGNORE_VC 0x180 = 0x%x\n",
|
||||
readl(csi2dev->base_regs + 0x180));
|
||||
printk("MIPI CSI2 HC VID_VC 0x184 = 0x%x\n",
|
||||
readl(csi2dev->base_regs + 0x184));
|
||||
printk("MIPI CSI2 HC FIFO_SEND_LEVEL 0x188 = 0x%x\n",
|
||||
readl(csi2dev->base_regs + 0x188));
|
||||
printk("MIPI CSI2 HC VID_VSYNC 0x18C = 0x%x\n",
|
||||
readl(csi2dev->base_regs + 0x18C));
|
||||
printk("MIPI CSI2 HC VID_SYNC_FP 0x190 = 0x%x\n",
|
||||
readl(csi2dev->base_regs + 0x190));
|
||||
printk("MIPI CSI2 HC VID_HSYNC 0x194 = 0x%x\n",
|
||||
readl(csi2dev->base_regs + 0x194));
|
||||
printk("MIPI CSI2 HC VID_HSYNC_BP 0x198 = 0x%x\n",
|
||||
readl(csi2dev->base_regs + 0x198));
|
||||
}
|
||||
#else
|
||||
static void mxc_mipi_csi2_reg_dump(struct mxc_mipi_csi2_dev *csi2dev)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static int mxc_mipi_csi2_phy_reset(struct mxc_mipi_csi2_dev *csi2dev)
|
||||
{
|
||||
struct device *dev = &csi2dev->pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct device_node *node;
|
||||
phandle phandle;
|
||||
u32 out_val[3];
|
||||
int ret;
|
||||
|
||||
ret = of_property_read_u32_array(np, "csis-phy-reset", out_val, 3);
|
||||
if (ret) {
|
||||
dev_info(dev, "no csis-hw-reset property found\n");
|
||||
} else {
|
||||
phandle = *out_val;
|
||||
|
||||
node = of_find_node_by_phandle(phandle);
|
||||
if (!node) {
|
||||
dev_dbg(dev, "not find src node by phandle\n");
|
||||
ret = PTR_ERR(node);
|
||||
}
|
||||
csi2dev->hw_reset.src = syscon_node_to_regmap(node);
|
||||
if (IS_ERR(csi2dev->hw_reset.src)) {
|
||||
dev_err(dev, "failed to get src regmap\n");
|
||||
ret = PTR_ERR(csi2dev->hw_reset.src);
|
||||
}
|
||||
of_node_put(node);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
csi2dev->hw_reset.req_src = out_val[1];
|
||||
csi2dev->hw_reset.rst_val = out_val[2];
|
||||
|
||||
/* reset mipi phy */
|
||||
regmap_update_bits(csi2dev->hw_reset.src,
|
||||
csi2dev->hw_reset.req_src,
|
||||
csi2dev->hw_reset.rst_val,
|
||||
csi2dev->hw_reset.rst_val);
|
||||
msleep(20);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mxc_mipi_csi2_phy_gpr(struct mxc_mipi_csi2_dev *csi2dev)
|
||||
{
|
||||
struct device *dev = &csi2dev->pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct device_node *node;
|
||||
phandle phandle;
|
||||
u32 out_val[2];
|
||||
int ret;
|
||||
|
||||
ret = of_property_read_u32_array(np, "phy-gpr", out_val, 2);
|
||||
if (ret) {
|
||||
dev_dbg(dev, "no phy-gpr property found\n");
|
||||
} else {
|
||||
phandle = *out_val;
|
||||
|
||||
node = of_find_node_by_phandle(phandle);
|
||||
if (!node) {
|
||||
dev_dbg(dev, "not find gpr node by phandle\n");
|
||||
ret = PTR_ERR(node);
|
||||
}
|
||||
csi2dev->phy_gpr.gpr = syscon_node_to_regmap(node);
|
||||
if (IS_ERR(csi2dev->phy_gpr.gpr)) {
|
||||
dev_err(dev, "failed to get gpr regmap\n");
|
||||
ret = PTR_ERR(csi2dev->phy_gpr.gpr);
|
||||
}
|
||||
of_node_put(node);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
csi2dev->phy_gpr.req_src = out_val[1];
|
||||
|
||||
regmap_update_bits(csi2dev->phy_gpr.gpr,
|
||||
csi2dev->phy_gpr.req_src,
|
||||
0x3FFF,
|
||||
GPR_CSI2_1_RX_ENABLE |
|
||||
GPR_CSI2_1_VID_INTFC_ENB |
|
||||
GPR_CSI2_1_HSEL |
|
||||
GPR_CSI2_1_CONT_CLK_MODE |
|
||||
GPR_CSI2_1_S_PRG_RXHS_SETTLE(csi2dev->
|
||||
hs_settle));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mxc_mipi_csi2_enable(struct mxc_mipi_csi2_dev *csi2dev)
|
||||
{
|
||||
mxc_mipi_csi2_phy_gpr(csi2dev);
|
||||
}
|
||||
|
||||
static void mxc_mipi_csi2_disable(struct mxc_mipi_csi2_dev *csi2dev)
|
||||
{
|
||||
/* Disable Data lanes */
|
||||
writel(0xf, csi2dev->base_regs + CSI2RX_CFG_DISABLE_DATA_LANES);
|
||||
}
|
||||
|
||||
static void mxc_mipi_csi2_hc_config(struct mxc_mipi_csi2_dev *csi2dev)
|
||||
{
|
||||
u32 val0, val1;
|
||||
u32 i;
|
||||
|
||||
val0 = 0;
|
||||
|
||||
/* Lanes */
|
||||
writel(csi2dev->num_lanes - 1,
|
||||
csi2dev->base_regs + CSI2RX_CFG_NUM_LANES);
|
||||
|
||||
for (i = 0; i < csi2dev->num_lanes; i++)
|
||||
val0 |= (1 << (csi2dev->data_lanes[i] - 1));
|
||||
|
||||
val1 = 0xF & ~val0;
|
||||
writel(val1, csi2dev->base_regs + CSI2RX_CFG_DISABLE_DATA_LANES);
|
||||
|
||||
/* Mask interrupt */
|
||||
writel(0x1FF, csi2dev->base_regs + CSI2RX_IRQ_MASK);
|
||||
|
||||
writel(1, csi2dev->base_regs + 0x180);
|
||||
/* vid_vc */
|
||||
writel(1, csi2dev->base_regs + 0x184);
|
||||
writel(csi2dev->send_level, csi2dev->base_regs + 0x188);
|
||||
}
|
||||
|
||||
static int mipi_csi2_clk_init(struct mxc_mipi_csi2_dev *csi2dev)
|
||||
{
|
||||
struct device *dev = &csi2dev->pdev->dev;
|
||||
|
||||
csi2dev->clk_core = devm_clk_get(dev, "clk_core");
|
||||
if (IS_ERR(csi2dev->clk_core)) {
|
||||
dev_err(dev, "failed to get csi core clk\n");
|
||||
return PTR_ERR(csi2dev->clk_core);
|
||||
}
|
||||
|
||||
csi2dev->clk_esc = devm_clk_get(dev, "clk_esc");
|
||||
if (IS_ERR(csi2dev->clk_esc)) {
|
||||
dev_err(dev, "failed to get csi esc clk\n");
|
||||
return PTR_ERR(csi2dev->clk_esc);
|
||||
}
|
||||
|
||||
csi2dev->clk_pxl = devm_clk_get(dev, "clk_pxl");
|
||||
if (IS_ERR(csi2dev->clk_pxl)) {
|
||||
dev_err(dev, "failed to get csi pixel link clk\n");
|
||||
return PTR_ERR(csi2dev->clk_pxl);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mipi_csi2_clk_enable(struct mxc_mipi_csi2_dev *csi2dev)
|
||||
{
|
||||
struct device *dev = &csi2dev->pdev->dev;
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(csi2dev->clk_core);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s, pre clk_core error\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
ret = clk_prepare_enable(csi2dev->clk_esc);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s, prepare clk_esc error\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
ret = clk_prepare_enable(csi2dev->clk_pxl);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s, prepare clk_pxl error\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mipi_csi2_clk_disable(struct mxc_mipi_csi2_dev *csi2dev)
|
||||
{
|
||||
clk_disable_unprepare(csi2dev->clk_core);
|
||||
clk_disable_unprepare(csi2dev->clk_esc);
|
||||
clk_disable_unprepare(csi2dev->clk_pxl);
|
||||
}
|
||||
|
||||
static int mipi_csi2_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* V4L2 subdev operations
|
||||
*/
|
||||
static int mipi_csi2_s_power(struct v4l2_subdev *sd, int on)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mipi_csi2_s_stream(struct v4l2_subdev *sd, int enable)
|
||||
{
|
||||
struct mxc_mipi_csi2_dev *csi2dev = sd_to_mxc_mipi_csi2_dev(sd);
|
||||
struct device *dev = &csi2dev->pdev->dev;
|
||||
struct v4l2_subdev *sensor_sd = csi2dev->sensor_sd;
|
||||
int ret = 0;
|
||||
|
||||
dev_dbg(&csi2dev->pdev->dev, "%s: %d, csi2dev: 0x%x\n",
|
||||
__func__, enable, csi2dev->flags);
|
||||
|
||||
if (enable) {
|
||||
if (!csi2dev->running) {
|
||||
pm_runtime_get_sync(dev);
|
||||
mxc_mipi_csi2_phy_reset(csi2dev);
|
||||
mxc_mipi_csi2_hc_config(csi2dev);
|
||||
mxc_mipi_csi2_enable(csi2dev);
|
||||
mxc_mipi_csi2_reg_dump(csi2dev);
|
||||
}
|
||||
v4l2_subdev_call(sensor_sd, video, s_stream, true);
|
||||
csi2dev->running++;
|
||||
|
||||
} else {
|
||||
|
||||
v4l2_subdev_call(sensor_sd, video, s_stream, false);
|
||||
csi2dev->running--;
|
||||
if (!csi2dev->running) {
|
||||
pm_runtime_put(dev);
|
||||
mxc_mipi_csi2_disable(csi2dev);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mipi_csis_enum_framesizes(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_pad_config *cfg,
|
||||
struct v4l2_subdev_frame_size_enum *fse)
|
||||
{
|
||||
struct mxc_mipi_csi2_dev *csi2dev = sd_to_mxc_mipi_csi2_dev(sd);
|
||||
struct v4l2_subdev *sensor_sd = csi2dev->sensor_sd;
|
||||
|
||||
return v4l2_subdev_call(sensor_sd, pad, enum_frame_size, NULL, fse);
|
||||
}
|
||||
|
||||
static int mipi_csis_enum_frameintervals(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_pad_config *cfg,
|
||||
struct v4l2_subdev_frame_interval_enum
|
||||
*fie)
|
||||
{
|
||||
struct mxc_mipi_csi2_dev *csi2dev = sd_to_mxc_mipi_csi2_dev(sd);
|
||||
struct v4l2_subdev *sensor_sd = csi2dev->sensor_sd;
|
||||
|
||||
return v4l2_subdev_call(sensor_sd, pad, enum_frame_interval, NULL, fie);
|
||||
}
|
||||
|
||||
static int mipi_csis_enum_mbus_code(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_pad_config *cfg,
|
||||
struct v4l2_subdev_mbus_code_enum *code)
|
||||
{
|
||||
struct mxc_mipi_csi2_dev *csi2dev = sd_to_mxc_mipi_csi2_dev(sd);
|
||||
struct v4l2_subdev *sensor_sd = csi2dev->sensor_sd;
|
||||
|
||||
return v4l2_subdev_call(sensor_sd, pad, enum_mbus_code, NULL, code);
|
||||
}
|
||||
|
||||
static int mipi_csi2_get_fmt(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_pad_config *cfg,
|
||||
struct v4l2_subdev_format *fmt)
|
||||
{
|
||||
struct mxc_mipi_csi2_dev *csi2dev = sd_to_mxc_mipi_csi2_dev(sd);
|
||||
struct v4l2_subdev *sensor_sd = csi2dev->sensor_sd;
|
||||
|
||||
if (fmt->pad)
|
||||
return -EINVAL;
|
||||
|
||||
return v4l2_subdev_call(sensor_sd, pad, get_fmt, NULL, fmt);
|
||||
}
|
||||
|
||||
static int mipi_csi2_set_fmt(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_pad_config *cfg,
|
||||
struct v4l2_subdev_format *fmt)
|
||||
{
|
||||
struct mxc_mipi_csi2_dev *csi2dev = sd_to_mxc_mipi_csi2_dev(sd);
|
||||
struct v4l2_subdev *sensor_sd = csi2dev->sensor_sd;
|
||||
|
||||
if (fmt->pad)
|
||||
return -EINVAL;
|
||||
|
||||
if (fmt->format.width * fmt->format.height > 720 * 480) {
|
||||
csi2dev->hs_settle = rxhs_settle[1];
|
||||
} else {
|
||||
csi2dev->hs_settle = rxhs_settle[0];
|
||||
}
|
||||
csi2dev->send_level = 64;
|
||||
|
||||
return v4l2_subdev_call(sensor_sd, pad, set_fmt, NULL, fmt);
|
||||
}
|
||||
|
||||
static int mipi_csis_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
|
||||
{
|
||||
struct mxc_mipi_csi2_dev *csi2dev = sd_to_mxc_mipi_csi2_dev(sd);
|
||||
struct v4l2_subdev *sensor_sd = csi2dev->sensor_sd;
|
||||
|
||||
return v4l2_subdev_call(sensor_sd, video, s_parm, a);
|
||||
}
|
||||
|
||||
static int mipi_csis_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
|
||||
{
|
||||
struct mxc_mipi_csi2_dev *csi2dev = sd_to_mxc_mipi_csi2_dev(sd);
|
||||
struct v4l2_subdev *sensor_sd = csi2dev->sensor_sd;
|
||||
|
||||
return v4l2_subdev_call(sensor_sd, video, g_parm, a);
|
||||
}
|
||||
|
||||
static const struct v4l2_subdev_internal_ops mipi_csi2_sd_internal_ops = {
|
||||
.open = mipi_csi2_open,
|
||||
};
|
||||
|
||||
static struct v4l2_subdev_pad_ops mipi_csi2_pad_ops = {
|
||||
.enum_frame_size = mipi_csis_enum_framesizes,
|
||||
.enum_frame_interval = mipi_csis_enum_frameintervals,
|
||||
.enum_mbus_code = mipi_csis_enum_mbus_code,
|
||||
.get_fmt = mipi_csi2_get_fmt,
|
||||
.set_fmt = mipi_csi2_set_fmt,
|
||||
};
|
||||
|
||||
static struct v4l2_subdev_core_ops mipi_csi2_core_ops = {
|
||||
.s_power = mipi_csi2_s_power,
|
||||
};
|
||||
|
||||
static struct v4l2_subdev_video_ops mipi_csi2_video_ops = {
|
||||
.s_stream = mipi_csi2_s_stream,
|
||||
|
||||
.s_parm = mipi_csis_s_parm,
|
||||
.g_parm = mipi_csis_g_parm,
|
||||
};
|
||||
|
||||
static struct v4l2_subdev_ops mipi_csi2_subdev_ops = {
|
||||
.core = &mipi_csi2_core_ops,
|
||||
.video = &mipi_csi2_video_ops,
|
||||
.pad = &mipi_csi2_pad_ops,
|
||||
};
|
||||
|
||||
static int mipi_csi2_parse_dt(struct mxc_mipi_csi2_dev *csi2dev)
|
||||
{
|
||||
struct device *dev = &csi2dev->pdev->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct v4l2_fwnode_endpoint endpoint;
|
||||
u32 i;
|
||||
|
||||
csi2dev->id = of_alias_get_id(node, "csi");
|
||||
|
||||
csi2dev->vchannel = of_property_read_bool(node, "virtual-channel");
|
||||
|
||||
node = of_graph_get_next_endpoint(node, NULL);
|
||||
if (!node) {
|
||||
dev_err(dev, "No port node at %s\n", node->full_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Get port node */
|
||||
memset(&endpoint, 0x0, sizeof(endpoint));
|
||||
v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &endpoint);
|
||||
|
||||
csi2dev->num_lanes = endpoint.bus.mipi_csi2.num_data_lanes;
|
||||
for (i = 0; i < csi2dev->num_lanes; i++)
|
||||
csi2dev->data_lanes[i] = endpoint.bus.mipi_csi2.data_lanes[i];
|
||||
|
||||
of_node_put(node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline struct mxc_mipi_csi2_dev
|
||||
*notifier_to_mipi_dev(struct v4l2_async_notifier *n)
|
||||
{
|
||||
return container_of(n, struct mxc_mipi_csi2_dev, subdev_notifier);
|
||||
}
|
||||
|
||||
static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
|
||||
struct v4l2_subdev *subdev,
|
||||
struct v4l2_async_subdev *asd)
|
||||
{
|
||||
struct mxc_mipi_csi2_dev *csi2dev = notifier_to_mipi_dev(notifier);
|
||||
|
||||
/* Find platform data for this sensor subdev */
|
||||
if (csi2dev->asd.match.fwnode == of_fwnode_handle(subdev->dev->of_node))
|
||||
csi2dev->sensor_sd = subdev;
|
||||
|
||||
if (subdev == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
v4l2_info(&csi2dev->v4l2_dev, "Registered sensor subdevice: %s\n",
|
||||
subdev->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct v4l2_async_notifier_operations subdev_notifier_ops = {
|
||||
.bound = subdev_notifier_bound,
|
||||
};
|
||||
|
||||
static int mipi_csis_subdev_host(struct mxc_mipi_csi2_dev *csi2dev)
|
||||
{
|
||||
struct device *dev = &csi2dev->pdev->dev;
|
||||
struct device_node *parent = dev->of_node;
|
||||
struct device_node *node, *port, *rem;
|
||||
int ret;
|
||||
|
||||
v4l2_async_notifier_init(&csi2dev->subdev_notifier);
|
||||
|
||||
/* Attach sensors linked to csi receivers */
|
||||
for_each_available_child_of_node(parent, node) {
|
||||
if (of_node_cmp(node->name, "port"))
|
||||
continue;
|
||||
|
||||
/* The csi node can have only port subnode. */
|
||||
port = of_get_next_child(node, NULL);
|
||||
if (!port)
|
||||
continue;
|
||||
rem = of_graph_get_remote_port_parent(port);
|
||||
of_node_put(port);
|
||||
if (rem == NULL) {
|
||||
v4l2_info(&csi2dev->v4l2_dev,
|
||||
"Remote device at %s not found\n",
|
||||
port->full_name);
|
||||
return -1;
|
||||
} else
|
||||
v4l2_info(&csi2dev->v4l2_dev,
|
||||
"Remote device at %s XXX found\n",
|
||||
port->full_name);
|
||||
|
||||
INIT_LIST_HEAD(&csi2dev->asd.list);
|
||||
csi2dev->asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
|
||||
csi2dev->asd.match.fwnode = of_fwnode_handle(rem);
|
||||
csi2dev->async_subdevs[0] = &csi2dev->asd;
|
||||
|
||||
of_node_put(rem);
|
||||
break;
|
||||
}
|
||||
|
||||
v4l2_async_notifier_add_subdev(&csi2dev->subdev_notifier,
|
||||
&csi2dev->asd);
|
||||
|
||||
csi2dev->subdev_notifier.v4l2_dev = &csi2dev->v4l2_dev;
|
||||
csi2dev->subdev_notifier.ops = &subdev_notifier_ops;
|
||||
|
||||
ret = v4l2_async_notifier_register(&csi2dev->v4l2_dev,
|
||||
&csi2dev->subdev_notifier);
|
||||
if (ret)
|
||||
dev_err(dev,
|
||||
"Error register async notifier regoster, ret %d\n",
|
||||
ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mipi_csi2_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *mem_res;
|
||||
struct mxc_mipi_csi2_dev *csi2dev;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
dev_info(&pdev->dev, "%s\n", __func__);
|
||||
csi2dev = devm_kzalloc(dev, sizeof(*csi2dev), GFP_KERNEL);
|
||||
if (!csi2dev)
|
||||
return -ENOMEM;
|
||||
|
||||
csi2dev->pdev = pdev;
|
||||
|
||||
ret = mipi_csi2_parse_dt(csi2dev);
|
||||
if (ret < 0)
|
||||
return -EINVAL;
|
||||
|
||||
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
csi2dev->base_regs = devm_ioremap_resource(dev, mem_res);
|
||||
if (IS_ERR(csi2dev->base_regs)) {
|
||||
dev_err(dev, "Failed to get mipi csi2 HC register\n");
|
||||
return PTR_ERR(csi2dev->base_regs);
|
||||
}
|
||||
|
||||
ret = mipi_csi2_clk_init(csi2dev);
|
||||
if (ret < 0)
|
||||
return -EINVAL;
|
||||
|
||||
v4l2_subdev_init(&csi2dev->sd, &mipi_csi2_subdev_ops);
|
||||
|
||||
csi2dev->sd.owner = THIS_MODULE;
|
||||
snprintf(csi2dev->sd.name, sizeof(csi2dev->sd.name), "%s.%d",
|
||||
MXC_MIPI_CSI2_YAV_SUBDEV_NAME, csi2dev->id);
|
||||
|
||||
csi2dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
|
||||
csi2dev->sd.dev = &pdev->dev;
|
||||
|
||||
/* First register a v4l2 device */
|
||||
ret = v4l2_device_register(dev, &csi2dev->v4l2_dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Unable to register v4l2 device.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = v4l2_async_register_subdev(&csi2dev->sd);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "%s--Async register faialed, ret=%d\n",
|
||||
__func__, ret);
|
||||
goto e_v4l_dev;
|
||||
}
|
||||
|
||||
ret = mipi_csis_subdev_host(csi2dev);
|
||||
if (ret < 0)
|
||||
goto e_clkdis;
|
||||
|
||||
/* This allows to retrieve the platform device id by the host driver */
|
||||
v4l2_set_subdevdata(&csi2dev->sd, pdev);
|
||||
|
||||
/* .. and a pointer to the subdev. */
|
||||
platform_set_drvdata(pdev, csi2dev);
|
||||
|
||||
ret = mipi_csi2_clk_enable(csi2dev);
|
||||
if (ret < 0)
|
||||
goto e_clkdis;
|
||||
|
||||
dev_info(&pdev->dev, "lanes: %d, name: %s\n",
|
||||
csi2dev->num_lanes, csi2dev->sd.name);
|
||||
|
||||
csi2dev->running = 0;
|
||||
csi2dev->flags = MXC_MIPI_CSI2_PM_POWERED;
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
e_clkdis:
|
||||
v4l2_async_unregister_subdev(&csi2dev->sd);
|
||||
e_v4l_dev:
|
||||
v4l2_device_unregister(&csi2dev->v4l2_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mipi_csi2_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct v4l2_subdev *sd = platform_get_drvdata(pdev);
|
||||
struct mxc_mipi_csi2_dev *csi2dev = sd_to_mxc_mipi_csi2_dev(sd);
|
||||
|
||||
mipi_csi2_clk_disable(csi2dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused mipi_csi2_pm_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct mxc_mipi_csi2_dev *csi2dev = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = mipi_csi2_clk_enable(csi2dev);
|
||||
if (ret < 0) {
|
||||
dev_info(dev, "%s:%d fail\n", __func__, __LINE__);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused mipi_csi2_runtime_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct mxc_mipi_csi2_dev *csi2dev = dev_get_drvdata(dev);
|
||||
|
||||
mipi_csi2_clk_disable(csi2dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused mipi_csi2_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct mxc_mipi_csi2_dev *csi2dev = dev_get_drvdata(dev);
|
||||
|
||||
if (csi2dev->flags & MXC_MIPI_CSI2_PM_SUSPENDED)
|
||||
return 0;
|
||||
|
||||
if (csi2dev->running) {
|
||||
dev_warn(dev, "running, prevent entering suspend.\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
mipi_csi2_clk_disable(csi2dev);
|
||||
csi2dev->flags &= ~MXC_MIPI_CSI2_PM_POWERED;
|
||||
csi2dev->flags |= MXC_MIPI_CSI2_PM_SUSPENDED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused mipi_csi2_pm_resume(struct device *dev)
|
||||
{
|
||||
struct mxc_mipi_csi2_dev *csi2dev = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
if (csi2dev->flags & MXC_MIPI_CSI2_PM_POWERED)
|
||||
return 0;
|
||||
|
||||
ret = mipi_csi2_clk_enable(csi2dev);
|
||||
if (ret < 0) {
|
||||
dev_info(dev, "%s:%d fail\n", __func__, __LINE__);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
csi2dev->flags |= MXC_MIPI_CSI2_PM_POWERED;
|
||||
csi2dev->flags &= ~MXC_MIPI_CSI2_PM_SUSPENDED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops mipi_csi_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(mipi_csi2_pm_suspend, mipi_csi2_pm_resume)
|
||||
SET_RUNTIME_PM_OPS(mipi_csi2_runtime_pm_suspend,
|
||||
mipi_csi2_pm_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
|
||||
static const struct of_device_id mipi_csi2_of_match[] = {
|
||||
{.compatible = "fsl,mxc-mipi-csi2_yav",},
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, mipi_csi2_of_match);
|
||||
|
||||
static struct platform_driver mipi_csi2_driver = {
|
||||
.driver = {
|
||||
.name = MXC_MIPI_CSI2_YAV_DRIVER_NAME,
|
||||
.of_match_table = mipi_csi2_of_match,
|
||||
.pm = &mipi_csi_pm_ops,
|
||||
},
|
||||
.probe = mipi_csi2_probe,
|
||||
.remove = mipi_csi2_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(mipi_csi2_driver);
|
||||
|
||||
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
|
||||
MODULE_DESCRIPTION("MXC MIPI CSI2 driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:" MXC_MIPI_CSI2_YAV_DRIVER_NAME);
|
124
drivers/media/platform/mxc/capture/Kconfig
Normal file
124
drivers/media/platform/mxc/capture/Kconfig
Normal file
|
@ -0,0 +1,124 @@
|
|||
if VIDEO_MXC_CAPTURE
|
||||
|
||||
config VIDEO_V4L2_MXC_INT_DEVICE
|
||||
tristate
|
||||
|
||||
config VIDEO_MXC_CSI_CAMERA
|
||||
tristate "CSI camera support"
|
||||
depends on VIDEO_MXC_CAPTURE && VIDEO_V4L2
|
||||
help
|
||||
This is the video4linux2 capture driver based on CSI module.
|
||||
|
||||
config MXC_VADC
|
||||
tristate "mxc VADC support"
|
||||
depends on VIDEO_MXC_CAPTURE && VIDEO_V4L2
|
||||
help
|
||||
If you plan to use the VADC with your MXC system, say Y here.
|
||||
|
||||
config MXC_MIPI_CSI
|
||||
tristate "mxc mipi csi driver"
|
||||
depends on VIDEO_MXC_CAPTURE && VIDEO_V4L2
|
||||
help
|
||||
This is a V4L2 driver for i.MX7D SoC MIPI-CSI2 receiver devices.
|
||||
|
||||
menu "MXC Camera/V4L2 PRP Features support"
|
||||
config VIDEO_MXC_IPU_CAMERA
|
||||
bool
|
||||
select VIDEO_V4L2_MXC_INT_DEVICE
|
||||
depends on VIDEO_MXC_CAPTURE && MXC_IPU
|
||||
default y
|
||||
|
||||
config MXC_CAMERA_OV5640
|
||||
tristate "OmniVision ov5640 camera support"
|
||||
depends on !VIDEO_MXC_EMMA_CAMERA && I2C
|
||||
depends on VIDEO_V4L2_MXC_INT_DEVICE
|
||||
help
|
||||
If you plan to use the ov5640 Camera with your MXC system, say Y here.
|
||||
|
||||
config MXC_CAMERA_OV5640_V2
|
||||
tristate "OmniVision ov5640 camera support"
|
||||
depends on VIDEO_MXC_CAPTURE && I2C
|
||||
help
|
||||
If you plan to use the ov5640 Camera with your MXC system, say Y here.
|
||||
|
||||
config MXC_CAMERA_OV5642
|
||||
tristate "OmniVision ov5642 camera support"
|
||||
depends on !VIDEO_MXC_EMMA_CAMERA && I2C
|
||||
depends on VIDEO_V4L2_MXC_INT_DEVICE
|
||||
help
|
||||
If you plan to use the ov5642 Camera with your MXC system, say Y here.
|
||||
|
||||
config MXC_CAMERA_OV5640_MIPI
|
||||
tristate "OmniVision ov5640 camera support using mipi"
|
||||
depends on !VIDEO_MXC_EMMA_CAMERA && I2C && MXC_MIPI_CSI2
|
||||
depends on VIDEO_V4L2_MXC_INT_DEVICE
|
||||
help
|
||||
If you plan to use the ov5640 Camera with mipi interface in your MXC system, say Y here.
|
||||
|
||||
config MXC_CAMERA_OV5640_MIPI_V2
|
||||
tristate "OmniVision ov5640 camera support using mipi"
|
||||
depends on MXC_MIPI_CSI && I2C
|
||||
help
|
||||
If you plan to use the ov5640 Camera with mipi interface in your MXC system, say Y here.
|
||||
|
||||
config MXC_CAMERA_OV5647_MIPI
|
||||
tristate "OmniVision ov5647 camera support using mipi"
|
||||
depends on MXC_MIPI_CSI && I2C
|
||||
help
|
||||
If you plan to use the ov5647 Camera with mipi interface in your MXC system, say Y here.
|
||||
|
||||
config MXC_TVIN_ADV7180
|
||||
tristate "Analog Device adv7180 TV Decoder Input support"
|
||||
depends on !VIDEO_MXC_EMMA_CAMERA && I2C
|
||||
depends on VIDEO_V4L2_MXC_INT_DEVICE
|
||||
help
|
||||
If you plan to use the adv7180 video decoder with your MXC system, say Y here.
|
||||
|
||||
choice
|
||||
prompt "Select Overlay Rounting"
|
||||
default MXC_IPU_DEVICE_QUEUE_SDC
|
||||
depends on VIDEO_MXC_IPU_CAMERA && FB_MXC_SYNC_PANEL
|
||||
|
||||
config MXC_IPU_DEVICE_QUEUE_SDC
|
||||
tristate "Queue ipu device for overlay library"
|
||||
depends on VIDEO_MXC_IPU_CAMERA
|
||||
help
|
||||
Use case CSI->MEM->IPU DEVICE->SDC:
|
||||
Images from sensor will be frist recieved in memory,then
|
||||
queue to ipu device for processing if needed, and displaying
|
||||
it on synchronous display with SDC use case.
|
||||
|
||||
config MXC_IPU_PRP_VF_SDC
|
||||
bool "Pre-Processor VF SDC library"
|
||||
depends on VIDEO_MXC_IPU_CAMERA
|
||||
help
|
||||
Use case PRP_VF_SDC:
|
||||
Preprocessing image from smart sensor for viewfinder and
|
||||
displaying it on synchronous display with SDC use case.
|
||||
If SDC BG is selected, Rotation will not be supported.
|
||||
CSI -> IC (PRP VF) -> MEM
|
||||
MEM -> IC (ROT) -> MEM
|
||||
MEM -> SDC (FG/BG)
|
||||
|
||||
endchoice
|
||||
|
||||
config MXC_IPU_PRP_ENC
|
||||
tristate "Pre-processor Encoder library"
|
||||
depends on VIDEO_MXC_IPU_CAMERA
|
||||
default y
|
||||
help
|
||||
Use case PRP_ENC:
|
||||
Preprocessing image from smart sensor for encoder.
|
||||
CSI -> IC (PRP ENC) -> MEM
|
||||
|
||||
config MXC_IPU_CSI_ENC
|
||||
tristate "IPU CSI Encoder library"
|
||||
depends on VIDEO_MXC_IPU_CAMERA
|
||||
default y
|
||||
help
|
||||
Use case IPU_CSI_ENC:
|
||||
Get raw image with CSI from smart sensor for encoder.
|
||||
CSI -> MEM
|
||||
endmenu
|
||||
|
||||
endif
|
38
drivers/media/platform/mxc/capture/Makefile
Normal file
38
drivers/media/platform/mxc/capture/Makefile
Normal file
|
@ -0,0 +1,38 @@
|
|||
ifeq ($(CONFIG_VIDEO_MXC_IPU_CAMERA),y)
|
||||
obj-$(CONFIG_VIDEO_MXC_CAPTURE) += mxc_v4l2_capture.o
|
||||
obj-$(CONFIG_MXC_IPU_PRP_VF_SDC) += ipu_prp_vf_sdc.o ipu_prp_vf_sdc_bg.o
|
||||
obj-$(CONFIG_MXC_IPU_DEVICE_QUEUE_SDC) += ipu_fg_overlay_sdc.o ipu_bg_overlay_sdc.o
|
||||
obj-$(CONFIG_MXC_IPU_PRP_ENC) += ipu_prp_enc.o ipu_still.o
|
||||
obj-$(CONFIG_MXC_IPU_CSI_ENC) += ipu_csi_enc.o ipu_still.o
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_VIDEO_MXC_CSI_CAMERA) += mx6s_capture.o
|
||||
obj-$(CONFIG_MXC_VADC) += mxc_vadc.o
|
||||
obj-$(CONFIG_MXC_MIPI_CSI) += mxc_mipi_csi.o
|
||||
|
||||
# Used for iMX 6QDL
|
||||
ov5640_camera_int-objs := ov5640.o
|
||||
obj-$(CONFIG_MXC_CAMERA_OV5640) += ov5640_camera_int.o
|
||||
|
||||
# Used for iMX 6UL/ULL/SX/SL/SLL
|
||||
ov5640_camera_v2-objs := ov5640_v2.o
|
||||
obj-$(CONFIG_MXC_CAMERA_OV5640_V2) += ov5640_camera_v2.o
|
||||
|
||||
ov5642_camera-objs := ov5642.o
|
||||
obj-$(CONFIG_MXC_CAMERA_OV5642) += ov5642_camera.o
|
||||
|
||||
# Used for iMX 6QDL/DQSCM
|
||||
ov5640_camera_mipi_int-objs := ov5640_mipi.o
|
||||
obj-$(CONFIG_MXC_CAMERA_OV5640_MIPI) += ov5640_camera_mipi_int.o
|
||||
|
||||
# Used for iMX 7D
|
||||
ov5640_camera_mipi_v2-objs := ov5640_mipi_v2.o
|
||||
obj-$(CONFIG_MXC_CAMERA_OV5640_MIPI_V2) += ov5640_camera_mipi_v2.o
|
||||
|
||||
ov5647_camera_mipi-objs := ov5647_mipi.o
|
||||
obj-$(CONFIG_MXC_CAMERA_OV5647_MIPI) += ov5647_camera_mipi.o
|
||||
|
||||
adv7180_tvin-objs := adv7180.o
|
||||
obj-$(CONFIG_MXC_TVIN_ADV7180) += adv7180_tvin.o
|
||||
|
||||
obj-$(CONFIG_VIDEO_V4L2_MXC_INT_DEVICE) += v4l2-int-device.o
|
1393
drivers/media/platform/mxc/capture/adv7180.c
Normal file
1393
drivers/media/platform/mxc/capture/adv7180.c
Normal file
File diff suppressed because it is too large
Load Diff
544
drivers/media/platform/mxc/capture/ipu_bg_overlay_sdc.c
Normal file
544
drivers/media/platform/mxc/capture/ipu_bg_overlay_sdc.c
Normal file
|
@ -0,0 +1,544 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright 2004-2015 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
* Copyright 2019 NXP
|
||||
*/
|
||||
|
||||
/*!
|
||||
* @file ipu_bg_overlay_sdc_bg.c
|
||||
*
|
||||
* @brief IPU Use case for PRP-VF back-ground
|
||||
*
|
||||
* @ingroup IPU
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/ipu.h>
|
||||
#include <linux/mipi_csi2.h>
|
||||
#include "mxc_v4l2_capture.h"
|
||||
#include "ipu_prp_sw.h"
|
||||
|
||||
static int csi_buffer_num;
|
||||
static u32 bpp, csi_mem_bufsize = 3;
|
||||
static u32 out_format;
|
||||
static struct ipu_soc *disp_ipu;
|
||||
static u32 offset;
|
||||
|
||||
static void csi_buf_work_func(struct work_struct *work)
|
||||
{
|
||||
int err = 0;
|
||||
cam_data *cam =
|
||||
container_of(work, struct _cam_data, csi_work_struct);
|
||||
|
||||
struct ipu_task task;
|
||||
memset(&task, 0, sizeof(task));
|
||||
|
||||
if (csi_buffer_num)
|
||||
task.input.paddr = cam->vf_bufs[0];
|
||||
else
|
||||
task.input.paddr = cam->vf_bufs[1];
|
||||
task.input.width = cam->crop_current.width;
|
||||
task.input.height = cam->crop_current.height;
|
||||
task.input.format = IPU_PIX_FMT_UYVY;
|
||||
|
||||
task.output.paddr = offset;
|
||||
task.output.width = cam->overlay_fb->var.xres;
|
||||
task.output.height = cam->overlay_fb->var.yres;
|
||||
task.output.format = out_format;
|
||||
task.output.rotate = cam->rotation;
|
||||
task.output.crop.pos.x = cam->win.w.left;
|
||||
task.output.crop.pos.y = cam->win.w.top;
|
||||
if (cam->win.w.width > 1024 || cam->win.w.height > 1024) {
|
||||
task.output.crop.w = cam->overlay_fb->var.xres;
|
||||
task.output.crop.h = cam->overlay_fb->var.yres;
|
||||
} else {
|
||||
task.output.crop.w = cam->win.w.width;
|
||||
task.output.crop.h = cam->win.w.height;
|
||||
}
|
||||
again:
|
||||
err = ipu_check_task(&task);
|
||||
if (err != IPU_CHECK_OK) {
|
||||
if (err > IPU_CHECK_ERR_MIN) {
|
||||
if (err == IPU_CHECK_ERR_SPLIT_INPUTW_OVER) {
|
||||
task.input.crop.w -= 8;
|
||||
goto again;
|
||||
}
|
||||
if (err == IPU_CHECK_ERR_SPLIT_INPUTH_OVER) {
|
||||
task.input.crop.h -= 8;
|
||||
goto again;
|
||||
}
|
||||
if (err == IPU_CHECK_ERR_SPLIT_OUTPUTW_OVER) {
|
||||
task.output.width -= 8;
|
||||
task.output.crop.w = task.output.width;
|
||||
goto again;
|
||||
}
|
||||
if (err == IPU_CHECK_ERR_SPLIT_OUTPUTH_OVER) {
|
||||
task.output.height -= 8;
|
||||
task.output.crop.h = task.output.height;
|
||||
goto again;
|
||||
}
|
||||
printk(KERN_ERR "check ipu taks fail\n");
|
||||
return;
|
||||
}
|
||||
printk(KERN_ERR "check ipu taks fail\n");
|
||||
return;
|
||||
}
|
||||
err = ipu_queue_task(&task);
|
||||
if (err < 0)
|
||||
printk(KERN_ERR "queue ipu task error\n");
|
||||
}
|
||||
|
||||
static void get_disp_ipu(cam_data *cam)
|
||||
{
|
||||
if (cam->output > 2)
|
||||
disp_ipu = ipu_get_soc(1); /* using DISP4 */
|
||||
else
|
||||
disp_ipu = ipu_get_soc(0);
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
* csi ENC callback function.
|
||||
*
|
||||
* @param irq int irq line
|
||||
* @param dev_id void * device id
|
||||
*
|
||||
* @return status IRQ_HANDLED for handled
|
||||
*/
|
||||
static irqreturn_t csi_enc_callback(int irq, void *dev_id)
|
||||
{
|
||||
cam_data *cam = (cam_data *) dev_id;
|
||||
ipu_channel_t chan = (irq == IPU_IRQ_CSI0_OUT_EOF) ?
|
||||
CSI_MEM0 : CSI_MEM1;
|
||||
|
||||
ipu_select_buffer(cam->ipu, chan,
|
||||
IPU_OUTPUT_BUFFER, csi_buffer_num);
|
||||
schedule_work(&cam->csi_work_struct);
|
||||
csi_buffer_num = (csi_buffer_num == 0) ? 1 : 0;
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int csi_enc_setup(cam_data *cam)
|
||||
{
|
||||
ipu_channel_params_t params;
|
||||
u32 pixel_fmt;
|
||||
int err = 0, sensor_protocol = 0;
|
||||
ipu_channel_t chan = (cam->csi == 0) ? CSI_MEM0 : CSI_MEM1;
|
||||
|
||||
#ifdef CONFIG_MXC_MIPI_CSI2
|
||||
void *mipi_csi2_info;
|
||||
int ipu_id;
|
||||
int csi_id;
|
||||
#endif
|
||||
|
||||
if (!cam) {
|
||||
printk(KERN_ERR "cam private is NULL\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
memset(¶ms, 0, sizeof(ipu_channel_params_t));
|
||||
params.csi_mem.csi = cam->csi;
|
||||
|
||||
sensor_protocol = ipu_csi_get_sensor_protocol(cam->ipu, cam->csi);
|
||||
switch (sensor_protocol) {
|
||||
case IPU_CSI_CLK_MODE_GATED_CLK:
|
||||
case IPU_CSI_CLK_MODE_NONGATED_CLK:
|
||||
case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE:
|
||||
case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR:
|
||||
case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR:
|
||||
params.csi_mem.interlaced = false;
|
||||
break;
|
||||
case IPU_CSI_CLK_MODE_CCIR656_INTERLACED:
|
||||
case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR:
|
||||
case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR:
|
||||
params.csi_mem.interlaced = true;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "sensor protocol unsupported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MXC_MIPI_CSI2
|
||||
mipi_csi2_info = mipi_csi2_get_info();
|
||||
|
||||
if (mipi_csi2_info) {
|
||||
if (mipi_csi2_get_status(mipi_csi2_info)) {
|
||||
ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info);
|
||||
csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info);
|
||||
|
||||
if (cam->ipu == ipu_get_soc(ipu_id)
|
||||
&& cam->csi == csi_id) {
|
||||
params.csi_mem.mipi_en = true;
|
||||
params.csi_mem.mipi_vc =
|
||||
mipi_csi2_get_virtual_channel(mipi_csi2_info);
|
||||
params.csi_mem.mipi_id =
|
||||
mipi_csi2_get_datatype(mipi_csi2_info);
|
||||
|
||||
mipi_csi2_pixelclk_enable(mipi_csi2_info);
|
||||
} else {
|
||||
params.csi_mem.mipi_en = false;
|
||||
params.csi_mem.mipi_vc = 0;
|
||||
params.csi_mem.mipi_id = 0;
|
||||
}
|
||||
} else {
|
||||
params.csi_mem.mipi_en = false;
|
||||
params.csi_mem.mipi_vc = 0;
|
||||
params.csi_mem.mipi_id = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (cam->vf_bufs_vaddr[0]) {
|
||||
dma_free_coherent(cam->dev, cam->vf_bufs_size[0],
|
||||
cam->vf_bufs_vaddr[0],
|
||||
(dma_addr_t) cam->vf_bufs[0]);
|
||||
}
|
||||
if (cam->vf_bufs_vaddr[1]) {
|
||||
dma_free_coherent(cam->dev, cam->vf_bufs_size[1],
|
||||
cam->vf_bufs_vaddr[1],
|
||||
(dma_addr_t) cam->vf_bufs[1]);
|
||||
}
|
||||
csi_mem_bufsize =
|
||||
cam->crop_current.width * cam->crop_current.height * 2;
|
||||
cam->vf_bufs_size[0] = PAGE_ALIGN(csi_mem_bufsize);
|
||||
cam->vf_bufs_vaddr[0] = (void *)dma_alloc_coherent(cam->dev,
|
||||
cam->vf_bufs_size[0],
|
||||
(dma_addr_t *) &
|
||||
cam->vf_bufs[0],
|
||||
GFP_DMA |
|
||||
GFP_KERNEL);
|
||||
if (cam->vf_bufs_vaddr[0] == NULL) {
|
||||
printk(KERN_ERR "Error to allocate vf buffer\n");
|
||||
err = -ENOMEM;
|
||||
goto out_2;
|
||||
}
|
||||
cam->vf_bufs_size[1] = PAGE_ALIGN(csi_mem_bufsize);
|
||||
cam->vf_bufs_vaddr[1] = (void *)dma_alloc_coherent(cam->dev,
|
||||
cam->vf_bufs_size[1],
|
||||
(dma_addr_t *) &
|
||||
cam->vf_bufs[1],
|
||||
GFP_DMA |
|
||||
GFP_KERNEL);
|
||||
if (cam->vf_bufs_vaddr[1] == NULL) {
|
||||
printk(KERN_ERR "Error to allocate vf buffer\n");
|
||||
err = -ENOMEM;
|
||||
goto out_1;
|
||||
}
|
||||
pr_debug("vf_bufs %x %x\n", cam->vf_bufs[0], cam->vf_bufs[1]);
|
||||
|
||||
err = ipu_init_channel(cam->ipu, chan, ¶ms);
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "ipu_init_channel %d\n", err);
|
||||
goto out_1;
|
||||
}
|
||||
|
||||
pixel_fmt = IPU_PIX_FMT_UYVY;
|
||||
err = ipu_init_channel_buffer(
|
||||
cam->ipu, chan, IPU_OUTPUT_BUFFER, pixel_fmt,
|
||||
cam->crop_current.width, cam->crop_current.height,
|
||||
cam->crop_current.width, IPU_ROTATE_NONE,
|
||||
cam->vf_bufs[0], cam->vf_bufs[1], 0,
|
||||
cam->offset.u_offset, cam->offset.u_offset);
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "CSI_MEM output buffer\n");
|
||||
goto out_1;
|
||||
}
|
||||
err = ipu_enable_channel(cam->ipu, chan);
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR "ipu_enable_channel CSI_MEM\n");
|
||||
goto out_1;
|
||||
}
|
||||
|
||||
csi_buffer_num = 0;
|
||||
|
||||
ipu_select_buffer(cam->ipu, chan, IPU_OUTPUT_BUFFER, 0);
|
||||
ipu_select_buffer(cam->ipu, chan, IPU_OUTPUT_BUFFER, 1);
|
||||
return err;
|
||||
out_1:
|
||||
if (cam->vf_bufs_vaddr[0]) {
|
||||
dma_free_coherent(cam->dev, cam->vf_bufs_size[0],
|
||||
cam->vf_bufs_vaddr[0],
|
||||
(dma_addr_t) cam->vf_bufs[0]);
|
||||
cam->vf_bufs_vaddr[0] = NULL;
|
||||
cam->vf_bufs[0] = 0;
|
||||
}
|
||||
if (cam->vf_bufs_vaddr[1]) {
|
||||
dma_free_coherent(cam->dev, cam->vf_bufs_size[1],
|
||||
cam->vf_bufs_vaddr[1],
|
||||
(dma_addr_t) cam->vf_bufs[1]);
|
||||
cam->vf_bufs_vaddr[1] = NULL;
|
||||
cam->vf_bufs[1] = 0;
|
||||
}
|
||||
out_2:
|
||||
return err;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Enable encoder task
|
||||
* @param private struct cam_data * mxc capture instance
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
static int csi_enc_enabling_tasks(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
int err = 0;
|
||||
|
||||
ipu_clear_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF + cam->csi);
|
||||
err = ipu_request_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF + cam->csi,
|
||||
csi_enc_callback, 0, "Mxc Camera", cam);
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "Error registering CSI_OUT_EOF irq\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
INIT_WORK(&cam->csi_work_struct, csi_buf_work_func);
|
||||
|
||||
err = csi_enc_setup(cam);
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "csi_enc_setup %d\n", err);
|
||||
goto out1;
|
||||
}
|
||||
|
||||
return err;
|
||||
out1:
|
||||
ipu_free_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF + cam->csi, cam);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*!
|
||||
* bg_overlay_start - start the overlay task
|
||||
*
|
||||
* @param private cam_data * mxc v4l2 main structure
|
||||
*
|
||||
*/
|
||||
static int bg_overlay_start(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
int err = 0;
|
||||
|
||||
if (!cam) {
|
||||
printk(KERN_ERR "private is NULL\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (cam->overlay_active == true) {
|
||||
pr_debug("already start.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
get_disp_ipu(cam);
|
||||
|
||||
out_format = cam->v4l2_fb.fmt.pixelformat;
|
||||
if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_BGR24) {
|
||||
bpp = 3, csi_mem_bufsize = 3;
|
||||
pr_info("BGR24\n");
|
||||
} else if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_RGB565) {
|
||||
bpp = 2, csi_mem_bufsize = 2;
|
||||
pr_info("RGB565\n");
|
||||
} else if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_BGR32) {
|
||||
bpp = 4, csi_mem_bufsize = 4;
|
||||
pr_info("BGR32\n");
|
||||
} else {
|
||||
printk(KERN_ERR
|
||||
"unsupported fix format from the framebuffer.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
offset = cam->v4l2_fb.fmt.bytesperline * cam->win.w.top +
|
||||
csi_mem_bufsize * cam->win.w.left;
|
||||
|
||||
if (cam->v4l2_fb.base == 0)
|
||||
printk(KERN_ERR "invalid frame buffer address.\n");
|
||||
else
|
||||
offset += (u32) cam->v4l2_fb.base;
|
||||
|
||||
csi_mem_bufsize = cam->win.w.width * cam->win.w.height
|
||||
* csi_mem_bufsize;
|
||||
|
||||
err = csi_enc_enabling_tasks(cam);
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "Error csi enc enable fail\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
cam->overlay_active = true;
|
||||
return err;
|
||||
}
|
||||
|
||||
/*!
|
||||
* bg_overlay_stop - stop the overlay task
|
||||
*
|
||||
* @param private cam_data * mxc v4l2 main structure
|
||||
*
|
||||
*/
|
||||
static int bg_overlay_stop(void *private)
|
||||
{
|
||||
int err = 0;
|
||||
cam_data *cam = (cam_data *) private;
|
||||
ipu_channel_t chan = (cam->csi == 0) ? CSI_MEM0 : CSI_MEM1;
|
||||
#ifdef CONFIG_MXC_MIPI_CSI2
|
||||
void *mipi_csi2_info;
|
||||
int ipu_id;
|
||||
int csi_id;
|
||||
#endif
|
||||
|
||||
if (cam->overlay_active == false)
|
||||
return 0;
|
||||
|
||||
err = ipu_disable_channel(cam->ipu, chan, true);
|
||||
|
||||
ipu_uninit_channel(cam->ipu, chan);
|
||||
|
||||
csi_buffer_num = 0;
|
||||
|
||||
#ifdef CONFIG_MXC_MIPI_CSI2
|
||||
mipi_csi2_info = mipi_csi2_get_info();
|
||||
|
||||
if (mipi_csi2_info) {
|
||||
if (mipi_csi2_get_status(mipi_csi2_info)) {
|
||||
ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info);
|
||||
csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info);
|
||||
|
||||
if (cam->ipu == ipu_get_soc(ipu_id)
|
||||
&& cam->csi == csi_id)
|
||||
mipi_csi2_pixelclk_disable(mipi_csi2_info);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
flush_work(&cam->csi_work_struct);
|
||||
cancel_work_sync(&cam->csi_work_struct);
|
||||
|
||||
if (cam->vf_bufs_vaddr[0]) {
|
||||
dma_free_coherent(cam->dev, cam->vf_bufs_size[0],
|
||||
cam->vf_bufs_vaddr[0], cam->vf_bufs[0]);
|
||||
cam->vf_bufs_vaddr[0] = NULL;
|
||||
cam->vf_bufs[0] = 0;
|
||||
}
|
||||
if (cam->vf_bufs_vaddr[1]) {
|
||||
dma_free_coherent(cam->dev, cam->vf_bufs_size[1],
|
||||
cam->vf_bufs_vaddr[1], cam->vf_bufs[1]);
|
||||
cam->vf_bufs_vaddr[1] = NULL;
|
||||
cam->vf_bufs[1] = 0;
|
||||
}
|
||||
if (cam->rot_vf_bufs_vaddr[0]) {
|
||||
dma_free_coherent(cam->dev, cam->rot_vf_buf_size[0],
|
||||
cam->rot_vf_bufs_vaddr[0],
|
||||
cam->rot_vf_bufs[0]);
|
||||
cam->rot_vf_bufs_vaddr[0] = NULL;
|
||||
cam->rot_vf_bufs[0] = 0;
|
||||
}
|
||||
if (cam->rot_vf_bufs_vaddr[1]) {
|
||||
dma_free_coherent(cam->dev, cam->rot_vf_buf_size[1],
|
||||
cam->rot_vf_bufs_vaddr[1],
|
||||
cam->rot_vf_bufs[1]);
|
||||
cam->rot_vf_bufs_vaddr[1] = NULL;
|
||||
cam->rot_vf_bufs[1] = 0;
|
||||
}
|
||||
|
||||
cam->overlay_active = false;
|
||||
return err;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Enable csi
|
||||
* @param private struct cam_data * mxc capture instance
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
static int bg_overlay_enable_csi(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
|
||||
return ipu_enable_csi(cam->ipu, cam->csi);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Disable csi
|
||||
* @param private struct cam_data * mxc capture instance
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
static int bg_overlay_disable_csi(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
|
||||
/* free csi eof irq firstly.
|
||||
* when disable csi, wait for idmac eof.
|
||||
* it requests eof irq again */
|
||||
ipu_free_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF + cam->csi, cam);
|
||||
|
||||
return ipu_disable_csi(cam->ipu, cam->csi);
|
||||
}
|
||||
|
||||
/*!
|
||||
* function to select bg as the working path
|
||||
*
|
||||
* @param private cam_data * mxc v4l2 main structure
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
int bg_overlay_sdc_select(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
|
||||
if (cam) {
|
||||
cam->vf_start_sdc = bg_overlay_start;
|
||||
cam->vf_stop_sdc = bg_overlay_stop;
|
||||
cam->vf_enable_csi = bg_overlay_enable_csi;
|
||||
cam->vf_disable_csi = bg_overlay_disable_csi;
|
||||
cam->overlay_active = false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(bg_overlay_sdc_select);
|
||||
|
||||
/*!
|
||||
* function to de-select bg as the working path
|
||||
*
|
||||
* @param private cam_data * mxc v4l2 main structure
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
int bg_overlay_sdc_deselect(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
|
||||
if (cam) {
|
||||
cam->vf_start_sdc = NULL;
|
||||
cam->vf_stop_sdc = NULL;
|
||||
cam->vf_enable_csi = NULL;
|
||||
cam->vf_disable_csi = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(bg_overlay_sdc_deselect);
|
||||
|
||||
/*!
|
||||
* Init background overlay task.
|
||||
*
|
||||
* @return Error code indicating success or failure
|
||||
*/
|
||||
__init int bg_overlay_sdc_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Deinit background overlay task.
|
||||
*
|
||||
* @return Error code indicating success or failure
|
||||
*/
|
||||
void __exit bg_overlay_sdc_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
module_init(bg_overlay_sdc_init);
|
||||
module_exit(bg_overlay_sdc_exit);
|
||||
|
||||
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
|
||||
MODULE_DESCRIPTION("IPU PRP VF SDC Backgroud Driver");
|
||||
MODULE_LICENSE("GPL");
|
423
drivers/media/platform/mxc/capture/ipu_csi_enc.c
Normal file
423
drivers/media/platform/mxc/capture/ipu_csi_enc.c
Normal file
|
@ -0,0 +1,423 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright 2009-2015 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
* Copyright 2019 NXP
|
||||
*/
|
||||
|
||||
/*!
|
||||
* @file ipu_csi_enc.c
|
||||
*
|
||||
* @brief CSI Use case for video capture
|
||||
*
|
||||
* @ingroup IPU
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/ipu.h>
|
||||
#include <linux/mipi_csi2.h>
|
||||
#include "mxc_v4l2_capture.h"
|
||||
#include "ipu_prp_sw.h"
|
||||
|
||||
#ifdef CAMERA_DBG
|
||||
#define CAMERA_TRACE(x) (printk)x
|
||||
#else
|
||||
#define CAMERA_TRACE(x)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Function definitions
|
||||
*/
|
||||
|
||||
/*!
|
||||
* csi ENC callback function.
|
||||
*
|
||||
* @param irq int irq line
|
||||
* @param dev_id void * device id
|
||||
*
|
||||
* @return status IRQ_HANDLED for handled
|
||||
*/
|
||||
static irqreturn_t csi_enc_callback(int irq, void *dev_id)
|
||||
{
|
||||
cam_data *cam = (cam_data *) dev_id;
|
||||
|
||||
if (cam->enc_callback == NULL)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
cam->enc_callback(irq, dev_id);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*!
|
||||
* CSI ENC enable channel setup function
|
||||
*
|
||||
* @param cam struct cam_data * mxc capture instance
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
static int csi_enc_setup(cam_data *cam)
|
||||
{
|
||||
ipu_channel_params_t params;
|
||||
u32 pixel_fmt;
|
||||
int err = 0, sensor_protocol = 0;
|
||||
dma_addr_t dummy = cam->dummy_frame.buffer.m.offset;
|
||||
ipu_channel_t chan = (cam->csi == 0) ? CSI_MEM0 : CSI_MEM1;
|
||||
#ifdef CONFIG_MXC_MIPI_CSI2
|
||||
void *mipi_csi2_info;
|
||||
int ipu_id;
|
||||
int csi_id;
|
||||
#endif
|
||||
|
||||
CAMERA_TRACE("In csi_enc_setup\n");
|
||||
if (!cam) {
|
||||
printk(KERN_ERR "cam private is NULL\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
memset(¶ms, 0, sizeof(ipu_channel_params_t));
|
||||
params.csi_mem.csi = cam->csi;
|
||||
|
||||
sensor_protocol = ipu_csi_get_sensor_protocol(cam->ipu, cam->csi);
|
||||
switch (sensor_protocol) {
|
||||
case IPU_CSI_CLK_MODE_GATED_CLK:
|
||||
case IPU_CSI_CLK_MODE_NONGATED_CLK:
|
||||
case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE:
|
||||
case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR:
|
||||
case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR:
|
||||
params.csi_mem.interlaced = false;
|
||||
break;
|
||||
case IPU_CSI_CLK_MODE_CCIR656_INTERLACED:
|
||||
case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR:
|
||||
case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR:
|
||||
params.csi_mem.interlaced = true;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "sensor protocol unsupported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420)
|
||||
pixel_fmt = IPU_PIX_FMT_YUV420P;
|
||||
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YVU420)
|
||||
pixel_fmt = IPU_PIX_FMT_YVU420P;
|
||||
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P)
|
||||
pixel_fmt = IPU_PIX_FMT_YUV422P;
|
||||
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY)
|
||||
pixel_fmt = IPU_PIX_FMT_UYVY;
|
||||
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
|
||||
pixel_fmt = IPU_PIX_FMT_YUYV;
|
||||
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_NV12)
|
||||
pixel_fmt = IPU_PIX_FMT_NV12;
|
||||
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24)
|
||||
pixel_fmt = IPU_PIX_FMT_BGR24;
|
||||
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24)
|
||||
pixel_fmt = IPU_PIX_FMT_RGB24;
|
||||
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565)
|
||||
pixel_fmt = IPU_PIX_FMT_RGB565;
|
||||
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR32)
|
||||
pixel_fmt = IPU_PIX_FMT_BGR32;
|
||||
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB32)
|
||||
pixel_fmt = IPU_PIX_FMT_RGB32;
|
||||
else {
|
||||
printk(KERN_ERR "format not supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MXC_MIPI_CSI2
|
||||
mipi_csi2_info = mipi_csi2_get_info();
|
||||
|
||||
if (mipi_csi2_info) {
|
||||
if (mipi_csi2_get_status(mipi_csi2_info)) {
|
||||
ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info);
|
||||
csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info);
|
||||
|
||||
if (cam->ipu == ipu_get_soc(ipu_id)
|
||||
&& cam->csi == csi_id) {
|
||||
params.csi_mem.mipi_en = true;
|
||||
params.csi_mem.mipi_vc =
|
||||
mipi_csi2_get_virtual_channel(mipi_csi2_info);
|
||||
params.csi_mem.mipi_id =
|
||||
mipi_csi2_get_datatype(mipi_csi2_info);
|
||||
|
||||
mipi_csi2_pixelclk_enable(mipi_csi2_info);
|
||||
} else {
|
||||
params.csi_mem.mipi_en = false;
|
||||
params.csi_mem.mipi_vc = 0;
|
||||
params.csi_mem.mipi_id = 0;
|
||||
}
|
||||
} else {
|
||||
params.csi_mem.mipi_en = false;
|
||||
params.csi_mem.mipi_vc = 0;
|
||||
params.csi_mem.mipi_id = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
err = ipu_init_channel(cam->ipu, chan, ¶ms);
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "ipu_init_channel %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = ipu_init_channel_buffer(cam->ipu,
|
||||
chan,
|
||||
IPU_OUTPUT_BUFFER,
|
||||
pixel_fmt, cam->v2f.fmt.pix.width,
|
||||
cam->v2f.fmt.pix.height,
|
||||
cam->v2f.fmt.pix.bytesperline,
|
||||
IPU_ROTATE_NONE,
|
||||
dummy, dummy, 0,
|
||||
cam->offset.u_offset,
|
||||
cam->offset.v_offset);
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "CSI_MEM output buffer\n");
|
||||
return err;
|
||||
}
|
||||
err = ipu_enable_channel(cam->ipu, chan);
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR "ipu_enable_channel CSI_MEM\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*!
|
||||
* function to update physical buffer address for encorder IDMA channel
|
||||
*
|
||||
* @param *private pointer to the cam_data structure
|
||||
* @param eba physical buffer address for encorder IDMA channel
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
static int csi_enc_eba_update(void *private, dma_addr_t eba)
|
||||
{
|
||||
int err = 0;
|
||||
cam_data *cam = (cam_data *) private;
|
||||
struct ipu_soc *ipu = cam->ipu;
|
||||
int *buffer_num = &cam->ping_pong_csi;
|
||||
ipu_channel_t chan = (cam->csi == 0) ? CSI_MEM0 : CSI_MEM1;
|
||||
|
||||
pr_debug("eba %x\n", eba);
|
||||
err = ipu_update_channel_buffer(ipu, chan, IPU_OUTPUT_BUFFER,
|
||||
*buffer_num, eba);
|
||||
if (err != 0) {
|
||||
ipu_clear_buffer_ready(ipu, chan, IPU_OUTPUT_BUFFER,
|
||||
*buffer_num);
|
||||
|
||||
err = ipu_update_channel_buffer(ipu, chan,
|
||||
IPU_OUTPUT_BUFFER, *buffer_num, eba);
|
||||
if (err != 0) {
|
||||
pr_err("ERROR: v4l2 capture: fail to update "
|
||||
"buf%d\n", *buffer_num);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
ipu_select_buffer(ipu, chan, IPU_OUTPUT_BUFFER, *buffer_num);
|
||||
|
||||
*buffer_num = (*buffer_num == 0) ? 1 : 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Enable encoder task
|
||||
* @param private struct cam_data * mxc capture instance
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
static int csi_enc_enabling_tasks(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
int err = 0;
|
||||
uint32_t irq = (cam->csi == 0) ?
|
||||
IPU_IRQ_CSI0_OUT_EOF : IPU_IRQ_CSI1_OUT_EOF;
|
||||
|
||||
CAMERA_TRACE("IPU:In csi_enc_enabling_tasks\n");
|
||||
|
||||
cam->dummy_frame.vaddress = dma_alloc_coherent(cam->dev,
|
||||
PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage),
|
||||
&cam->dummy_frame.paddress,
|
||||
GFP_DMA | GFP_KERNEL);
|
||||
if (cam->dummy_frame.vaddress == 0) {
|
||||
pr_err("ERROR: v4l2 capture: Allocate dummy frame "
|
||||
"failed.\n");
|
||||
return -ENOBUFS;
|
||||
}
|
||||
cam->dummy_frame.buffer.type = V4L2_BUF_TYPE_PRIVATE;
|
||||
cam->dummy_frame.buffer.length =
|
||||
PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage);
|
||||
cam->dummy_frame.buffer.m.offset = cam->dummy_frame.paddress;
|
||||
|
||||
ipu_clear_irq(cam->ipu, irq);
|
||||
err = ipu_request_irq(
|
||||
cam->ipu, irq, csi_enc_callback, 0, "Mxc Camera", cam);
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "Error registering rot irq\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = csi_enc_setup(cam);
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "csi_enc_setup %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Disable encoder task
|
||||
* @param private struct cam_data * mxc capture instance
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
static int csi_enc_disabling_tasks(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
int err = 0;
|
||||
ipu_channel_t chan = (cam->csi == 0) ? CSI_MEM0 : CSI_MEM1;
|
||||
#ifdef CONFIG_MXC_MIPI_CSI2
|
||||
void *mipi_csi2_info;
|
||||
int ipu_id;
|
||||
int csi_id;
|
||||
#endif
|
||||
|
||||
err = ipu_disable_channel(cam->ipu, chan, true);
|
||||
|
||||
ipu_uninit_channel(cam->ipu, chan);
|
||||
|
||||
if (cam->dummy_frame.vaddress != 0) {
|
||||
dma_free_coherent(cam->dev, cam->dummy_frame.buffer.length,
|
||||
cam->dummy_frame.vaddress,
|
||||
cam->dummy_frame.paddress);
|
||||
cam->dummy_frame.vaddress = 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MXC_MIPI_CSI2
|
||||
mipi_csi2_info = mipi_csi2_get_info();
|
||||
|
||||
if (mipi_csi2_info) {
|
||||
if (mipi_csi2_get_status(mipi_csi2_info)) {
|
||||
ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info);
|
||||
csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info);
|
||||
|
||||
if (cam->ipu == ipu_get_soc(ipu_id)
|
||||
&& cam->csi == csi_id)
|
||||
mipi_csi2_pixelclk_disable(mipi_csi2_info);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Enable csi
|
||||
* @param private struct cam_data * mxc capture instance
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
static int csi_enc_enable_csi(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
|
||||
return ipu_enable_csi(cam->ipu, cam->csi);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Disable csi
|
||||
* @param private struct cam_data * mxc capture instance
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
static int csi_enc_disable_csi(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
uint32_t irq = (cam->csi == 0) ?
|
||||
IPU_IRQ_CSI0_OUT_EOF : IPU_IRQ_CSI1_OUT_EOF;
|
||||
|
||||
/* free csi eof irq firstly.
|
||||
* when disable csi, wait for idmac eof.
|
||||
* it requests eof irq again */
|
||||
ipu_free_irq(cam->ipu, irq, cam);
|
||||
|
||||
return ipu_disable_csi(cam->ipu, cam->csi);
|
||||
}
|
||||
|
||||
/*!
|
||||
* function to select CSI ENC as the working path
|
||||
*
|
||||
* @param private struct cam_data * mxc capture instance
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int csi_enc_select(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
int err = 0;
|
||||
|
||||
if (cam) {
|
||||
cam->enc_update_eba = csi_enc_eba_update;
|
||||
cam->enc_enable = csi_enc_enabling_tasks;
|
||||
cam->enc_disable = csi_enc_disabling_tasks;
|
||||
cam->enc_enable_csi = csi_enc_enable_csi;
|
||||
cam->enc_disable_csi = csi_enc_disable_csi;
|
||||
} else {
|
||||
err = -EIO;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(csi_enc_select);
|
||||
|
||||
/*!
|
||||
* function to de-select CSI ENC as the working path
|
||||
*
|
||||
* @param private struct cam_data * mxc capture instance
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int csi_enc_deselect(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
int err = 0;
|
||||
|
||||
if (cam) {
|
||||
cam->enc_update_eba = NULL;
|
||||
cam->enc_enable = NULL;
|
||||
cam->enc_disable = NULL;
|
||||
cam->enc_enable_csi = NULL;
|
||||
cam->enc_disable_csi = NULL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(csi_enc_deselect);
|
||||
|
||||
/*!
|
||||
* Init the Encorder channels
|
||||
*
|
||||
* @return Error code indicating success or failure
|
||||
*/
|
||||
__init int csi_enc_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Deinit the Encorder channels
|
||||
*
|
||||
*/
|
||||
void __exit csi_enc_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
module_init(csi_enc_init);
|
||||
module_exit(csi_enc_exit);
|
||||
|
||||
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
|
||||
MODULE_DESCRIPTION("CSI ENC Driver");
|
||||
MODULE_LICENSE("GPL");
|
633
drivers/media/platform/mxc/capture/ipu_fg_overlay_sdc.c
Normal file
633
drivers/media/platform/mxc/capture/ipu_fg_overlay_sdc.c
Normal file
|
@ -0,0 +1,633 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright 2004-2015 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
* Copyright 2019 NXP
|
||||
*/
|
||||
|
||||
/*!
|
||||
* @file ipu_foreground_sdc.c
|
||||
*
|
||||
* @brief IPU Use case for PRP-VF
|
||||
*
|
||||
* @ingroup IPU
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/ipu.h>
|
||||
#include <linux/mxcfb.h>
|
||||
#include <linux/mipi_csi2.h>
|
||||
|
||||
#include "mxc_v4l2_capture.h"
|
||||
#include "ipu_prp_sw.h"
|
||||
|
||||
#ifdef CAMERA_DBG
|
||||
#define CAMERA_TRACE(x) (printk)x
|
||||
#else
|
||||
#define CAMERA_TRACE(x)
|
||||
#endif
|
||||
|
||||
static int csi_buffer_num, buffer_num;
|
||||
static u32 csi_mem_bufsize;
|
||||
static struct ipu_soc *disp_ipu;
|
||||
static struct fb_info *fbi;
|
||||
static struct fb_var_screeninfo fbvar;
|
||||
static u32 vf_out_format;
|
||||
static void csi_buf_work_func(struct work_struct *work)
|
||||
{
|
||||
int err = 0;
|
||||
cam_data *cam =
|
||||
container_of(work, struct _cam_data, csi_work_struct);
|
||||
|
||||
struct ipu_task task;
|
||||
memset(&task, 0, sizeof(task));
|
||||
|
||||
if (csi_buffer_num)
|
||||
task.input.paddr = cam->vf_bufs[0];
|
||||
else
|
||||
task.input.paddr = cam->vf_bufs[1];
|
||||
task.input.width = cam->crop_current.width;
|
||||
task.input.height = cam->crop_current.height;
|
||||
task.input.format = IPU_PIX_FMT_NV12;
|
||||
|
||||
if (buffer_num == 0)
|
||||
task.output.paddr = fbi->fix.smem_start +
|
||||
(fbi->fix.line_length * fbvar.yres);
|
||||
else
|
||||
task.output.paddr = fbi->fix.smem_start;
|
||||
task.output.width = cam->win.w.width;
|
||||
task.output.height = cam->win.w.height;
|
||||
task.output.format = vf_out_format;
|
||||
task.output.rotate = cam->rotation;
|
||||
again:
|
||||
err = ipu_check_task(&task);
|
||||
if (err != IPU_CHECK_OK) {
|
||||
if (err > IPU_CHECK_ERR_MIN) {
|
||||
if (err == IPU_CHECK_ERR_SPLIT_INPUTW_OVER) {
|
||||
task.input.crop.w -= 8;
|
||||
goto again;
|
||||
}
|
||||
if (err == IPU_CHECK_ERR_SPLIT_INPUTH_OVER) {
|
||||
task.input.crop.h -= 8;
|
||||
goto again;
|
||||
}
|
||||
if (err == IPU_CHECK_ERR_SPLIT_OUTPUTW_OVER) {
|
||||
task.output.width -= 8;
|
||||
task.output.crop.w = task.output.width;
|
||||
goto again;
|
||||
}
|
||||
if (err == IPU_CHECK_ERR_SPLIT_OUTPUTH_OVER) {
|
||||
task.output.height -= 8;
|
||||
task.output.crop.h = task.output.height;
|
||||
goto again;
|
||||
}
|
||||
printk(KERN_ERR "check ipu taks fail\n");
|
||||
return;
|
||||
}
|
||||
printk(KERN_ERR "check ipu taks fail\n");
|
||||
return;
|
||||
}
|
||||
err = ipu_queue_task(&task);
|
||||
if (err < 0)
|
||||
printk(KERN_ERR "queue ipu task error\n");
|
||||
ipu_select_buffer(disp_ipu, MEM_FG_SYNC, IPU_INPUT_BUFFER, buffer_num);
|
||||
buffer_num = (buffer_num == 0) ? 1 : 0;
|
||||
}
|
||||
|
||||
static void get_disp_ipu(cam_data *cam)
|
||||
{
|
||||
if (cam->output > 2)
|
||||
disp_ipu = ipu_get_soc(1); /* using DISP4 */
|
||||
else
|
||||
disp_ipu = ipu_get_soc(0);
|
||||
}
|
||||
|
||||
/*!
|
||||
* csi ENC callback function.
|
||||
*
|
||||
* @param irq int irq line
|
||||
* @param dev_id void * device id
|
||||
*
|
||||
* @return status IRQ_HANDLED for handled
|
||||
*/
|
||||
static irqreturn_t csi_enc_callback(int irq, void *dev_id)
|
||||
{
|
||||
cam_data *cam = (cam_data *) dev_id;
|
||||
ipu_channel_t chan = (irq == IPU_IRQ_CSI0_OUT_EOF) ?
|
||||
CSI_MEM0 : CSI_MEM1;
|
||||
|
||||
ipu_select_buffer(cam->ipu, chan,
|
||||
IPU_OUTPUT_BUFFER, csi_buffer_num);
|
||||
|
||||
if ((cam->crop_current.width != cam->win.w.width) ||
|
||||
(cam->crop_current.height != cam->win.w.height) ||
|
||||
(vf_out_format != IPU_PIX_FMT_NV12) ||
|
||||
(cam->rotation >= IPU_ROTATE_VERT_FLIP))
|
||||
schedule_work(&cam->csi_work_struct);
|
||||
csi_buffer_num = (csi_buffer_num == 0) ? 1 : 0;
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int csi_enc_setup(cam_data *cam)
|
||||
{
|
||||
ipu_channel_params_t params;
|
||||
int err = 0, sensor_protocol = 0;
|
||||
ipu_channel_t chan = (cam->csi == 0) ? CSI_MEM0 : CSI_MEM1;
|
||||
#ifdef CONFIG_MXC_MIPI_CSI2
|
||||
void *mipi_csi2_info;
|
||||
int ipu_id;
|
||||
int csi_id;
|
||||
#endif
|
||||
|
||||
CAMERA_TRACE("In csi_enc_setup\n");
|
||||
if (!cam) {
|
||||
printk(KERN_ERR "cam private is NULL\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
memset(¶ms, 0, sizeof(ipu_channel_params_t));
|
||||
params.csi_mem.csi = cam->csi;
|
||||
|
||||
sensor_protocol = ipu_csi_get_sensor_protocol(cam->ipu, cam->csi);
|
||||
switch (sensor_protocol) {
|
||||
case IPU_CSI_CLK_MODE_GATED_CLK:
|
||||
case IPU_CSI_CLK_MODE_NONGATED_CLK:
|
||||
case IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE:
|
||||
case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR:
|
||||
case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR:
|
||||
params.csi_mem.interlaced = false;
|
||||
break;
|
||||
case IPU_CSI_CLK_MODE_CCIR656_INTERLACED:
|
||||
case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR:
|
||||
case IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR:
|
||||
params.csi_mem.interlaced = true;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "sensor protocol unsupported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MXC_MIPI_CSI2
|
||||
mipi_csi2_info = mipi_csi2_get_info();
|
||||
|
||||
if (mipi_csi2_info) {
|
||||
if (mipi_csi2_get_status(mipi_csi2_info)) {
|
||||
ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info);
|
||||
csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info);
|
||||
|
||||
if (cam->ipu == ipu_get_soc(ipu_id)
|
||||
&& cam->csi == csi_id) {
|
||||
params.csi_mem.mipi_en = true;
|
||||
params.csi_mem.mipi_vc =
|
||||
mipi_csi2_get_virtual_channel(mipi_csi2_info);
|
||||
params.csi_mem.mipi_id =
|
||||
mipi_csi2_get_datatype(mipi_csi2_info);
|
||||
|
||||
mipi_csi2_pixelclk_enable(mipi_csi2_info);
|
||||
} else {
|
||||
params.csi_mem.mipi_en = false;
|
||||
params.csi_mem.mipi_vc = 0;
|
||||
params.csi_mem.mipi_id = 0;
|
||||
}
|
||||
} else {
|
||||
params.csi_mem.mipi_en = false;
|
||||
params.csi_mem.mipi_vc = 0;
|
||||
params.csi_mem.mipi_id = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (cam->vf_bufs_vaddr[0]) {
|
||||
dma_free_coherent(cam->dev, cam->vf_bufs_size[0],
|
||||
cam->vf_bufs_vaddr[0],
|
||||
(dma_addr_t) cam->vf_bufs[0]);
|
||||
}
|
||||
if (cam->vf_bufs_vaddr[1]) {
|
||||
dma_free_coherent(cam->dev, cam->vf_bufs_size[1],
|
||||
cam->vf_bufs_vaddr[1],
|
||||
(dma_addr_t) cam->vf_bufs[1]);
|
||||
}
|
||||
csi_mem_bufsize = cam->crop_current.width *
|
||||
cam->crop_current.height * 3/2;
|
||||
cam->vf_bufs_size[0] = PAGE_ALIGN(csi_mem_bufsize);
|
||||
cam->vf_bufs_vaddr[0] = (void *)dma_alloc_coherent(cam->dev,
|
||||
cam->vf_bufs_size[0],
|
||||
(dma_addr_t *) &
|
||||
cam->vf_bufs[0],
|
||||
GFP_DMA |
|
||||
GFP_KERNEL);
|
||||
if (cam->vf_bufs_vaddr[0] == NULL) {
|
||||
printk(KERN_ERR "Error to allocate vf buffer\n");
|
||||
err = -ENOMEM;
|
||||
goto out_2;
|
||||
}
|
||||
cam->vf_bufs_size[1] = PAGE_ALIGN(csi_mem_bufsize);
|
||||
cam->vf_bufs_vaddr[1] = (void *)dma_alloc_coherent(cam->dev,
|
||||
cam->vf_bufs_size[1],
|
||||
(dma_addr_t *) &
|
||||
cam->vf_bufs[1],
|
||||
GFP_DMA |
|
||||
GFP_KERNEL);
|
||||
if (cam->vf_bufs_vaddr[1] == NULL) {
|
||||
printk(KERN_ERR "Error to allocate vf buffer\n");
|
||||
err = -ENOMEM;
|
||||
goto out_1;
|
||||
}
|
||||
pr_debug("vf_bufs %x %x\n", cam->vf_bufs[0], cam->vf_bufs[1]);
|
||||
|
||||
err = ipu_init_channel(cam->ipu, chan, ¶ms);
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "ipu_init_channel %d\n", err);
|
||||
goto out_1;
|
||||
}
|
||||
|
||||
if ((cam->crop_current.width == cam->win.w.width) &&
|
||||
(cam->crop_current.height == cam->win.w.height) &&
|
||||
(vf_out_format == IPU_PIX_FMT_NV12) &&
|
||||
(cam->rotation < IPU_ROTATE_VERT_FLIP)) {
|
||||
err = ipu_init_channel_buffer(cam->ipu, chan,
|
||||
IPU_OUTPUT_BUFFER, IPU_PIX_FMT_NV12,
|
||||
cam->crop_current.width,
|
||||
cam->crop_current.height,
|
||||
cam->crop_current.width, IPU_ROTATE_NONE,
|
||||
fbi->fix.smem_start +
|
||||
(fbi->fix.line_length * fbvar.yres),
|
||||
fbi->fix.smem_start, 0,
|
||||
cam->offset.u_offset, cam->offset.u_offset);
|
||||
} else {
|
||||
err = ipu_init_channel_buffer(cam->ipu, chan,
|
||||
IPU_OUTPUT_BUFFER, IPU_PIX_FMT_NV12,
|
||||
cam->crop_current.width,
|
||||
cam->crop_current.height,
|
||||
cam->crop_current.width, IPU_ROTATE_NONE,
|
||||
cam->vf_bufs[0], cam->vf_bufs[1], 0,
|
||||
cam->offset.u_offset, cam->offset.u_offset);
|
||||
}
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "CSI_MEM output buffer\n");
|
||||
goto out_1;
|
||||
}
|
||||
err = ipu_enable_channel(cam->ipu, chan);
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR "ipu_enable_channel CSI_MEM\n");
|
||||
goto out_1;
|
||||
}
|
||||
|
||||
csi_buffer_num = 0;
|
||||
|
||||
ipu_select_buffer(cam->ipu, chan, IPU_OUTPUT_BUFFER, 0);
|
||||
ipu_select_buffer(cam->ipu, chan, IPU_OUTPUT_BUFFER, 1);
|
||||
return err;
|
||||
out_1:
|
||||
if (cam->vf_bufs_vaddr[0]) {
|
||||
dma_free_coherent(cam->dev, cam->vf_bufs_size[0],
|
||||
cam->vf_bufs_vaddr[0],
|
||||
(dma_addr_t) cam->vf_bufs[0]);
|
||||
cam->vf_bufs_vaddr[0] = NULL;
|
||||
cam->vf_bufs[0] = 0;
|
||||
}
|
||||
if (cam->vf_bufs_vaddr[1]) {
|
||||
dma_free_coherent(cam->dev, cam->vf_bufs_size[1],
|
||||
cam->vf_bufs_vaddr[1],
|
||||
(dma_addr_t) cam->vf_bufs[1]);
|
||||
cam->vf_bufs_vaddr[1] = NULL;
|
||||
cam->vf_bufs[1] = 0;
|
||||
}
|
||||
out_2:
|
||||
return err;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Enable encoder task
|
||||
* @param private struct cam_data * mxc capture instance
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
static int csi_enc_enabling_tasks(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
int err = 0;
|
||||
CAMERA_TRACE("IPU:In csi_enc_enabling_tasks\n");
|
||||
|
||||
ipu_clear_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF + cam->csi);
|
||||
err = ipu_request_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF + cam->csi,
|
||||
csi_enc_callback, 0, "Mxc Camera", cam);
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "Error registering CSI_OUT_EOF irq\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
INIT_WORK(&cam->csi_work_struct, csi_buf_work_func);
|
||||
|
||||
err = csi_enc_setup(cam);
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "csi_enc_setup %d\n", err);
|
||||
goto out1;
|
||||
}
|
||||
|
||||
return err;
|
||||
out1:
|
||||
ipu_free_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF + cam->csi, cam);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function definitions
|
||||
*/
|
||||
|
||||
/*!
|
||||
* foreground_start - start the vf task
|
||||
*
|
||||
* @param private cam_data * mxc v4l2 main structure
|
||||
*
|
||||
*/
|
||||
static int foreground_start(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
int err = 0, i = 0, screen_size;
|
||||
char *base;
|
||||
|
||||
if (!cam) {
|
||||
printk(KERN_ERR "private is NULL\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (cam->overlay_active == true) {
|
||||
pr_debug("already started.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
get_disp_ipu(cam);
|
||||
|
||||
for (i = 0; i < num_registered_fb; i++) {
|
||||
char *idstr = registered_fb[i]->fix.id;
|
||||
if (((strcmp(idstr, "DISP3 FG") == 0) && (cam->output < 3)) ||
|
||||
((strcmp(idstr, "DISP4 FG") == 0) && (cam->output >= 3))) {
|
||||
fbi = registered_fb[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fbi == NULL) {
|
||||
printk(KERN_ERR "DISP FG fb not found\n");
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
fbvar = fbi->var;
|
||||
|
||||
/* Store the overlay frame buffer's original std */
|
||||
cam->fb_origin_std = fbvar.nonstd;
|
||||
|
||||
if (cam->devtype == IMX5_V4L2 || cam->devtype == IMX6_V4L2) {
|
||||
/* Use DP to do CSC so that we can get better performance */
|
||||
vf_out_format = IPU_PIX_FMT_NV12;
|
||||
fbvar.nonstd = vf_out_format;
|
||||
} else {
|
||||
vf_out_format = IPU_PIX_FMT_RGB565;
|
||||
fbvar.nonstd = 0;
|
||||
}
|
||||
|
||||
fbvar.bits_per_pixel = 16;
|
||||
fbvar.xres = fbvar.xres_virtual = cam->win.w.width;
|
||||
fbvar.yres = cam->win.w.height;
|
||||
fbvar.yres_virtual = cam->win.w.height * 2;
|
||||
fbvar.yoffset = 0;
|
||||
fbvar.vmode &= ~FB_VMODE_YWRAP;
|
||||
fbvar.accel_flags = FB_ACCEL_DOUBLE_FLAG;
|
||||
fbvar.activate |= FB_ACTIVATE_FORCE;
|
||||
fb_set_var(fbi, &fbvar);
|
||||
|
||||
ipu_disp_set_window_pos(disp_ipu, MEM_FG_SYNC, cam->win.w.left,
|
||||
cam->win.w.top);
|
||||
|
||||
/* Fill black color for framebuffer */
|
||||
base = (char *) fbi->screen_base;
|
||||
screen_size = fbi->var.xres * fbi->var.yres;
|
||||
if (cam->devtype == IMX5_V4L2 || cam->devtype == IMX6_V4L2) {
|
||||
memset(base, 0, screen_size);
|
||||
base += screen_size;
|
||||
for (i = 0; i < screen_size / 2; i++, base++)
|
||||
*base = 0x80;
|
||||
} else {
|
||||
for (i = 0; i < screen_size * 2; i++, base++)
|
||||
*base = 0x00;
|
||||
}
|
||||
|
||||
console_lock();
|
||||
fb_blank(fbi, FB_BLANK_UNBLANK);
|
||||
console_unlock();
|
||||
|
||||
/* correct display ch buffer address */
|
||||
ipu_update_channel_buffer(disp_ipu, MEM_FG_SYNC, IPU_INPUT_BUFFER,
|
||||
0, fbi->fix.smem_start +
|
||||
(fbi->fix.line_length * fbvar.yres));
|
||||
ipu_update_channel_buffer(disp_ipu, MEM_FG_SYNC, IPU_INPUT_BUFFER,
|
||||
1, fbi->fix.smem_start);
|
||||
|
||||
err = csi_enc_enabling_tasks(cam);
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "Error csi enc enable fail\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
cam->overlay_active = true;
|
||||
return err;
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
* foreground_stop - stop the vf task
|
||||
*
|
||||
* @param private cam_data * mxc v4l2 main structure
|
||||
*
|
||||
*/
|
||||
static int foreground_stop(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
int err = 0, i = 0;
|
||||
struct fb_info *fbi = NULL;
|
||||
struct fb_var_screeninfo fbvar;
|
||||
ipu_channel_t chan = (cam->csi == 0) ? CSI_MEM0 : CSI_MEM1;
|
||||
|
||||
#ifdef CONFIG_MXC_MIPI_CSI2
|
||||
void *mipi_csi2_info;
|
||||
int ipu_id;
|
||||
int csi_id;
|
||||
#endif
|
||||
|
||||
if (cam->overlay_active == false)
|
||||
return 0;
|
||||
|
||||
err = ipu_disable_channel(cam->ipu, chan, true);
|
||||
|
||||
ipu_uninit_channel(cam->ipu, chan);
|
||||
|
||||
csi_buffer_num = 0;
|
||||
buffer_num = 0;
|
||||
|
||||
for (i = 0; i < num_registered_fb; i++) {
|
||||
char *idstr = registered_fb[i]->fix.id;
|
||||
if (((strcmp(idstr, "DISP3 FG") == 0) && (cam->output < 3)) ||
|
||||
((strcmp(idstr, "DISP4 FG") == 0) && (cam->output >= 3))) {
|
||||
fbi = registered_fb[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fbi == NULL) {
|
||||
printk(KERN_ERR "DISP FG fb not found\n");
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
console_lock();
|
||||
fb_blank(fbi, FB_BLANK_POWERDOWN);
|
||||
console_unlock();
|
||||
|
||||
/* Set the overlay frame buffer std to what it is used to be */
|
||||
fbvar = fbi->var;
|
||||
fbvar.accel_flags = FB_ACCEL_TRIPLE_FLAG;
|
||||
fbvar.nonstd = cam->fb_origin_std;
|
||||
fbvar.activate |= FB_ACTIVATE_FORCE;
|
||||
fb_set_var(fbi, &fbvar);
|
||||
|
||||
#ifdef CONFIG_MXC_MIPI_CSI2
|
||||
mipi_csi2_info = mipi_csi2_get_info();
|
||||
|
||||
if (mipi_csi2_info) {
|
||||
if (mipi_csi2_get_status(mipi_csi2_info)) {
|
||||
ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info);
|
||||
csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info);
|
||||
|
||||
if (cam->ipu == ipu_get_soc(ipu_id)
|
||||
&& cam->csi == csi_id)
|
||||
mipi_csi2_pixelclk_disable(mipi_csi2_info);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
flush_work(&cam->csi_work_struct);
|
||||
cancel_work_sync(&cam->csi_work_struct);
|
||||
|
||||
if (cam->vf_bufs_vaddr[0]) {
|
||||
dma_free_coherent(cam->dev, cam->vf_bufs_size[0],
|
||||
cam->vf_bufs_vaddr[0],
|
||||
(dma_addr_t) cam->vf_bufs[0]);
|
||||
cam->vf_bufs_vaddr[0] = NULL;
|
||||
cam->vf_bufs[0] = 0;
|
||||
}
|
||||
if (cam->vf_bufs_vaddr[1]) {
|
||||
dma_free_coherent(cam->dev, cam->vf_bufs_size[1],
|
||||
cam->vf_bufs_vaddr[1],
|
||||
(dma_addr_t) cam->vf_bufs[1]);
|
||||
cam->vf_bufs_vaddr[1] = NULL;
|
||||
cam->vf_bufs[1] = 0;
|
||||
}
|
||||
|
||||
cam->overlay_active = false;
|
||||
return err;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Enable csi
|
||||
* @param private struct cam_data * mxc capture instance
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
static int foreground_enable_csi(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
|
||||
return ipu_enable_csi(cam->ipu, cam->csi);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Disable csi
|
||||
* @param private struct cam_data * mxc capture instance
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
static int foreground_disable_csi(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
|
||||
/* free csi eof irq firstly.
|
||||
* when disable csi, wait for idmac eof.
|
||||
* it requests eof irq again */
|
||||
ipu_free_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF + cam->csi, cam);
|
||||
|
||||
return ipu_disable_csi(cam->ipu, cam->csi);
|
||||
}
|
||||
|
||||
/*!
|
||||
* function to select foreground as the working path
|
||||
*
|
||||
* @param private cam_data * mxc v4l2 main structure
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
int foreground_sdc_select(void *private)
|
||||
{
|
||||
cam_data *cam;
|
||||
int err = 0;
|
||||
if (private) {
|
||||
cam = (cam_data *) private;
|
||||
cam->vf_start_sdc = foreground_start;
|
||||
cam->vf_stop_sdc = foreground_stop;
|
||||
cam->vf_enable_csi = foreground_enable_csi;
|
||||
cam->vf_disable_csi = foreground_disable_csi;
|
||||
cam->overlay_active = false;
|
||||
} else
|
||||
err = -EIO;
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(foreground_sdc_select);
|
||||
|
||||
/*!
|
||||
* function to de-select foreground as the working path
|
||||
*
|
||||
* @param private cam_data * mxc v4l2 main structure
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int foreground_sdc_deselect(void *private)
|
||||
{
|
||||
cam_data *cam;
|
||||
|
||||
if (private) {
|
||||
cam = (cam_data *) private;
|
||||
cam->vf_start_sdc = NULL;
|
||||
cam->vf_stop_sdc = NULL;
|
||||
cam->vf_enable_csi = NULL;
|
||||
cam->vf_disable_csi = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(foreground_sdc_deselect);
|
||||
|
||||
/*!
|
||||
* Init viewfinder task.
|
||||
*
|
||||
* @return Error code indicating success or failure
|
||||
*/
|
||||
__init int foreground_sdc_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Deinit viewfinder task.
|
||||
*
|
||||
* @return Error code indicating success or failure
|
||||
*/
|
||||
void __exit foreground_sdc_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
module_init(foreground_sdc_init);
|
||||
module_exit(foreground_sdc_exit);
|
||||
|
||||
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
|
||||
MODULE_DESCRIPTION("IPU PRP VF SDC Driver");
|
||||
MODULE_LICENSE("GPL");
|
590
drivers/media/platform/mxc/capture/ipu_prp_enc.c
Normal file
590
drivers/media/platform/mxc/capture/ipu_prp_enc.c
Normal file
|
@ -0,0 +1,590 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright 2004-2015 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
* Copyright 2019 NXP
|
||||
*/
|
||||
|
||||
/*!
|
||||
* @file ipu_prp_enc.c
|
||||
*
|
||||
* @brief IPU Use case for PRP-ENC
|
||||
*
|
||||
* @ingroup IPU
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/ipu.h>
|
||||
#include <linux/mipi_csi2.h>
|
||||
#include "mxc_v4l2_capture.h"
|
||||
#include "ipu_prp_sw.h"
|
||||
|
||||
#ifdef CAMERA_DBG
|
||||
#define CAMERA_TRACE(x) (printk)x
|
||||
#else
|
||||
#define CAMERA_TRACE(x)
|
||||
#endif
|
||||
|
||||
static ipu_rotate_mode_t grotation = IPU_ROTATE_NONE;
|
||||
|
||||
/*
|
||||
* Function definitions
|
||||
*/
|
||||
|
||||
/*!
|
||||
* IPU ENC callback function.
|
||||
*
|
||||
* @param irq int irq line
|
||||
* @param dev_id void * device id
|
||||
*
|
||||
* @return status IRQ_HANDLED for handled
|
||||
*/
|
||||
static irqreturn_t prp_enc_callback(int irq, void *dev_id)
|
||||
{
|
||||
cam_data *cam = (cam_data *) dev_id;
|
||||
|
||||
if (cam->enc_callback == NULL)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
cam->enc_callback(irq, dev_id);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*!
|
||||
* PrpENC enable channel setup function
|
||||
*
|
||||
* @param cam struct cam_data * mxc capture instance
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
static int prp_enc_setup(cam_data *cam)
|
||||
{
|
||||
ipu_channel_params_t enc;
|
||||
int err = 0;
|
||||
dma_addr_t dummy = cam->dummy_frame.buffer.m.offset;
|
||||
#ifdef CONFIG_MXC_MIPI_CSI2
|
||||
void *mipi_csi2_info;
|
||||
int ipu_id;
|
||||
int csi_id;
|
||||
#endif
|
||||
|
||||
CAMERA_TRACE("In prp_enc_setup\n");
|
||||
if (!cam) {
|
||||
printk(KERN_ERR "cam private is NULL\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
memset(&enc, 0, sizeof(ipu_channel_params_t));
|
||||
|
||||
ipu_csi_get_window_size(cam->ipu, &enc.csi_prp_enc_mem.in_width,
|
||||
&enc.csi_prp_enc_mem.in_height, cam->csi);
|
||||
|
||||
enc.csi_prp_enc_mem.in_pixel_fmt = IPU_PIX_FMT_UYVY;
|
||||
enc.csi_prp_enc_mem.out_width = cam->v2f.fmt.pix.width;
|
||||
enc.csi_prp_enc_mem.out_height = cam->v2f.fmt.pix.height;
|
||||
enc.csi_prp_enc_mem.csi = cam->csi;
|
||||
if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
|
||||
enc.csi_prp_enc_mem.out_width = cam->v2f.fmt.pix.height;
|
||||
enc.csi_prp_enc_mem.out_height = cam->v2f.fmt.pix.width;
|
||||
}
|
||||
|
||||
if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) {
|
||||
enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_YUV420P;
|
||||
pr_info("YUV420\n");
|
||||
} else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YVU420) {
|
||||
enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_YVU420P;
|
||||
pr_info("YVU420\n");
|
||||
} else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P) {
|
||||
enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_YUV422P;
|
||||
pr_info("YUV422P\n");
|
||||
} else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) {
|
||||
enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_YUYV;
|
||||
pr_info("YUYV\n");
|
||||
} else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY) {
|
||||
enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_UYVY;
|
||||
pr_info("UYVY\n");
|
||||
} else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_NV12) {
|
||||
enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_NV12;
|
||||
pr_info("NV12\n");
|
||||
} else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24) {
|
||||
enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_BGR24;
|
||||
pr_info("BGR24\n");
|
||||
} else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24) {
|
||||
enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_RGB24;
|
||||
pr_info("RGB24\n");
|
||||
} else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565) {
|
||||
enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_RGB565;
|
||||
pr_info("RGB565\n");
|
||||
} else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR32) {
|
||||
enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_BGR32;
|
||||
pr_info("BGR32\n");
|
||||
} else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB32) {
|
||||
enc.csi_prp_enc_mem.out_pixel_fmt = IPU_PIX_FMT_RGB32;
|
||||
pr_info("RGB32\n");
|
||||
} else {
|
||||
printk(KERN_ERR "format not supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MXC_MIPI_CSI2
|
||||
mipi_csi2_info = mipi_csi2_get_info();
|
||||
|
||||
if (mipi_csi2_info) {
|
||||
if (mipi_csi2_get_status(mipi_csi2_info)) {
|
||||
ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info);
|
||||
csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info);
|
||||
|
||||
if (cam->ipu == ipu_get_soc(ipu_id)
|
||||
&& cam->csi == csi_id) {
|
||||
enc.csi_prp_enc_mem.mipi_en = true;
|
||||
enc.csi_prp_enc_mem.mipi_vc =
|
||||
mipi_csi2_get_virtual_channel(mipi_csi2_info);
|
||||
enc.csi_prp_enc_mem.mipi_id =
|
||||
mipi_csi2_get_datatype(mipi_csi2_info);
|
||||
|
||||
mipi_csi2_pixelclk_enable(mipi_csi2_info);
|
||||
} else {
|
||||
enc.csi_prp_enc_mem.mipi_en = false;
|
||||
enc.csi_prp_enc_mem.mipi_vc = 0;
|
||||
enc.csi_prp_enc_mem.mipi_id = 0;
|
||||
}
|
||||
} else {
|
||||
enc.csi_prp_enc_mem.mipi_en = false;
|
||||
enc.csi_prp_enc_mem.mipi_vc = 0;
|
||||
enc.csi_prp_enc_mem.mipi_id = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
err = ipu_init_channel(cam->ipu, CSI_PRP_ENC_MEM, &enc);
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "ipu_init_channel %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
grotation = cam->rotation;
|
||||
if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
|
||||
if (cam->rot_enc_bufs_vaddr[0]) {
|
||||
dma_free_coherent(cam->dev, cam->rot_enc_buf_size[0],
|
||||
cam->rot_enc_bufs_vaddr[0],
|
||||
cam->rot_enc_bufs[0]);
|
||||
}
|
||||
if (cam->rot_enc_bufs_vaddr[1]) {
|
||||
dma_free_coherent(cam->dev, cam->rot_enc_buf_size[1],
|
||||
cam->rot_enc_bufs_vaddr[1],
|
||||
cam->rot_enc_bufs[1]);
|
||||
}
|
||||
cam->rot_enc_buf_size[0] =
|
||||
PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage);
|
||||
cam->rot_enc_bufs_vaddr[0] =
|
||||
(void *)dma_alloc_coherent(cam->dev, cam->rot_enc_buf_size[0],
|
||||
&cam->rot_enc_bufs[0],
|
||||
GFP_DMA | GFP_KERNEL);
|
||||
if (!cam->rot_enc_bufs_vaddr[0]) {
|
||||
printk(KERN_ERR "alloc enc_bufs0\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
cam->rot_enc_buf_size[1] =
|
||||
PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage);
|
||||
cam->rot_enc_bufs_vaddr[1] =
|
||||
(void *)dma_alloc_coherent(cam->dev, cam->rot_enc_buf_size[1],
|
||||
&cam->rot_enc_bufs[1],
|
||||
GFP_DMA | GFP_KERNEL);
|
||||
if (!cam->rot_enc_bufs_vaddr[1]) {
|
||||
dma_free_coherent(cam->dev, cam->rot_enc_buf_size[0],
|
||||
cam->rot_enc_bufs_vaddr[0],
|
||||
cam->rot_enc_bufs[0]);
|
||||
cam->rot_enc_bufs_vaddr[0] = NULL;
|
||||
cam->rot_enc_bufs[0] = 0;
|
||||
printk(KERN_ERR "alloc enc_bufs1\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
err = ipu_init_channel_buffer(cam->ipu, CSI_PRP_ENC_MEM,
|
||||
IPU_OUTPUT_BUFFER,
|
||||
enc.csi_prp_enc_mem.out_pixel_fmt,
|
||||
enc.csi_prp_enc_mem.out_width,
|
||||
enc.csi_prp_enc_mem.out_height,
|
||||
enc.csi_prp_enc_mem.out_width,
|
||||
IPU_ROTATE_NONE,
|
||||
cam->rot_enc_bufs[0],
|
||||
cam->rot_enc_bufs[1], 0, 0, 0);
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "CSI_PRP_ENC_MEM err\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = ipu_init_channel(cam->ipu, MEM_ROT_ENC_MEM, NULL);
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "MEM_ROT_ENC_MEM channel err\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = ipu_init_channel_buffer(cam->ipu, MEM_ROT_ENC_MEM,
|
||||
IPU_INPUT_BUFFER,
|
||||
enc.csi_prp_enc_mem.out_pixel_fmt,
|
||||
enc.csi_prp_enc_mem.out_width,
|
||||
enc.csi_prp_enc_mem.out_height,
|
||||
enc.csi_prp_enc_mem.out_width,
|
||||
cam->rotation,
|
||||
cam->rot_enc_bufs[0],
|
||||
cam->rot_enc_bufs[1], 0, 0, 0);
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "MEM_ROT_ENC_MEM input buffer\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err =
|
||||
ipu_init_channel_buffer(cam->ipu, MEM_ROT_ENC_MEM,
|
||||
IPU_OUTPUT_BUFFER,
|
||||
enc.csi_prp_enc_mem.out_pixel_fmt,
|
||||
enc.csi_prp_enc_mem.out_height,
|
||||
enc.csi_prp_enc_mem.out_width,
|
||||
cam->v2f.fmt.pix.bytesperline /
|
||||
bytes_per_pixel(enc.csi_prp_enc_mem.
|
||||
out_pixel_fmt),
|
||||
IPU_ROTATE_NONE,
|
||||
dummy, dummy, 0,
|
||||
cam->offset.u_offset,
|
||||
cam->offset.v_offset);
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "MEM_ROT_ENC_MEM output buffer\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = ipu_link_channels(cam->ipu,
|
||||
CSI_PRP_ENC_MEM, MEM_ROT_ENC_MEM);
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR
|
||||
"link CSI_PRP_ENC_MEM-MEM_ROT_ENC_MEM\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = ipu_enable_channel(cam->ipu, CSI_PRP_ENC_MEM);
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR "ipu_enable_channel CSI_PRP_ENC_MEM\n");
|
||||
return err;
|
||||
}
|
||||
err = ipu_enable_channel(cam->ipu, MEM_ROT_ENC_MEM);
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR "ipu_enable_channel MEM_ROT_ENC_MEM\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
ipu_select_buffer(cam->ipu, CSI_PRP_ENC_MEM,
|
||||
IPU_OUTPUT_BUFFER, 0);
|
||||
ipu_select_buffer(cam->ipu, CSI_PRP_ENC_MEM,
|
||||
IPU_OUTPUT_BUFFER, 1);
|
||||
} else {
|
||||
err =
|
||||
ipu_init_channel_buffer(cam->ipu, CSI_PRP_ENC_MEM,
|
||||
IPU_OUTPUT_BUFFER,
|
||||
enc.csi_prp_enc_mem.out_pixel_fmt,
|
||||
enc.csi_prp_enc_mem.out_width,
|
||||
enc.csi_prp_enc_mem.out_height,
|
||||
cam->v2f.fmt.pix.bytesperline /
|
||||
bytes_per_pixel(enc.csi_prp_enc_mem.
|
||||
out_pixel_fmt),
|
||||
cam->rotation,
|
||||
dummy, dummy, 0,
|
||||
cam->offset.u_offset,
|
||||
cam->offset.v_offset);
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "CSI_PRP_ENC_MEM output buffer\n");
|
||||
return err;
|
||||
}
|
||||
err = ipu_enable_channel(cam->ipu, CSI_PRP_ENC_MEM);
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR "ipu_enable_channel CSI_PRP_ENC_MEM\n");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*!
|
||||
* function to update physical buffer address for encorder IDMA channel
|
||||
*
|
||||
* @param private pointer to cam_data structure
|
||||
* @param eba physical buffer address for encorder IDMA channel
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
static int prp_enc_eba_update(void *private, dma_addr_t eba)
|
||||
{
|
||||
int err = 0;
|
||||
cam_data *cam = (cam_data *) private;
|
||||
struct ipu_soc *ipu = cam->ipu;
|
||||
int *buffer_num = &cam->ping_pong_csi;
|
||||
|
||||
pr_debug("eba %x\n", eba);
|
||||
if (grotation >= IPU_ROTATE_90_RIGHT) {
|
||||
err = ipu_update_channel_buffer(ipu, MEM_ROT_ENC_MEM,
|
||||
IPU_OUTPUT_BUFFER, *buffer_num,
|
||||
eba);
|
||||
} else {
|
||||
err = ipu_update_channel_buffer(ipu, CSI_PRP_ENC_MEM,
|
||||
IPU_OUTPUT_BUFFER, *buffer_num,
|
||||
eba);
|
||||
}
|
||||
if (err != 0) {
|
||||
if (grotation >= IPU_ROTATE_90_RIGHT) {
|
||||
ipu_clear_buffer_ready(ipu, MEM_ROT_ENC_MEM,
|
||||
IPU_OUTPUT_BUFFER,
|
||||
*buffer_num);
|
||||
err = ipu_update_channel_buffer(ipu, MEM_ROT_ENC_MEM,
|
||||
IPU_OUTPUT_BUFFER,
|
||||
*buffer_num,
|
||||
eba);
|
||||
} else {
|
||||
ipu_clear_buffer_ready(ipu, CSI_PRP_ENC_MEM,
|
||||
IPU_OUTPUT_BUFFER,
|
||||
*buffer_num);
|
||||
err = ipu_update_channel_buffer(ipu, CSI_PRP_ENC_MEM,
|
||||
IPU_OUTPUT_BUFFER,
|
||||
*buffer_num,
|
||||
eba);
|
||||
}
|
||||
|
||||
if (err != 0) {
|
||||
pr_err("ERROR: v4l2 capture: fail to update "
|
||||
"buf%d\n", *buffer_num);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (grotation >= IPU_ROTATE_90_RIGHT) {
|
||||
ipu_select_buffer(ipu, MEM_ROT_ENC_MEM, IPU_OUTPUT_BUFFER,
|
||||
*buffer_num);
|
||||
} else {
|
||||
ipu_select_buffer(ipu, CSI_PRP_ENC_MEM, IPU_OUTPUT_BUFFER,
|
||||
*buffer_num);
|
||||
}
|
||||
|
||||
*buffer_num = (*buffer_num == 0) ? 1 : 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Enable encoder task
|
||||
* @param private struct cam_data * mxc capture instance
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
static int prp_enc_enabling_tasks(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
int err = 0;
|
||||
CAMERA_TRACE("IPU:In prp_enc_enabling_tasks\n");
|
||||
|
||||
cam->dummy_frame.vaddress = dma_alloc_coherent(cam->dev,
|
||||
PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage),
|
||||
&cam->dummy_frame.paddress,
|
||||
GFP_DMA | GFP_KERNEL);
|
||||
if (cam->dummy_frame.vaddress == 0) {
|
||||
pr_err("ERROR: v4l2 capture: Allocate dummy frame "
|
||||
"failed.\n");
|
||||
return -ENOBUFS;
|
||||
}
|
||||
cam->dummy_frame.buffer.type = V4L2_BUF_TYPE_PRIVATE;
|
||||
cam->dummy_frame.buffer.length =
|
||||
PAGE_ALIGN(cam->v2f.fmt.pix.sizeimage);
|
||||
cam->dummy_frame.buffer.m.offset = cam->dummy_frame.paddress;
|
||||
|
||||
if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
|
||||
err = ipu_request_irq(cam->ipu, IPU_IRQ_PRP_ENC_ROT_OUT_EOF,
|
||||
prp_enc_callback, 0, "Mxc Camera", cam);
|
||||
} else {
|
||||
err = ipu_request_irq(cam->ipu, IPU_IRQ_PRP_ENC_OUT_EOF,
|
||||
prp_enc_callback, 0, "Mxc Camera", cam);
|
||||
}
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "Error registering rot irq\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = prp_enc_setup(cam);
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "prp_enc_setup %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Disable encoder task
|
||||
* @param private struct cam_data * mxc capture instance
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
static int prp_enc_disabling_tasks(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
int err = 0;
|
||||
#ifdef CONFIG_MXC_MIPI_CSI2
|
||||
void *mipi_csi2_info;
|
||||
int ipu_id;
|
||||
int csi_id;
|
||||
#endif
|
||||
|
||||
if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
|
||||
ipu_free_irq(cam->ipu, IPU_IRQ_PRP_ENC_ROT_OUT_EOF, cam);
|
||||
ipu_unlink_channels(cam->ipu, CSI_PRP_ENC_MEM, MEM_ROT_ENC_MEM);
|
||||
}
|
||||
|
||||
err = ipu_disable_channel(cam->ipu, CSI_PRP_ENC_MEM, true);
|
||||
if (cam->rotation >= IPU_ROTATE_90_RIGHT)
|
||||
err |= ipu_disable_channel(cam->ipu, MEM_ROT_ENC_MEM, true);
|
||||
|
||||
ipu_uninit_channel(cam->ipu, CSI_PRP_ENC_MEM);
|
||||
if (cam->rotation >= IPU_ROTATE_90_RIGHT)
|
||||
ipu_uninit_channel(cam->ipu, MEM_ROT_ENC_MEM);
|
||||
|
||||
if (cam->dummy_frame.vaddress != 0) {
|
||||
dma_free_coherent(cam->dev, cam->dummy_frame.buffer.length,
|
||||
cam->dummy_frame.vaddress,
|
||||
cam->dummy_frame.paddress);
|
||||
cam->dummy_frame.vaddress = 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MXC_MIPI_CSI2
|
||||
mipi_csi2_info = mipi_csi2_get_info();
|
||||
|
||||
if (mipi_csi2_info) {
|
||||
if (mipi_csi2_get_status(mipi_csi2_info)) {
|
||||
ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info);
|
||||
csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info);
|
||||
|
||||
if (cam->ipu == ipu_get_soc(ipu_id)
|
||||
&& cam->csi == csi_id)
|
||||
mipi_csi2_pixelclk_disable(mipi_csi2_info);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Enable csi
|
||||
* @param private struct cam_data * mxc capture instance
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
static int prp_enc_enable_csi(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
|
||||
return ipu_enable_csi(cam->ipu, cam->csi);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Disable csi
|
||||
* @param private struct cam_data * mxc capture instance
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
static int prp_enc_disable_csi(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
|
||||
/* free csi eof irq firstly.
|
||||
* when disable csi, wait for idmac eof.
|
||||
* it requests eof irq again */
|
||||
if (cam->rotation < IPU_ROTATE_90_RIGHT)
|
||||
ipu_free_irq(cam->ipu, IPU_IRQ_PRP_ENC_OUT_EOF, cam);
|
||||
|
||||
return ipu_disable_csi(cam->ipu, cam->csi);
|
||||
}
|
||||
|
||||
/*!
|
||||
* function to select PRP-ENC as the working path
|
||||
*
|
||||
* @param private struct cam_data * mxc capture instance
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int prp_enc_select(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
int err = 0;
|
||||
|
||||
if (cam) {
|
||||
cam->enc_update_eba = prp_enc_eba_update;
|
||||
cam->enc_enable = prp_enc_enabling_tasks;
|
||||
cam->enc_disable = prp_enc_disabling_tasks;
|
||||
cam->enc_enable_csi = prp_enc_enable_csi;
|
||||
cam->enc_disable_csi = prp_enc_disable_csi;
|
||||
} else {
|
||||
err = -EIO;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(prp_enc_select);
|
||||
|
||||
/*!
|
||||
* function to de-select PRP-ENC as the working path
|
||||
*
|
||||
* @param private struct cam_data * mxc capture instance
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int prp_enc_deselect(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
int err = 0;
|
||||
|
||||
if (cam) {
|
||||
cam->enc_update_eba = NULL;
|
||||
cam->enc_enable = NULL;
|
||||
cam->enc_disable = NULL;
|
||||
cam->enc_enable_csi = NULL;
|
||||
cam->enc_disable_csi = NULL;
|
||||
if (cam->rot_enc_bufs_vaddr[0]) {
|
||||
dma_free_coherent(cam->dev, cam->rot_enc_buf_size[0],
|
||||
cam->rot_enc_bufs_vaddr[0],
|
||||
cam->rot_enc_bufs[0]);
|
||||
cam->rot_enc_bufs_vaddr[0] = NULL;
|
||||
cam->rot_enc_bufs[0] = 0;
|
||||
}
|
||||
if (cam->rot_enc_bufs_vaddr[1]) {
|
||||
dma_free_coherent(cam->dev, cam->rot_enc_buf_size[1],
|
||||
cam->rot_enc_bufs_vaddr[1],
|
||||
cam->rot_enc_bufs[1]);
|
||||
cam->rot_enc_bufs_vaddr[1] = NULL;
|
||||
cam->rot_enc_bufs[1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(prp_enc_deselect);
|
||||
|
||||
/*!
|
||||
* Init the Encorder channels
|
||||
*
|
||||
* @return Error code indicating success or failure
|
||||
*/
|
||||
__init int prp_enc_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Deinit the Encorder channels
|
||||
*
|
||||
*/
|
||||
void __exit prp_enc_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
module_init(prp_enc_init);
|
||||
module_exit(prp_enc_exit);
|
||||
|
||||
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
|
||||
MODULE_DESCRIPTION("IPU PRP ENC Driver");
|
||||
MODULE_LICENSE("GPL");
|
36
drivers/media/platform/mxc/capture/ipu_prp_sw.h
Normal file
36
drivers/media/platform/mxc/capture/ipu_prp_sw.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright 2004-2014 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
* Copyright 2019 NXP
|
||||
*/
|
||||
|
||||
/*!
|
||||
* @file ipu_prp_sw.h
|
||||
*
|
||||
* @brief This file contains the IPU PRP use case driver header.
|
||||
*
|
||||
* @ingroup IPU
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_IPU__PRP_SW_H_
|
||||
#define _INCLUDE_IPU__PRP_SW_H_
|
||||
|
||||
int csi_enc_select(void *private);
|
||||
int csi_enc_deselect(void *private);
|
||||
int prp_enc_select(void *private);
|
||||
int prp_enc_deselect(void *private);
|
||||
#ifdef CONFIG_MXC_IPU_PRP_VF_SDC
|
||||
int prp_vf_sdc_select(void *private);
|
||||
int prp_vf_sdc_deselect(void *private);
|
||||
int prp_vf_sdc_select_bg(void *private);
|
||||
int prp_vf_sdc_deselect_bg(void *private);
|
||||
#else
|
||||
int foreground_sdc_select(void *private);
|
||||
int foreground_sdc_deselect(void *private);
|
||||
int bg_overlay_sdc_select(void *private);
|
||||
int bg_overlay_sdc_deselect(void *private);
|
||||
#endif
|
||||
int prp_still_select(void *private);
|
||||
int prp_still_deselect(void *private);
|
||||
|
||||
#endif
|
576
drivers/media/platform/mxc/capture/ipu_prp_vf_sdc.c
Normal file
576
drivers/media/platform/mxc/capture/ipu_prp_vf_sdc.c
Normal file
|
@ -0,0 +1,576 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright 2004-2014 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* @file ipu_prp_vf_sdc.c
|
||||
*
|
||||
* @brief IPU Use case for PRP-VF
|
||||
*
|
||||
* @ingroup IPU
|
||||
*/
|
||||
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/ipu.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mxcfb.h>
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/mipi_csi2.h>
|
||||
#include "mxc_v4l2_capture.h"
|
||||
#include "ipu_prp_sw.h"
|
||||
|
||||
static int buffer_num;
|
||||
static struct ipu_soc *disp_ipu;
|
||||
|
||||
static void get_disp_ipu(cam_data *cam)
|
||||
{
|
||||
if (cam->output > 2)
|
||||
disp_ipu = ipu_get_soc(1); /* using DISP4 */
|
||||
else
|
||||
disp_ipu = ipu_get_soc(0);
|
||||
}
|
||||
|
||||
static irqreturn_t prpvf_rot_eof_callback(int irq, void *dev_id)
|
||||
{
|
||||
cam_data *cam = dev_id;
|
||||
pr_debug("buffer_num %d\n", buffer_num);
|
||||
|
||||
if (cam->vf_rotation >= IPU_ROTATE_VERT_FLIP) {
|
||||
ipu_select_buffer(disp_ipu, MEM_FG_SYNC,
|
||||
IPU_INPUT_BUFFER, buffer_num);
|
||||
buffer_num = (buffer_num == 0) ? 1 : 0;
|
||||
ipu_select_buffer(cam->ipu, MEM_ROT_VF_MEM,
|
||||
IPU_OUTPUT_BUFFER, buffer_num);
|
||||
} else {
|
||||
ipu_select_buffer(disp_ipu, MEM_FG_SYNC,
|
||||
IPU_INPUT_BUFFER, buffer_num);
|
||||
buffer_num = (buffer_num == 0) ? 1 : 0;
|
||||
ipu_select_buffer(cam->ipu, CSI_PRP_VF_MEM,
|
||||
IPU_OUTPUT_BUFFER, buffer_num);
|
||||
}
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
/*
|
||||
* Function definitions
|
||||
*/
|
||||
|
||||
/*!
|
||||
* prpvf_start - start the vf task
|
||||
*
|
||||
* @param private cam_data * mxc v4l2 main structure
|
||||
*
|
||||
*/
|
||||
static int prpvf_start(void *private)
|
||||
{
|
||||
struct fb_var_screeninfo fbvar;
|
||||
struct fb_info *fbi = NULL;
|
||||
cam_data *cam = (cam_data *) private;
|
||||
ipu_channel_params_t vf;
|
||||
u32 vf_out_format = 0;
|
||||
u32 size = 2, temp = 0;
|
||||
int err = 0, i = 0;
|
||||
short *tmp, color;
|
||||
#ifdef CONFIG_MXC_MIPI_CSI2
|
||||
void *mipi_csi2_info;
|
||||
int ipu_id;
|
||||
int csi_id;
|
||||
#endif
|
||||
|
||||
if (!cam) {
|
||||
printk(KERN_ERR "private is NULL\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (cam->overlay_active == true) {
|
||||
pr_debug("already started.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
get_disp_ipu(cam);
|
||||
|
||||
for (i = 0; i < num_registered_fb; i++) {
|
||||
char *idstr = registered_fb[i]->fix.id;
|
||||
if (((strcmp(idstr, "DISP3 FG") == 0) && (cam->output < 3)) ||
|
||||
((strcmp(idstr, "DISP4 FG") == 0) && (cam->output >= 3))) {
|
||||
fbi = registered_fb[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fbi == NULL) {
|
||||
printk(KERN_ERR "DISP FG fb not found\n");
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
fbvar = fbi->var;
|
||||
|
||||
/* Store the overlay frame buffer's original std */
|
||||
cam->fb_origin_std = fbvar.nonstd;
|
||||
|
||||
if (cam->devtype == IMX5_V4L2 || cam->devtype == IMX6_V4L2) {
|
||||
/* Use DP to do CSC so that we can get better performance */
|
||||
vf_out_format = IPU_PIX_FMT_UYVY;
|
||||
fbvar.nonstd = vf_out_format;
|
||||
color = 0x80;
|
||||
} else {
|
||||
vf_out_format = IPU_PIX_FMT_RGB565;
|
||||
fbvar.nonstd = 0;
|
||||
color = 0x0;
|
||||
}
|
||||
|
||||
fbvar.bits_per_pixel = 16;
|
||||
fbvar.xres = fbvar.xres_virtual = cam->win.w.width;
|
||||
fbvar.yres = cam->win.w.height;
|
||||
fbvar.yres_virtual = cam->win.w.height * 2;
|
||||
fbvar.yoffset = 0;
|
||||
fbvar.accel_flags = FB_ACCEL_DOUBLE_FLAG;
|
||||
fbvar.activate |= FB_ACTIVATE_FORCE;
|
||||
fb_set_var(fbi, &fbvar);
|
||||
|
||||
ipu_disp_set_window_pos(disp_ipu, MEM_FG_SYNC, cam->win.w.left,
|
||||
cam->win.w.top);
|
||||
|
||||
/* Fill black color for framebuffer */
|
||||
tmp = (short *) fbi->screen_base;
|
||||
for (i = 0; i < (fbi->fix.line_length * fbi->var.yres)/2;
|
||||
i++, tmp++)
|
||||
*tmp = color;
|
||||
|
||||
console_lock();
|
||||
fb_blank(fbi, FB_BLANK_UNBLANK);
|
||||
console_unlock();
|
||||
|
||||
/* correct display ch buffer address */
|
||||
ipu_update_channel_buffer(disp_ipu, MEM_FG_SYNC, IPU_INPUT_BUFFER,
|
||||
0, fbi->fix.smem_start +
|
||||
(fbi->fix.line_length * fbvar.yres));
|
||||
ipu_update_channel_buffer(disp_ipu, MEM_FG_SYNC, IPU_INPUT_BUFFER,
|
||||
1, fbi->fix.smem_start);
|
||||
|
||||
memset(&vf, 0, sizeof(ipu_channel_params_t));
|
||||
ipu_csi_get_window_size(cam->ipu, &vf.csi_prp_vf_mem.in_width,
|
||||
&vf.csi_prp_vf_mem.in_height, cam->csi);
|
||||
vf.csi_prp_vf_mem.in_pixel_fmt = IPU_PIX_FMT_UYVY;
|
||||
vf.csi_prp_vf_mem.out_width = cam->win.w.width;
|
||||
vf.csi_prp_vf_mem.out_height = cam->win.w.height;
|
||||
vf.csi_prp_vf_mem.csi = cam->csi;
|
||||
if (cam->vf_rotation >= IPU_ROTATE_90_RIGHT) {
|
||||
vf.csi_prp_vf_mem.out_width = cam->win.w.height;
|
||||
vf.csi_prp_vf_mem.out_height = cam->win.w.width;
|
||||
}
|
||||
vf.csi_prp_vf_mem.out_pixel_fmt = vf_out_format;
|
||||
size = cam->win.w.width * cam->win.w.height * size;
|
||||
|
||||
#ifdef CONFIG_MXC_MIPI_CSI2
|
||||
mipi_csi2_info = mipi_csi2_get_info();
|
||||
|
||||
if (mipi_csi2_info) {
|
||||
if (mipi_csi2_get_status(mipi_csi2_info)) {
|
||||
ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info);
|
||||
csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info);
|
||||
|
||||
if (cam->ipu == ipu_get_soc(ipu_id)
|
||||
&& cam->csi == csi_id) {
|
||||
vf.csi_prp_vf_mem.mipi_en = true;
|
||||
vf.csi_prp_vf_mem.mipi_vc =
|
||||
mipi_csi2_get_virtual_channel(mipi_csi2_info);
|
||||
vf.csi_prp_vf_mem.mipi_id =
|
||||
mipi_csi2_get_datatype(mipi_csi2_info);
|
||||
|
||||
mipi_csi2_pixelclk_enable(mipi_csi2_info);
|
||||
} else {
|
||||
vf.csi_prp_vf_mem.mipi_en = false;
|
||||
vf.csi_prp_vf_mem.mipi_vc = 0;
|
||||
vf.csi_prp_vf_mem.mipi_id = 0;
|
||||
}
|
||||
} else {
|
||||
vf.csi_prp_vf_mem.mipi_en = false;
|
||||
vf.csi_prp_vf_mem.mipi_vc = 0;
|
||||
vf.csi_prp_vf_mem.mipi_id = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
err = ipu_init_channel(cam->ipu, CSI_PRP_VF_MEM, &vf);
|
||||
if (err != 0)
|
||||
goto out_5;
|
||||
|
||||
if (cam->vf_bufs_vaddr[0]) {
|
||||
dma_free_coherent(cam->dev, cam->vf_bufs_size[0],
|
||||
cam->vf_bufs_vaddr[0],
|
||||
(dma_addr_t) cam->vf_bufs[0]);
|
||||
}
|
||||
if (cam->vf_bufs_vaddr[1]) {
|
||||
dma_free_coherent(cam->dev, cam->vf_bufs_size[1],
|
||||
cam->vf_bufs_vaddr[1],
|
||||
(dma_addr_t) cam->vf_bufs[1]);
|
||||
}
|
||||
cam->vf_bufs_size[0] = PAGE_ALIGN(size);
|
||||
cam->vf_bufs_vaddr[0] = (void *)dma_alloc_coherent(cam->dev,
|
||||
cam->vf_bufs_size[0],
|
||||
(dma_addr_t *) &
|
||||
cam->vf_bufs[0],
|
||||
GFP_DMA |
|
||||
GFP_KERNEL);
|
||||
if (cam->vf_bufs_vaddr[0] == NULL) {
|
||||
printk(KERN_ERR "Error to allocate vf buffer\n");
|
||||
err = -ENOMEM;
|
||||
goto out_4;
|
||||
}
|
||||
cam->vf_bufs_size[1] = PAGE_ALIGN(size);
|
||||
cam->vf_bufs_vaddr[1] = (void *)dma_alloc_coherent(cam->dev,
|
||||
cam->vf_bufs_size[1],
|
||||
(dma_addr_t *) &
|
||||
cam->vf_bufs[1],
|
||||
GFP_DMA |
|
||||
GFP_KERNEL);
|
||||
if (cam->vf_bufs_vaddr[1] == NULL) {
|
||||
printk(KERN_ERR "Error to allocate vf buffer\n");
|
||||
err = -ENOMEM;
|
||||
goto out_3;
|
||||
}
|
||||
pr_debug("vf_bufs %x %x\n", cam->vf_bufs[0], cam->vf_bufs[1]);
|
||||
|
||||
if (cam->vf_rotation >= IPU_ROTATE_VERT_FLIP) {
|
||||
err = ipu_init_channel_buffer(cam->ipu, CSI_PRP_VF_MEM,
|
||||
IPU_OUTPUT_BUFFER,
|
||||
vf_out_format,
|
||||
vf.csi_prp_vf_mem.out_width,
|
||||
vf.csi_prp_vf_mem.out_height,
|
||||
vf.csi_prp_vf_mem.out_width,
|
||||
IPU_ROTATE_NONE,
|
||||
cam->vf_bufs[0], cam->vf_bufs[1],
|
||||
0, 0, 0);
|
||||
if (err != 0)
|
||||
goto out_3;
|
||||
|
||||
err = ipu_init_channel(cam->ipu, MEM_ROT_VF_MEM, NULL);
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "Error MEM_ROT_VF_MEM channel\n");
|
||||
goto out_3;
|
||||
}
|
||||
|
||||
err = ipu_init_channel_buffer(cam->ipu, MEM_ROT_VF_MEM,
|
||||
IPU_INPUT_BUFFER,
|
||||
vf_out_format,
|
||||
vf.csi_prp_vf_mem.out_width,
|
||||
vf.csi_prp_vf_mem.out_height,
|
||||
vf.csi_prp_vf_mem.out_width,
|
||||
cam->vf_rotation,
|
||||
cam->vf_bufs[0],
|
||||
cam->vf_bufs[1],
|
||||
0, 0, 0);
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "Error MEM_ROT_VF_MEM input buffer\n");
|
||||
goto out_2;
|
||||
}
|
||||
|
||||
if (cam->vf_rotation < IPU_ROTATE_90_RIGHT) {
|
||||
temp = vf.csi_prp_vf_mem.out_width;
|
||||
vf.csi_prp_vf_mem.out_width =
|
||||
vf.csi_prp_vf_mem.out_height;
|
||||
vf.csi_prp_vf_mem.out_height = temp;
|
||||
}
|
||||
|
||||
err = ipu_init_channel_buffer(cam->ipu, MEM_ROT_VF_MEM,
|
||||
IPU_OUTPUT_BUFFER,
|
||||
vf_out_format,
|
||||
vf.csi_prp_vf_mem.out_height,
|
||||
vf.csi_prp_vf_mem.out_width,
|
||||
vf.csi_prp_vf_mem.out_height,
|
||||
IPU_ROTATE_NONE,
|
||||
fbi->fix.smem_start +
|
||||
(fbi->fix.line_length *
|
||||
fbi->var.yres),
|
||||
fbi->fix.smem_start, 0, 0, 0);
|
||||
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "Error MEM_ROT_VF_MEM output buffer\n");
|
||||
goto out_2;
|
||||
}
|
||||
|
||||
ipu_clear_irq(cam->ipu, IPU_IRQ_PRP_VF_ROT_OUT_EOF);
|
||||
err = ipu_request_irq(cam->ipu, IPU_IRQ_PRP_VF_ROT_OUT_EOF,
|
||||
prpvf_rot_eof_callback,
|
||||
0, "Mxc Camera", cam);
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR "Error request irq:IPU_IRQ_PRP_VF_ROT_OUT_EOF\n");
|
||||
goto out_2;
|
||||
}
|
||||
|
||||
err = ipu_link_channels(cam->ipu,
|
||||
CSI_PRP_VF_MEM, MEM_ROT_VF_MEM);
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR
|
||||
"Error link CSI_PRP_VF_MEM-MEM_ROT_VF_MEM\n");
|
||||
goto out_1;
|
||||
}
|
||||
|
||||
ipu_enable_channel(cam->ipu, CSI_PRP_VF_MEM);
|
||||
ipu_enable_channel(cam->ipu, MEM_ROT_VF_MEM);
|
||||
|
||||
ipu_select_buffer(cam->ipu, CSI_PRP_VF_MEM,
|
||||
IPU_OUTPUT_BUFFER, 0);
|
||||
ipu_select_buffer(cam->ipu, CSI_PRP_VF_MEM,
|
||||
IPU_OUTPUT_BUFFER, 1);
|
||||
ipu_select_buffer(cam->ipu, MEM_ROT_VF_MEM,
|
||||
IPU_OUTPUT_BUFFER, 0);
|
||||
} else {
|
||||
err = ipu_init_channel_buffer(cam->ipu, CSI_PRP_VF_MEM,
|
||||
IPU_OUTPUT_BUFFER,
|
||||
vf_out_format, cam->win.w.width,
|
||||
cam->win.w.height,
|
||||
cam->win.w.width,
|
||||
cam->vf_rotation,
|
||||
fbi->fix.smem_start +
|
||||
(fbi->fix.line_length *
|
||||
fbi->var.yres),
|
||||
fbi->fix.smem_start, 0, 0, 0);
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "Error initializing CSI_PRP_VF_MEM\n");
|
||||
goto out_4;
|
||||
}
|
||||
ipu_clear_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF);
|
||||
err = ipu_request_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF,
|
||||
prpvf_rot_eof_callback,
|
||||
0, "Mxc Camera", cam);
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR "Error request irq:IPU_IRQ_PRP_VF_OUT_EOF\n");
|
||||
goto out_4;
|
||||
}
|
||||
|
||||
ipu_enable_channel(cam->ipu, CSI_PRP_VF_MEM);
|
||||
|
||||
ipu_select_buffer(cam->ipu, CSI_PRP_VF_MEM,
|
||||
IPU_OUTPUT_BUFFER, 0);
|
||||
}
|
||||
|
||||
cam->overlay_active = true;
|
||||
return err;
|
||||
|
||||
out_1:
|
||||
ipu_free_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF, NULL);
|
||||
out_2:
|
||||
if (cam->vf_rotation >= IPU_ROTATE_VERT_FLIP)
|
||||
ipu_uninit_channel(cam->ipu, MEM_ROT_VF_MEM);
|
||||
out_3:
|
||||
if (cam->vf_bufs_vaddr[0]) {
|
||||
dma_free_coherent(cam->dev, cam->vf_bufs_size[0],
|
||||
cam->vf_bufs_vaddr[0],
|
||||
(dma_addr_t) cam->vf_bufs[0]);
|
||||
cam->vf_bufs_vaddr[0] = NULL;
|
||||
cam->vf_bufs[0] = 0;
|
||||
}
|
||||
if (cam->vf_bufs_vaddr[1]) {
|
||||
dma_free_coherent(cam->dev, cam->vf_bufs_size[1],
|
||||
cam->vf_bufs_vaddr[1],
|
||||
(dma_addr_t) cam->vf_bufs[1]);
|
||||
cam->vf_bufs_vaddr[1] = NULL;
|
||||
cam->vf_bufs[1] = 0;
|
||||
}
|
||||
out_4:
|
||||
ipu_uninit_channel(cam->ipu, CSI_PRP_VF_MEM);
|
||||
out_5:
|
||||
return err;
|
||||
}
|
||||
|
||||
/*!
|
||||
* prpvf_stop - stop the vf task
|
||||
*
|
||||
* @param private cam_data * mxc v4l2 main structure
|
||||
*
|
||||
*/
|
||||
static int prpvf_stop(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
int err = 0, i = 0;
|
||||
struct fb_info *fbi = NULL;
|
||||
struct fb_var_screeninfo fbvar;
|
||||
#ifdef CONFIG_MXC_MIPI_CSI2
|
||||
void *mipi_csi2_info;
|
||||
int ipu_id;
|
||||
int csi_id;
|
||||
#endif
|
||||
|
||||
if (cam->overlay_active == false)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < num_registered_fb; i++) {
|
||||
char *idstr = registered_fb[i]->fix.id;
|
||||
if (((strcmp(idstr, "DISP3 FG") == 0) && (cam->output < 3)) ||
|
||||
((strcmp(idstr, "DISP4 FG") == 0) && (cam->output >= 3))) {
|
||||
fbi = registered_fb[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fbi == NULL) {
|
||||
printk(KERN_ERR "DISP FG fb not found\n");
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
if (cam->vf_rotation >= IPU_ROTATE_VERT_FLIP) {
|
||||
ipu_unlink_channels(cam->ipu, CSI_PRP_VF_MEM, MEM_ROT_VF_MEM);
|
||||
ipu_free_irq(cam->ipu, IPU_IRQ_PRP_VF_ROT_OUT_EOF, cam);
|
||||
}
|
||||
buffer_num = 0;
|
||||
|
||||
ipu_disable_channel(cam->ipu, CSI_PRP_VF_MEM, true);
|
||||
|
||||
if (cam->vf_rotation >= IPU_ROTATE_VERT_FLIP) {
|
||||
ipu_disable_channel(cam->ipu, MEM_ROT_VF_MEM, true);
|
||||
ipu_uninit_channel(cam->ipu, MEM_ROT_VF_MEM);
|
||||
}
|
||||
ipu_uninit_channel(cam->ipu, CSI_PRP_VF_MEM);
|
||||
|
||||
console_lock();
|
||||
fb_blank(fbi, FB_BLANK_POWERDOWN);
|
||||
console_unlock();
|
||||
|
||||
/* Set the overlay frame buffer std to what it is used to be */
|
||||
fbvar = fbi->var;
|
||||
fbvar.accel_flags = FB_ACCEL_TRIPLE_FLAG;
|
||||
fbvar.nonstd = cam->fb_origin_std;
|
||||
fbvar.activate |= FB_ACTIVATE_FORCE;
|
||||
fb_set_var(fbi, &fbvar);
|
||||
|
||||
#ifdef CONFIG_MXC_MIPI_CSI2
|
||||
mipi_csi2_info = mipi_csi2_get_info();
|
||||
|
||||
if (mipi_csi2_info) {
|
||||
if (mipi_csi2_get_status(mipi_csi2_info)) {
|
||||
ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info);
|
||||
csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info);
|
||||
|
||||
if (cam->ipu == ipu_get_soc(ipu_id)
|
||||
&& cam->csi == csi_id)
|
||||
mipi_csi2_pixelclk_disable(mipi_csi2_info);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (cam->vf_bufs_vaddr[0]) {
|
||||
dma_free_coherent(cam->dev, cam->vf_bufs_size[0],
|
||||
cam->vf_bufs_vaddr[0],
|
||||
(dma_addr_t) cam->vf_bufs[0]);
|
||||
cam->vf_bufs_vaddr[0] = NULL;
|
||||
cam->vf_bufs[0] = 0;
|
||||
}
|
||||
if (cam->vf_bufs_vaddr[1]) {
|
||||
dma_free_coherent(cam->dev, cam->vf_bufs_size[1],
|
||||
cam->vf_bufs_vaddr[1],
|
||||
(dma_addr_t) cam->vf_bufs[1]);
|
||||
cam->vf_bufs_vaddr[1] = NULL;
|
||||
cam->vf_bufs[1] = 0;
|
||||
}
|
||||
|
||||
cam->overlay_active = false;
|
||||
return err;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Enable csi
|
||||
* @param private struct cam_data * mxc capture instance
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
static int prp_vf_enable_csi(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
|
||||
return ipu_enable_csi(cam->ipu, cam->csi);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Disable csi
|
||||
* @param private struct cam_data * mxc capture instance
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
static int prp_vf_disable_csi(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
|
||||
/* free csi eof irq firstly.
|
||||
* when disable csi, wait for idmac eof.
|
||||
* it requests eof irq again */
|
||||
if (cam->vf_rotation < IPU_ROTATE_VERT_FLIP)
|
||||
ipu_free_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF, cam);
|
||||
|
||||
return ipu_disable_csi(cam->ipu, cam->csi);
|
||||
}
|
||||
|
||||
/*!
|
||||
* function to select PRP-VF as the working path
|
||||
*
|
||||
* @param private cam_data * mxc v4l2 main structure
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
int prp_vf_sdc_select(void *private)
|
||||
{
|
||||
cam_data *cam;
|
||||
int err = 0;
|
||||
if (private) {
|
||||
cam = (cam_data *) private;
|
||||
cam->vf_start_sdc = prpvf_start;
|
||||
cam->vf_stop_sdc = prpvf_stop;
|
||||
cam->vf_enable_csi = prp_vf_enable_csi;
|
||||
cam->vf_disable_csi = prp_vf_disable_csi;
|
||||
cam->overlay_active = false;
|
||||
} else
|
||||
err = -EIO;
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(prp_vf_sdc_select);
|
||||
|
||||
/*!
|
||||
* function to de-select PRP-VF as the working path
|
||||
*
|
||||
* @param private cam_data * mxc v4l2 main structure
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int prp_vf_sdc_deselect(void *private)
|
||||
{
|
||||
cam_data *cam;
|
||||
|
||||
if (private) {
|
||||
cam = (cam_data *) private;
|
||||
cam->vf_start_sdc = NULL;
|
||||
cam->vf_stop_sdc = NULL;
|
||||
cam->vf_enable_csi = NULL;
|
||||
cam->vf_disable_csi = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(prp_vf_sdc_deselect);
|
||||
|
||||
/*!
|
||||
* Init viewfinder task.
|
||||
*
|
||||
* @return Error code indicating success or failure
|
||||
*/
|
||||
__init int prp_vf_sdc_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Deinit viewfinder task.
|
||||
*
|
||||
* @return Error code indicating success or failure
|
||||
*/
|
||||
void __exit prp_vf_sdc_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
module_init(prp_vf_sdc_init);
|
||||
module_exit(prp_vf_sdc_exit);
|
||||
|
||||
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
|
||||
MODULE_DESCRIPTION("IPU PRP VF SDC Driver");
|
||||
MODULE_LICENSE("GPL");
|
514
drivers/media/platform/mxc/capture/ipu_prp_vf_sdc_bg.c
Normal file
514
drivers/media/platform/mxc/capture/ipu_prp_vf_sdc_bg.c
Normal file
|
@ -0,0 +1,514 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright 2004-2014 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
* Copyright 2019 NXP
|
||||
*/
|
||||
|
||||
/*!
|
||||
* @file ipu_prp_vf_sdc_bg.c
|
||||
*
|
||||
* @brief IPU Use case for PRP-VF back-ground
|
||||
*
|
||||
* @ingroup IPU
|
||||
*/
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/ipu.h>
|
||||
#include <linux/module.h>
|
||||
#include <mach/mipi_csi2.h>
|
||||
#include "mxc_v4l2_capture.h"
|
||||
#include "ipu_prp_sw.h"
|
||||
|
||||
static int buffer_num;
|
||||
static int buffer_ready;
|
||||
static struct ipu_soc *disp_ipu;
|
||||
|
||||
static void get_disp_ipu(cam_data *cam)
|
||||
{
|
||||
if (cam->output > 2)
|
||||
disp_ipu = ipu_get_soc(1); /* using DISP4 */
|
||||
else
|
||||
disp_ipu = ipu_get_soc(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function definitions
|
||||
*/
|
||||
|
||||
/*!
|
||||
* SDC V-Sync callback function.
|
||||
*
|
||||
* @param irq int irq line
|
||||
* @param dev_id void * device id
|
||||
*
|
||||
* @return status IRQ_HANDLED for handled
|
||||
*/
|
||||
static irqreturn_t prpvf_sdc_vsync_callback(int irq, void *dev_id)
|
||||
{
|
||||
cam_data *cam = dev_id;
|
||||
if (buffer_ready > 0) {
|
||||
ipu_select_buffer(cam->ipu, MEM_ROT_VF_MEM,
|
||||
IPU_OUTPUT_BUFFER, 0);
|
||||
buffer_ready--;
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*!
|
||||
* VF EOF callback function.
|
||||
*
|
||||
* @param irq int irq line
|
||||
* @param dev_id void * device id
|
||||
*
|
||||
* @return status IRQ_HANDLED for handled
|
||||
*/
|
||||
static irqreturn_t prpvf_vf_eof_callback(int irq, void *dev_id)
|
||||
{
|
||||
cam_data *cam = dev_id;
|
||||
pr_debug("buffer_ready %d buffer_num %d\n", buffer_ready, buffer_num);
|
||||
|
||||
ipu_select_buffer(cam->ipu, MEM_ROT_VF_MEM,
|
||||
IPU_INPUT_BUFFER, buffer_num);
|
||||
buffer_num = (buffer_num == 0) ? 1 : 0;
|
||||
ipu_select_buffer(cam->ipu, CSI_PRP_VF_MEM,
|
||||
IPU_OUTPUT_BUFFER, buffer_num);
|
||||
buffer_ready++;
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*!
|
||||
* prpvf_start - start the vf task
|
||||
*
|
||||
* @param private cam_data * mxc v4l2 main structure
|
||||
*
|
||||
*/
|
||||
static int prpvf_start(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
ipu_channel_params_t vf;
|
||||
u32 format;
|
||||
u32 offset;
|
||||
u32 bpp, size = 3;
|
||||
int err = 0;
|
||||
#ifdef CONFIG_MXC_MIPI_CSI2
|
||||
void *mipi_csi2_info;
|
||||
int ipu_id;
|
||||
int csi_id;
|
||||
#endif
|
||||
|
||||
if (!cam) {
|
||||
printk(KERN_ERR "private is NULL\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (cam->overlay_active == true) {
|
||||
pr_debug("already start.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
get_disp_ipu(cam);
|
||||
|
||||
format = cam->v4l2_fb.fmt.pixelformat;
|
||||
if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_BGR24) {
|
||||
bpp = 3, size = 3;
|
||||
pr_info("BGR24\n");
|
||||
} else if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_RGB565) {
|
||||
bpp = 2, size = 2;
|
||||
pr_info("RGB565\n");
|
||||
} else if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_BGR32) {
|
||||
bpp = 4, size = 4;
|
||||
pr_info("BGR32\n");
|
||||
} else {
|
||||
printk(KERN_ERR
|
||||
"unsupported fix format from the framebuffer.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
offset = cam->v4l2_fb.fmt.bytesperline * cam->win.w.top +
|
||||
size * cam->win.w.left;
|
||||
|
||||
if (cam->v4l2_fb.base == 0)
|
||||
printk(KERN_ERR "invalid frame buffer address.\n");
|
||||
else
|
||||
offset += (u32) cam->v4l2_fb.base;
|
||||
|
||||
memset(&vf, 0, sizeof(ipu_channel_params_t));
|
||||
ipu_csi_get_window_size(cam->ipu, &vf.csi_prp_vf_mem.in_width,
|
||||
&vf.csi_prp_vf_mem.in_height, cam->csi);
|
||||
vf.csi_prp_vf_mem.in_pixel_fmt = IPU_PIX_FMT_UYVY;
|
||||
vf.csi_prp_vf_mem.out_width = cam->win.w.width;
|
||||
vf.csi_prp_vf_mem.out_height = cam->win.w.height;
|
||||
vf.csi_prp_vf_mem.csi = cam->csi;
|
||||
if (cam->vf_rotation >= IPU_ROTATE_90_RIGHT) {
|
||||
vf.csi_prp_vf_mem.out_width = cam->win.w.height;
|
||||
vf.csi_prp_vf_mem.out_height = cam->win.w.width;
|
||||
}
|
||||
vf.csi_prp_vf_mem.out_pixel_fmt = format;
|
||||
size = cam->win.w.width * cam->win.w.height * size;
|
||||
|
||||
#ifdef CONFIG_MXC_MIPI_CSI2
|
||||
mipi_csi2_info = mipi_csi2_get_info();
|
||||
|
||||
if (mipi_csi2_info) {
|
||||
if (mipi_csi2_get_status(mipi_csi2_info)) {
|
||||
ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info);
|
||||
csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info);
|
||||
|
||||
if (cam->ipu == ipu_get_soc(ipu_id)
|
||||
&& cam->csi == csi_id) {
|
||||
vf.csi_prp_vf_mem.mipi_en = true;
|
||||
vf.csi_prp_vf_mem.mipi_vc =
|
||||
mipi_csi2_get_virtual_channel(mipi_csi2_info);
|
||||
vf.csi_prp_vf_mem.mipi_id =
|
||||
mipi_csi2_get_datatype(mipi_csi2_info);
|
||||
|
||||
mipi_csi2_pixelclk_enable(mipi_csi2_info);
|
||||
} else {
|
||||
vf.csi_prp_vf_mem.mipi_en = false;
|
||||
vf.csi_prp_vf_mem.mipi_vc = 0;
|
||||
vf.csi_prp_vf_mem.mipi_id = 0;
|
||||
}
|
||||
} else {
|
||||
vf.csi_prp_vf_mem.mipi_en = false;
|
||||
vf.csi_prp_vf_mem.mipi_vc = 0;
|
||||
vf.csi_prp_vf_mem.mipi_id = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
err = ipu_init_channel(cam->ipu, CSI_PRP_VF_MEM, &vf);
|
||||
if (err != 0)
|
||||
goto out_4;
|
||||
|
||||
if (cam->vf_bufs_vaddr[0]) {
|
||||
dma_free_coherent(cam->dev, cam->vf_bufs_size[0],
|
||||
cam->vf_bufs_vaddr[0], cam->vf_bufs[0]);
|
||||
}
|
||||
if (cam->vf_bufs_vaddr[1]) {
|
||||
dma_free_coherent(cam->dev, cam->vf_bufs_size[1],
|
||||
cam->vf_bufs_vaddr[1], cam->vf_bufs[1]);
|
||||
}
|
||||
cam->vf_bufs_size[0] = PAGE_ALIGN(size);
|
||||
cam->vf_bufs_vaddr[0] = (void *)dma_alloc_coherent(cam->dev,
|
||||
cam->vf_bufs_size[0],
|
||||
&cam->vf_bufs[0],
|
||||
GFP_DMA |
|
||||
GFP_KERNEL);
|
||||
if (cam->vf_bufs_vaddr[0] == NULL) {
|
||||
printk(KERN_ERR "Error to allocate vf buffer\n");
|
||||
err = -ENOMEM;
|
||||
goto out_3;
|
||||
}
|
||||
cam->vf_bufs_size[1] = PAGE_ALIGN(size);
|
||||
cam->vf_bufs_vaddr[1] = (void *)dma_alloc_coherent(cam->dev,
|
||||
cam->vf_bufs_size[1],
|
||||
&cam->vf_bufs[1],
|
||||
GFP_DMA |
|
||||
GFP_KERNEL);
|
||||
if (cam->vf_bufs_vaddr[1] == NULL) {
|
||||
printk(KERN_ERR "Error to allocate vf buffer\n");
|
||||
err = -ENOMEM;
|
||||
goto out_3;
|
||||
}
|
||||
|
||||
err = ipu_init_channel_buffer(cam->ipu, CSI_PRP_VF_MEM,
|
||||
IPU_OUTPUT_BUFFER,
|
||||
format, vf.csi_prp_vf_mem.out_width,
|
||||
vf.csi_prp_vf_mem.out_height,
|
||||
vf.csi_prp_vf_mem.out_width,
|
||||
IPU_ROTATE_NONE,
|
||||
cam->vf_bufs[0],
|
||||
cam->vf_bufs[1],
|
||||
0, 0, 0);
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "Error initializing CSI_PRP_VF_MEM\n");
|
||||
goto out_3;
|
||||
}
|
||||
err = ipu_init_channel(cam->ipu, MEM_ROT_VF_MEM, NULL);
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "Error MEM_ROT_VF_MEM channel\n");
|
||||
goto out_3;
|
||||
}
|
||||
|
||||
err = ipu_init_channel_buffer(cam->ipu, MEM_ROT_VF_MEM,
|
||||
IPU_INPUT_BUFFER,
|
||||
format, vf.csi_prp_vf_mem.out_width,
|
||||
vf.csi_prp_vf_mem.out_height,
|
||||
vf.csi_prp_vf_mem.out_width,
|
||||
cam->vf_rotation,
|
||||
cam->vf_bufs[0],
|
||||
cam->vf_bufs[1],
|
||||
0, 0, 0);
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "Error MEM_ROT_VF_MEM input buffer\n");
|
||||
goto out_2;
|
||||
}
|
||||
|
||||
if (cam->vf_rotation >= IPU_ROTATE_90_RIGHT) {
|
||||
err = ipu_init_channel_buffer(cam->ipu, MEM_ROT_VF_MEM,
|
||||
IPU_OUTPUT_BUFFER,
|
||||
format,
|
||||
vf.csi_prp_vf_mem.out_height,
|
||||
vf.csi_prp_vf_mem.out_width,
|
||||
cam->overlay_fb->var.xres * bpp,
|
||||
IPU_ROTATE_NONE,
|
||||
offset, 0, 0, 0, 0);
|
||||
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "Error MEM_ROT_VF_MEM output buffer\n");
|
||||
goto out_2;
|
||||
}
|
||||
} else {
|
||||
err = ipu_init_channel_buffer(cam->ipu, MEM_ROT_VF_MEM,
|
||||
IPU_OUTPUT_BUFFER,
|
||||
format,
|
||||
vf.csi_prp_vf_mem.out_width,
|
||||
vf.csi_prp_vf_mem.out_height,
|
||||
cam->overlay_fb->var.xres * bpp,
|
||||
IPU_ROTATE_NONE,
|
||||
offset, 0, 0, 0, 0);
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "Error MEM_ROT_VF_MEM output buffer\n");
|
||||
goto out_2;
|
||||
}
|
||||
}
|
||||
|
||||
ipu_clear_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF);
|
||||
err = ipu_request_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF,
|
||||
prpvf_vf_eof_callback,
|
||||
0, "Mxc Camera", cam);
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR
|
||||
"Error registering IPU_IRQ_PRP_VF_OUT_EOF irq.\n");
|
||||
goto out_2;
|
||||
}
|
||||
|
||||
ipu_clear_irq(disp_ipu, IPU_IRQ_BG_SF_END);
|
||||
err = ipu_request_irq(disp_ipu, IPU_IRQ_BG_SF_END,
|
||||
prpvf_sdc_vsync_callback,
|
||||
0, "Mxc Camera", cam);
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "Error registering IPU_IRQ_BG_SF_END irq.\n");
|
||||
goto out_1;
|
||||
}
|
||||
|
||||
ipu_enable_channel(cam->ipu, CSI_PRP_VF_MEM);
|
||||
ipu_enable_channel(cam->ipu, MEM_ROT_VF_MEM);
|
||||
|
||||
buffer_num = 0;
|
||||
buffer_ready = 0;
|
||||
ipu_select_buffer(cam->ipu, CSI_PRP_VF_MEM, IPU_OUTPUT_BUFFER, 0);
|
||||
|
||||
cam->overlay_active = true;
|
||||
return err;
|
||||
|
||||
out_1:
|
||||
ipu_free_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF, NULL);
|
||||
out_2:
|
||||
ipu_uninit_channel(cam->ipu, MEM_ROT_VF_MEM);
|
||||
out_3:
|
||||
ipu_uninit_channel(cam->ipu, CSI_PRP_VF_MEM);
|
||||
out_4:
|
||||
if (cam->vf_bufs_vaddr[0]) {
|
||||
dma_free_coherent(cam->dev, cam->vf_bufs_size[0],
|
||||
cam->vf_bufs_vaddr[0], cam->vf_bufs[0]);
|
||||
cam->vf_bufs_vaddr[0] = NULL;
|
||||
cam->vf_bufs[0] = 0;
|
||||
}
|
||||
if (cam->vf_bufs_vaddr[1]) {
|
||||
dma_free_coherent(cam->dev, cam->vf_bufs_size[1],
|
||||
cam->vf_bufs_vaddr[1], cam->vf_bufs[1]);
|
||||
cam->vf_bufs_vaddr[1] = NULL;
|
||||
cam->vf_bufs[1] = 0;
|
||||
}
|
||||
if (cam->rot_vf_bufs_vaddr[0]) {
|
||||
dma_free_coherent(cam->dev, cam->rot_vf_buf_size[0],
|
||||
cam->rot_vf_bufs_vaddr[0],
|
||||
cam->rot_vf_bufs[0]);
|
||||
cam->rot_vf_bufs_vaddr[0] = NULL;
|
||||
cam->rot_vf_bufs[0] = 0;
|
||||
}
|
||||
if (cam->rot_vf_bufs_vaddr[1]) {
|
||||
dma_free_coherent(cam->dev, cam->rot_vf_buf_size[1],
|
||||
cam->rot_vf_bufs_vaddr[1],
|
||||
cam->rot_vf_bufs[1]);
|
||||
cam->rot_vf_bufs_vaddr[1] = NULL;
|
||||
cam->rot_vf_bufs[1] = 0;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/*!
|
||||
* prpvf_stop - stop the vf task
|
||||
*
|
||||
* @param private cam_data * mxc v4l2 main structure
|
||||
*
|
||||
*/
|
||||
static int prpvf_stop(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
#ifdef CONFIG_MXC_MIPI_CSI2
|
||||
void *mipi_csi2_info;
|
||||
int ipu_id;
|
||||
int csi_id;
|
||||
#endif
|
||||
|
||||
if (cam->overlay_active == false)
|
||||
return 0;
|
||||
|
||||
ipu_free_irq(disp_ipu, IPU_IRQ_BG_SF_END, cam);
|
||||
|
||||
ipu_disable_channel(cam->ipu, CSI_PRP_VF_MEM, true);
|
||||
ipu_disable_channel(cam->ipu, MEM_ROT_VF_MEM, true);
|
||||
ipu_uninit_channel(cam->ipu, CSI_PRP_VF_MEM);
|
||||
ipu_uninit_channel(cam->ipu, MEM_ROT_VF_MEM);
|
||||
|
||||
#ifdef CONFIG_MXC_MIPI_CSI2
|
||||
mipi_csi2_info = mipi_csi2_get_info();
|
||||
|
||||
if (mipi_csi2_info) {
|
||||
if (mipi_csi2_get_status(mipi_csi2_info)) {
|
||||
ipu_id = mipi_csi2_get_bind_ipu(mipi_csi2_info);
|
||||
csi_id = mipi_csi2_get_bind_csi(mipi_csi2_info);
|
||||
|
||||
if (cam->ipu == ipu_get_soc(ipu_id)
|
||||
&& cam->csi == csi_id)
|
||||
mipi_csi2_pixelclk_disable(mipi_csi2_info);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (cam->vf_bufs_vaddr[0]) {
|
||||
dma_free_coherent(cam->dev, cam->vf_bufs_size[0],
|
||||
cam->vf_bufs_vaddr[0], cam->vf_bufs[0]);
|
||||
cam->vf_bufs_vaddr[0] = NULL;
|
||||
cam->vf_bufs[0] = 0;
|
||||
}
|
||||
if (cam->vf_bufs_vaddr[1]) {
|
||||
dma_free_coherent(cam->dev, cam->vf_bufs_size[1],
|
||||
cam->vf_bufs_vaddr[1], cam->vf_bufs[1]);
|
||||
cam->vf_bufs_vaddr[1] = NULL;
|
||||
cam->vf_bufs[1] = 0;
|
||||
}
|
||||
if (cam->rot_vf_bufs_vaddr[0]) {
|
||||
dma_free_coherent(cam->dev, cam->rot_vf_buf_size[0],
|
||||
cam->rot_vf_bufs_vaddr[0],
|
||||
cam->rot_vf_bufs[0]);
|
||||
cam->rot_vf_bufs_vaddr[0] = NULL;
|
||||
cam->rot_vf_bufs[0] = 0;
|
||||
}
|
||||
if (cam->rot_vf_bufs_vaddr[1]) {
|
||||
dma_free_coherent(cam->dev, cam->rot_vf_buf_size[1],
|
||||
cam->rot_vf_bufs_vaddr[1],
|
||||
cam->rot_vf_bufs[1]);
|
||||
cam->rot_vf_bufs_vaddr[1] = NULL;
|
||||
cam->rot_vf_bufs[1] = 0;
|
||||
}
|
||||
|
||||
buffer_num = 0;
|
||||
buffer_ready = 0;
|
||||
cam->overlay_active = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Enable csi
|
||||
* @param private struct cam_data * mxc capture instance
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
static int prp_vf_enable_csi(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
|
||||
return ipu_enable_csi(cam->ipu, cam->csi);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Disable csi
|
||||
* @param private struct cam_data * mxc capture instance
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
static int prp_vf_disable_csi(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
|
||||
/* free csi eof irq firstly.
|
||||
* when disable csi, wait for idmac eof.
|
||||
* it requests eof irq again */
|
||||
ipu_free_irq(cam->ipu, IPU_IRQ_PRP_VF_OUT_EOF, cam);
|
||||
|
||||
return ipu_disable_csi(cam->ipu, cam->csi);
|
||||
}
|
||||
|
||||
/*!
|
||||
* function to select PRP-VF as the working path
|
||||
*
|
||||
* @param private cam_data * mxc v4l2 main structure
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
int prp_vf_sdc_select_bg(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
|
||||
if (cam) {
|
||||
cam->vf_start_sdc = prpvf_start;
|
||||
cam->vf_stop_sdc = prpvf_stop;
|
||||
cam->vf_enable_csi = prp_vf_enable_csi;
|
||||
cam->vf_disable_csi = prp_vf_disable_csi;
|
||||
cam->overlay_active = false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(prp_vf_sdc_select_bg);
|
||||
|
||||
/*!
|
||||
* function to de-select PRP-VF as the working path
|
||||
*
|
||||
* @param private cam_data * mxc v4l2 main structure
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
int prp_vf_sdc_deselect_bg(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
|
||||
if (cam) {
|
||||
cam->vf_start_sdc = NULL;
|
||||
cam->vf_stop_sdc = NULL;
|
||||
cam->vf_enable_csi = NULL;
|
||||
cam->vf_disable_csi = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(prp_vf_sdc_deselect_bg);
|
||||
|
||||
/*!
|
||||
* Init viewfinder task.
|
||||
*
|
||||
* @return Error code indicating success or failure
|
||||
*/
|
||||
__init int prp_vf_sdc_init_bg(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Deinit viewfinder task.
|
||||
*
|
||||
* @return Error code indicating success or failure
|
||||
*/
|
||||
void __exit prp_vf_sdc_exit_bg(void)
|
||||
{
|
||||
}
|
||||
|
||||
module_init(prp_vf_sdc_init_bg);
|
||||
module_exit(prp_vf_sdc_exit_bg);
|
||||
|
||||
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
|
||||
MODULE_DESCRIPTION("IPU PRP VF SDC Backgroud Driver");
|
||||
MODULE_LICENSE("GPL");
|
261
drivers/media/platform/mxc/capture/ipu_still.c
Normal file
261
drivers/media/platform/mxc/capture/ipu_still.c
Normal file
|
@ -0,0 +1,261 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright 2004-2014 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
* Copyright 2019 NXP
|
||||
*/
|
||||
|
||||
/*!
|
||||
* @file ipu_still.c
|
||||
*
|
||||
* @brief IPU Use case for still image capture
|
||||
*
|
||||
* @ingroup IPU
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/semaphore.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/ipu.h>
|
||||
#include "mxc_v4l2_capture.h"
|
||||
#include "ipu_prp_sw.h"
|
||||
|
||||
static int callback_eof_flag;
|
||||
#ifndef CONFIG_MXC_IPU_V1
|
||||
static int buffer_num;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MXC_IPU_V1
|
||||
static int callback_flag;
|
||||
/*
|
||||
* Function definitions
|
||||
*/
|
||||
/*!
|
||||
* CSI EOF callback function.
|
||||
*
|
||||
* @param irq int irq line
|
||||
* @param dev_id void * device id
|
||||
*
|
||||
* @return status IRQ_HANDLED for handled
|
||||
*/
|
||||
static irqreturn_t prp_csi_eof_callback(int irq, void *dev_id)
|
||||
{
|
||||
cam_data *cam = devid;
|
||||
ipu_select_buffer(cam->ipu, CSI_MEM, IPU_OUTPUT_BUFFER,
|
||||
callback_flag%2 ? 1 : 0);
|
||||
if (callback_flag == 0)
|
||||
ipu_enable_channel(cam->ipu, CSI_MEM);
|
||||
|
||||
callback_flag++;
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* CSI callback function.
|
||||
*
|
||||
* @param irq int irq line
|
||||
* @param dev_id void * device id
|
||||
*
|
||||
* @return status IRQ_HANDLED for handled
|
||||
*/
|
||||
static irqreturn_t prp_still_callback(int irq, void *dev_id)
|
||||
{
|
||||
cam_data *cam = (cam_data *) dev_id;
|
||||
|
||||
callback_eof_flag++;
|
||||
if (callback_eof_flag < 5) {
|
||||
#ifndef CONFIG_MXC_IPU_V1
|
||||
buffer_num = (buffer_num == 0) ? 1 : 0;
|
||||
ipu_select_buffer(cam->ipu, CSI_MEM,
|
||||
IPU_OUTPUT_BUFFER, buffer_num);
|
||||
#endif
|
||||
} else {
|
||||
cam->still_counter++;
|
||||
wake_up_interruptible(&cam->still_queue);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*!
|
||||
* start csi->mem task
|
||||
* @param private struct cam_data * mxc capture instance
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
static int prp_still_start(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
u32 pixel_fmt;
|
||||
int err;
|
||||
ipu_channel_params_t params;
|
||||
|
||||
if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420)
|
||||
pixel_fmt = IPU_PIX_FMT_YUV420P;
|
||||
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_NV12)
|
||||
pixel_fmt = IPU_PIX_FMT_NV12;
|
||||
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P)
|
||||
pixel_fmt = IPU_PIX_FMT_YUV422P;
|
||||
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY)
|
||||
pixel_fmt = IPU_PIX_FMT_UYVY;
|
||||
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
|
||||
pixel_fmt = IPU_PIX_FMT_YUYV;
|
||||
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR24)
|
||||
pixel_fmt = IPU_PIX_FMT_BGR24;
|
||||
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24)
|
||||
pixel_fmt = IPU_PIX_FMT_RGB24;
|
||||
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565)
|
||||
pixel_fmt = IPU_PIX_FMT_RGB565;
|
||||
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_BGR32)
|
||||
pixel_fmt = IPU_PIX_FMT_BGR32;
|
||||
else if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB32)
|
||||
pixel_fmt = IPU_PIX_FMT_RGB32;
|
||||
else {
|
||||
printk(KERN_ERR "format not supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
err = ipu_init_channel(cam->ipu, CSI_MEM, ¶ms);
|
||||
if (err != 0)
|
||||
return err;
|
||||
|
||||
err = ipu_init_channel_buffer(cam->ipu, CSI_MEM, IPU_OUTPUT_BUFFER,
|
||||
pixel_fmt, cam->v2f.fmt.pix.width,
|
||||
cam->v2f.fmt.pix.height,
|
||||
cam->v2f.fmt.pix.width, IPU_ROTATE_NONE,
|
||||
cam->still_buf[0], cam->still_buf[1], 0,
|
||||
0, 0);
|
||||
if (err != 0)
|
||||
return err;
|
||||
|
||||
#ifdef CONFIG_MXC_IPU_V1
|
||||
ipu_clear_irq(IPU_IRQ_SENSOR_OUT_EOF);
|
||||
err = ipu_request_irq(IPU_IRQ_SENSOR_OUT_EOF, prp_still_callback,
|
||||
0, "Mxc Camera", cam);
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "Error registering irq.\n");
|
||||
return err;
|
||||
}
|
||||
callback_flag = 0;
|
||||
callback_eof_flag = 0;
|
||||
ipu_clear_irq(IPU_IRQ_SENSOR_EOF);
|
||||
err = ipu_request_irq(IPU_IRQ_SENSOR_EOF, prp_csi_eof_callback,
|
||||
0, "Mxc Camera", cam);
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "Error IPU_IRQ_SENSOR_EOF\n");
|
||||
return err;
|
||||
}
|
||||
#else
|
||||
callback_eof_flag = 0;
|
||||
buffer_num = 0;
|
||||
|
||||
ipu_clear_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF);
|
||||
err = ipu_request_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF,
|
||||
prp_still_callback,
|
||||
0, "Mxc Camera", cam);
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "Error registering irq.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
ipu_select_buffer(cam->ipu, CSI_MEM, IPU_OUTPUT_BUFFER, 0);
|
||||
ipu_enable_channel(cam->ipu, CSI_MEM);
|
||||
ipu_enable_csi(cam->ipu, cam->csi);
|
||||
#endif
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*!
|
||||
* stop csi->mem encoder task
|
||||
* @param private struct cam_data * mxc capture instance
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
static int prp_still_stop(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
int err = 0;
|
||||
|
||||
#ifdef CONFIG_MXC_IPU_V1
|
||||
ipu_free_irq(IPU_IRQ_SENSOR_EOF, NULL);
|
||||
ipu_free_irq(IPU_IRQ_SENSOR_OUT_EOF, cam);
|
||||
#else
|
||||
ipu_free_irq(cam->ipu, IPU_IRQ_CSI0_OUT_EOF, cam);
|
||||
#endif
|
||||
|
||||
ipu_disable_csi(cam->ipu, cam->csi);
|
||||
ipu_disable_channel(cam->ipu, CSI_MEM, true);
|
||||
ipu_uninit_channel(cam->ipu, CSI_MEM);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*!
|
||||
* function to select CSI_MEM as the working path
|
||||
*
|
||||
* @param private struct cam_data * mxc capture instance
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
int prp_still_select(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
|
||||
if (cam) {
|
||||
cam->csi_start = prp_still_start;
|
||||
cam->csi_stop = prp_still_stop;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(prp_still_select);
|
||||
|
||||
/*!
|
||||
* function to de-select CSI_MEM as the working path
|
||||
*
|
||||
* @param private struct cam_data * mxc capture instance
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
int prp_still_deselect(void *private)
|
||||
{
|
||||
cam_data *cam = (cam_data *) private;
|
||||
int err = 0;
|
||||
|
||||
err = prp_still_stop(cam);
|
||||
|
||||
if (cam) {
|
||||
cam->csi_start = NULL;
|
||||
cam->csi_stop = NULL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(prp_still_deselect);
|
||||
|
||||
/*!
|
||||
* Init the Encorder channels
|
||||
*
|
||||
* @return Error code indicating success or failure
|
||||
*/
|
||||
__init int prp_still_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Deinit the Encorder channels
|
||||
*
|
||||
*/
|
||||
void __exit prp_still_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
module_init(prp_still_init);
|
||||
module_exit(prp_still_exit);
|
||||
|
||||
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
|
||||
MODULE_DESCRIPTION("IPU PRP STILL IMAGE Driver");
|
||||
MODULE_LICENSE("GPL");
|
2049
drivers/media/platform/mxc/capture/mx6s_capture.c
Normal file
2049
drivers/media/platform/mxc/capture/mx6s_capture.c
Normal file
File diff suppressed because it is too large
Load Diff
1312
drivers/media/platform/mxc/capture/mxc_mipi_csi.c
Normal file
1312
drivers/media/platform/mxc/capture/mxc_mipi_csi.c
Normal file
File diff suppressed because it is too large
Load Diff
3142
drivers/media/platform/mxc/capture/mxc_v4l2_capture.c
Normal file
3142
drivers/media/platform/mxc/capture/mxc_v4l2_capture.c
Normal file
File diff suppressed because it is too large
Load Diff
256
drivers/media/platform/mxc/capture/mxc_v4l2_capture.h
Normal file
256
drivers/media/platform/mxc/capture/mxc_v4l2_capture.h
Normal file
|
@ -0,0 +1,256 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright 2004-2015 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
* Copyright 2019 NXP
|
||||
*/
|
||||
|
||||
/*!
|
||||
* @defgroup MXC_V4L2_CAPTURE MXC V4L2 Video Capture Driver
|
||||
*/
|
||||
/*!
|
||||
* @file mxc_v4l2_capture.h
|
||||
*
|
||||
* @brief mxc V4L2 capture device API Header file
|
||||
*
|
||||
* It include all the defines for frame operations, also three structure defines
|
||||
* use case ops structure, common v4l2 driver structure and frame structure.
|
||||
*
|
||||
* @ingroup MXC_V4L2_CAPTURE
|
||||
*/
|
||||
#ifndef __MXC_V4L2_CAPTURE_H__
|
||||
#define __MXC_V4L2_CAPTURE_H__
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mxc_v4l2.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/pxp_dma.h>
|
||||
#include <linux/ipu-v3.h>
|
||||
#include <linux/platform_data/dma-imx.h>
|
||||
|
||||
#include <media/v4l2-dev.h>
|
||||
#include "v4l2-int-device.h"
|
||||
|
||||
|
||||
#define FRAME_NUM 10
|
||||
#define MXC_SENSOR_NUM 2
|
||||
|
||||
enum imx_v4l2_devtype {
|
||||
IMX5_V4L2,
|
||||
IMX6_V4L2,
|
||||
};
|
||||
|
||||
/*!
|
||||
* v4l2 frame structure.
|
||||
*/
|
||||
struct mxc_v4l_frame {
|
||||
u32 paddress;
|
||||
void *vaddress;
|
||||
int count;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
struct v4l2_buffer buffer;
|
||||
struct list_head queue;
|
||||
int index;
|
||||
union {
|
||||
int ipu_buf_num;
|
||||
int csi_buf_num;
|
||||
};
|
||||
};
|
||||
|
||||
/* Only for old version. Will go away soon. */
|
||||
typedef struct {
|
||||
u8 clk_mode;
|
||||
u8 ext_vsync;
|
||||
u8 Vsync_pol;
|
||||
u8 Hsync_pol;
|
||||
u8 pixclk_pol;
|
||||
u8 data_pol;
|
||||
u8 data_width;
|
||||
u8 pack_tight;
|
||||
u8 force_eof;
|
||||
u8 data_en_pol;
|
||||
u16 width;
|
||||
u16 height;
|
||||
u32 pixel_fmt;
|
||||
u32 mclk;
|
||||
u16 active_width;
|
||||
u16 active_height;
|
||||
} sensor_interface;
|
||||
|
||||
/* Sensor control function */
|
||||
/* Only for old version. Will go away soon. */
|
||||
struct camera_sensor {
|
||||
void (*set_color) (int bright, int saturation, int red, int green,
|
||||
int blue);
|
||||
void (*get_color) (int *bright, int *saturation, int *red, int *green,
|
||||
int *blue);
|
||||
void (*set_ae_mode) (int ae_mode);
|
||||
void (*get_ae_mode) (int *ae_mode);
|
||||
sensor_interface *(*config) (int *frame_rate, int high_quality);
|
||||
sensor_interface *(*reset) (void);
|
||||
void (*get_std) (v4l2_std_id *std);
|
||||
void (*set_std) (v4l2_std_id std);
|
||||
unsigned int csi;
|
||||
};
|
||||
|
||||
/*!
|
||||
* common v4l2 driver structure.
|
||||
*/
|
||||
typedef struct _cam_data {
|
||||
struct device *dev;
|
||||
struct video_device *video_dev;
|
||||
int device_type;
|
||||
|
||||
/* semaphore guard against SMP multithreading */
|
||||
struct semaphore busy_lock;
|
||||
|
||||
int open_count;
|
||||
|
||||
/* params lock for this camera */
|
||||
struct semaphore param_lock;
|
||||
|
||||
/* Encoder */
|
||||
struct list_head ready_q;
|
||||
struct list_head done_q;
|
||||
struct list_head working_q;
|
||||
int ping_pong_csi;
|
||||
spinlock_t queue_int_lock;
|
||||
spinlock_t dqueue_int_lock;
|
||||
struct mxc_v4l_frame frame[FRAME_NUM];
|
||||
struct mxc_v4l_frame dummy_frame;
|
||||
wait_queue_head_t enc_queue;
|
||||
int enc_counter;
|
||||
dma_addr_t rot_enc_bufs[2];
|
||||
void *rot_enc_bufs_vaddr[2];
|
||||
int rot_enc_buf_size[2];
|
||||
enum v4l2_buf_type type;
|
||||
|
||||
/* still image capture */
|
||||
wait_queue_head_t still_queue;
|
||||
int still_counter;
|
||||
dma_addr_t still_buf[2];
|
||||
void *still_buf_vaddr;
|
||||
|
||||
/* overlay */
|
||||
struct v4l2_window win;
|
||||
struct v4l2_framebuffer v4l2_fb;
|
||||
dma_addr_t vf_bufs[2];
|
||||
void *vf_bufs_vaddr[2];
|
||||
int vf_bufs_size[2];
|
||||
dma_addr_t rot_vf_bufs[2];
|
||||
void *rot_vf_bufs_vaddr[2];
|
||||
int rot_vf_buf_size[2];
|
||||
bool overlay_active;
|
||||
int output;
|
||||
struct fb_info *overlay_fb;
|
||||
int fb_origin_std;
|
||||
struct work_struct csi_work_struct;
|
||||
|
||||
/* v4l2 format */
|
||||
struct v4l2_format v2f;
|
||||
struct v4l2_format input_fmt; /* camera in */
|
||||
bool bswapenable;
|
||||
int rotation; /* for IPUv1 and IPUv3, this means encoder rotation */
|
||||
int vf_rotation; /* viewfinder rotation only for IPUv1 and IPUv3 */
|
||||
struct v4l2_mxc_offset offset;
|
||||
|
||||
/* V4l2 control bit */
|
||||
int bright;
|
||||
int hue;
|
||||
int contrast;
|
||||
int saturation;
|
||||
int red;
|
||||
int green;
|
||||
int blue;
|
||||
int ae_mode;
|
||||
|
||||
/* standard */
|
||||
struct v4l2_streamparm streamparm;
|
||||
struct v4l2_standard standard;
|
||||
bool standard_autodetect;
|
||||
|
||||
/* crop */
|
||||
struct v4l2_rect crop_bounds;
|
||||
struct v4l2_rect crop_defrect;
|
||||
struct v4l2_rect crop_current;
|
||||
|
||||
int (*enc_update_eba) (void *private, dma_addr_t eba);
|
||||
int (*enc_enable) (void *private);
|
||||
int (*enc_disable) (void *private);
|
||||
int (*enc_enable_csi) (void *private);
|
||||
int (*enc_disable_csi) (void *private);
|
||||
void (*enc_callback) (u32 mask, void *dev);
|
||||
int (*vf_start_adc) (void *private);
|
||||
int (*vf_stop_adc) (void *private);
|
||||
int (*vf_start_sdc) (void *private);
|
||||
int (*vf_stop_sdc) (void *private);
|
||||
int (*vf_enable_csi) (void *private);
|
||||
int (*vf_disable_csi) (void *private);
|
||||
int (*csi_start) (void *private);
|
||||
int (*csi_stop) (void *private);
|
||||
|
||||
/* misc status flag */
|
||||
bool overlay_on;
|
||||
bool capture_on;
|
||||
int overlay_pid;
|
||||
int capture_pid;
|
||||
bool low_power;
|
||||
wait_queue_head_t power_queue;
|
||||
unsigned int ipu_id;
|
||||
unsigned int csi;
|
||||
u8 mclk_source;
|
||||
bool mclk_on[2]; /* two mclk sources at most now */
|
||||
int current_input;
|
||||
|
||||
int local_buf_num;
|
||||
|
||||
/* camera sensor interface */
|
||||
struct camera_sensor *cam_sensor; /* old version */
|
||||
struct v4l2_int_device *all_sensors[MXC_SENSOR_NUM];
|
||||
struct v4l2_int_device *sensor;
|
||||
struct v4l2_int_device *self;
|
||||
int sensor_index;
|
||||
void *ipu;
|
||||
void *csi_soc;
|
||||
enum imx_v4l2_devtype devtype;
|
||||
|
||||
/* v4l2 buf elements related to PxP DMA */
|
||||
struct completion pxp_tx_cmpl;
|
||||
struct pxp_channel *pxp_chan;
|
||||
struct pxp_config_data pxp_conf;
|
||||
struct dma_async_tx_descriptor *txd;
|
||||
dma_cookie_t cookie;
|
||||
struct scatterlist sg[2];
|
||||
} cam_data;
|
||||
|
||||
struct sensor_data {
|
||||
const struct ov5642_platform_data *platform_data;
|
||||
struct v4l2_int_device *v4l2_int_device;
|
||||
struct i2c_client *i2c_client;
|
||||
struct v4l2_pix_format pix;
|
||||
struct v4l2_captureparm streamcap;
|
||||
bool on;
|
||||
|
||||
/* control settings */
|
||||
int brightness;
|
||||
int hue;
|
||||
int contrast;
|
||||
int saturation;
|
||||
int red;
|
||||
int green;
|
||||
int blue;
|
||||
int ae_mode;
|
||||
|
||||
u32 mclk;
|
||||
u8 mclk_source;
|
||||
struct clk *sensor_clk;
|
||||
int csi;
|
||||
|
||||
void (*io_init)(void);
|
||||
};
|
||||
|
||||
void set_mclk_rate(uint32_t *p_mclk_freq, uint32_t csi);
|
||||
#endif /* __MXC_V4L2_CAPTURE_H__ */
|
830
drivers/media/platform/mxc/capture/mxc_vadc.c
Normal file
830
drivers/media/platform/mxc/capture/mxc_vadc.c
Normal file
|
@ -0,0 +1,830 @@
|
|||
/*
|
||||
* Copyright (C) 2014-2015 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/media-bus-format.h>
|
||||
#include <media/v4l2-ioctl.h>
|
||||
#include <linux/videodev2.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
#include "mxc_vadc.h"
|
||||
|
||||
/* Resource names for the VADC driver. */
|
||||
#define VAFE_REGS_ADDR_RES_NAME "vadc-vafe"
|
||||
#define VDEC_REGS_ADDR_RES_NAME "vadc-vdec"
|
||||
|
||||
#define reg32_write(addr, val) __raw_writel(val, addr)
|
||||
#define reg32_read(addr) __raw_readl(addr)
|
||||
#define reg32setbit(addr, bitpos) \
|
||||
reg32_write((addr), (reg32_read((addr)) | (1<<(bitpos))))
|
||||
|
||||
#define reg32clrbit(addr, bitpos) \
|
||||
reg32_write((addr), (reg32_read((addr)) & (0xFFFFFFFF ^ (1<<(bitpos)))))
|
||||
|
||||
#define GPC_CNTR 0x00
|
||||
#define IMX6SX_GPC_CNTR_VADC_ANALOG_OFF_MASK BIT(17)
|
||||
#define IMX6SX_GPC_CNTR_VADC_POWER_DOWN_MASK BIT(18)
|
||||
|
||||
void __iomem *vafe_regbase;
|
||||
void __iomem *vdec_regbase;
|
||||
|
||||
|
||||
/* List of input video formats supported. The video formats is corresponding
|
||||
* with v4l2 id in video_fmt
|
||||
*/
|
||||
enum video_fmt_idx {
|
||||
VADC_NTSC = 0, /* Locked on (M) NTSC video signal. */
|
||||
VADC_PAL, /* (B, G, H, I, N)PAL video signal. */
|
||||
};
|
||||
|
||||
/* Number of video standards supported (including 'not locked' signal). */
|
||||
#define VADC_STD_MAX (VADC_PAL + 1)
|
||||
|
||||
/* Video format structure. */
|
||||
struct video_fmt{
|
||||
v4l2_std_id v4l2_std; /* Video for linux ID. */
|
||||
char name[16]; /* Name (e.g., "NTSC", "PAL", etc.) */
|
||||
u16 raw_width; /* Raw width. */
|
||||
u16 raw_height; /* Raw height. */
|
||||
u16 active_width; /* Active width. */
|
||||
u16 active_height; /* Active height. */
|
||||
u16 framerates;
|
||||
};
|
||||
|
||||
/*
|
||||
* Maintains the information on the current state of the sensor.
|
||||
*/
|
||||
struct vadc_state {
|
||||
struct v4l2_device v4l2_dev;
|
||||
struct v4l2_subdev sd;
|
||||
struct video_fmt *fmt;
|
||||
|
||||
struct clk *vadc_clk;
|
||||
struct clk *csi_clk;
|
||||
struct regmap *gpr;
|
||||
void __iomem *gpc_reg;
|
||||
|
||||
u32 vadc_in;
|
||||
u32 csi_id;
|
||||
};
|
||||
|
||||
static int vadc_querystd(struct v4l2_subdev *sd, v4l2_std_id *std);
|
||||
|
||||
/* Description of video formats supported.
|
||||
*
|
||||
* PAL: raw=720x625, active=720x576.
|
||||
* NTSC: raw=720x525, active=720x480.
|
||||
*/
|
||||
static struct video_fmt video_fmts[] = {
|
||||
/* NTSC */
|
||||
{
|
||||
.v4l2_std = V4L2_STD_NTSC,
|
||||
.name = "NTSC",
|
||||
.raw_width = 720,
|
||||
.raw_height = 525,
|
||||
.active_width = 720,
|
||||
.active_height = 480,
|
||||
.framerates = 30,
|
||||
},
|
||||
/* (B, G, H, I, N) PAL */
|
||||
{
|
||||
.v4l2_std = V4L2_STD_PAL,
|
||||
.name = "PAL",
|
||||
.raw_width = 720,
|
||||
.raw_height = 625,
|
||||
.active_width = 720,
|
||||
.active_height = 576,
|
||||
.framerates = 25,
|
||||
},
|
||||
};
|
||||
|
||||
static void afe_voltage_clampingmode(void)
|
||||
{
|
||||
reg32_write(AFE_CLAMP, 0x07);
|
||||
reg32_write(AFE_CLMPAMP, 0x60);
|
||||
reg32_write(AFE_CLMPDAT, 0xF0);
|
||||
}
|
||||
|
||||
static void afe_alwayson_clampingmode(void)
|
||||
{
|
||||
reg32_write(AFE_CLAMP, 0x15);
|
||||
reg32_write(AFE_CLMPDAT, 0x08);
|
||||
reg32_write(AFE_CLMPAMP, 0x00);
|
||||
}
|
||||
|
||||
static void afe_init(void)
|
||||
{
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
reg32_write(AFE_PDBUF, 0x1f);
|
||||
reg32_write(AFE_PDADC, 0x0f);
|
||||
reg32_write(AFE_PDSARH, 0x01);
|
||||
reg32_write(AFE_PDSARL, 0xff);
|
||||
reg32_write(AFE_PDADCRFH, 0x01);
|
||||
reg32_write(AFE_PDADCRFL, 0xff);
|
||||
reg32_write(AFE_ICTRL, 0x3a);
|
||||
reg32_write(AFE_ICTLSTG, 0x1e);
|
||||
|
||||
reg32_write(AFE_RCTRLSTG, 0x1e);
|
||||
reg32_write(AFE_INPBUF, 0x035);
|
||||
reg32_write(AFE_INPFLT, 0x02);
|
||||
reg32_write(AFE_ADCDGN, 0x40);
|
||||
reg32_write(AFE_TSTSEL, 0x10);
|
||||
|
||||
reg32_write(AFE_ACCTST, 0x07);
|
||||
|
||||
reg32_write(AFE_BGREG, 0x08);
|
||||
|
||||
reg32_write(AFE_ADCGN, 0x09);
|
||||
|
||||
/* set current controlled clamping
|
||||
* always on, low current */
|
||||
reg32_write(AFE_CLAMP, 0x11);
|
||||
reg32_write(AFE_CLMPAMP, 0x08);
|
||||
}
|
||||
|
||||
static void vdec_mode_timing_init(int std)
|
||||
{
|
||||
if (std == V4L2_STD_NTSC) {
|
||||
/* NTSC 720x480 */
|
||||
reg32_write(VDEC_HACTS, 0x66);
|
||||
reg32_write(VDEC_HACTE, 0x24);
|
||||
|
||||
reg32_write(VDEC_VACTS, 0x29);
|
||||
reg32_write(VDEC_VACTE, 0x04);
|
||||
|
||||
/* set V Position */
|
||||
reg32_write(VDEC_VRTPOS, 0x2);
|
||||
} else if (std == V4L2_STD_PAL) {
|
||||
/* PAL 720x576 */
|
||||
reg32_write(VDEC_HACTS, 0x66);
|
||||
reg32_write(VDEC_HACTE, 0x24);
|
||||
|
||||
reg32_write(VDEC_VACTS, 0x29);
|
||||
reg32_write(VDEC_VACTE, 0x04);
|
||||
|
||||
/* set V Position */
|
||||
reg32_write(VDEC_VRTPOS, 0x6);
|
||||
} else
|
||||
pr_debug("Error not support video mode\n");
|
||||
|
||||
/* set H Position */
|
||||
reg32_write(VDEC_HZPOS, 0x60);
|
||||
|
||||
/* set H ignore start */
|
||||
reg32_write(VDEC_HSIGS, 0xf8);
|
||||
|
||||
/* set H ignore end */
|
||||
reg32_write(VDEC_HSIGE, 0x18);
|
||||
}
|
||||
|
||||
/*
|
||||
* vdec_init()
|
||||
* Initialises the VDEC registers
|
||||
* Returns: nothing
|
||||
*/
|
||||
static void vdec_init(struct vadc_state *vadc)
|
||||
{
|
||||
v4l2_std_id std;
|
||||
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
/* Get work mode PAL or NTSC */
|
||||
vadc_querystd(&vadc->sd, &std);
|
||||
|
||||
vdec_mode_timing_init(std);
|
||||
|
||||
/* vcr detect threshold high, automatic detections */
|
||||
reg32_write(VDEC_VSCON2, 0);
|
||||
|
||||
reg32_write(VDEC_BASE + 0x110, 0x01);
|
||||
|
||||
/* set the noramp mode on the Hloop PLL. */
|
||||
reg32_write(VDEC_BASE+(0x14*4), 0x10);
|
||||
|
||||
/* set the YC relative delay.*/
|
||||
reg32_write(VDEC_YCDEL, 0x90);
|
||||
|
||||
/* setup the Hpll */
|
||||
reg32_write(VDEC_BASE+(0x13*4), 0x13);
|
||||
|
||||
/* setup the 2d comb */
|
||||
/* set the gain of the Hdetail output to 3
|
||||
* set the notch alpha gain to 1 */
|
||||
reg32_write(VDEC_CFC2, 0x34);
|
||||
|
||||
/* setup various 2d comb bits.*/
|
||||
reg32_write(VDEC_BASE+(0x02*4), 0x01);
|
||||
reg32_write(VDEC_BASE+(0x03*4), 0x18);
|
||||
reg32_write(VDEC_BASE+(0x04*4), 0x34);
|
||||
|
||||
/* set the start of the burst gate */
|
||||
reg32_write(VDEC_BRSTGT, 0x30);
|
||||
|
||||
/* set 1f motion gain */
|
||||
reg32_write(VDEC_BASE+(0x0f*4), 0x20);
|
||||
|
||||
/* set the 1F chroma motion detector thresh
|
||||
* for colour reverse detection */
|
||||
reg32_write(VDEC_THSH1, 0x02);
|
||||
reg32_write(VDEC_BASE+(0x4a*4), 0x20);
|
||||
reg32_write(VDEC_BASE+(0x4b*4), 0x08);
|
||||
|
||||
reg32_write(VDEC_BASE+(0x4c*4), 0x08);
|
||||
|
||||
/* set the threshold for the narrow/wide adaptive chroma BW */
|
||||
reg32_write(VDEC_BASE+(0x20*4), 0x20);
|
||||
|
||||
/* turn up the colour with the new colour gain reg */
|
||||
/* hue: */
|
||||
reg32_write(VDEC_HUE, 0x00);
|
||||
|
||||
/* cbgain: 22 B4 */
|
||||
reg32_write(VDEC_CBGN, 0xb4);
|
||||
/* cr gain 80 */
|
||||
reg32_write(VDEC_CRGN, 0x80);
|
||||
/* luma gain (contrast) */
|
||||
reg32_write(VDEC_CNTR, 0x80);
|
||||
|
||||
/* setup the signed black level register, brightness */
|
||||
reg32_write(VDEC_BRT, 0x00);
|
||||
|
||||
/* filter the standard detection
|
||||
* enable the comb for the ntsc443 */
|
||||
reg32_write(VDEC_STDDBG, 0x20);
|
||||
|
||||
/* setup chroma kill thresh for no chroma */
|
||||
reg32_write(VDEC_CHBTH, 0x0);
|
||||
|
||||
/* set chroma loop to wider BW
|
||||
* no set it to normal BW. i fixed the bw problem.*/
|
||||
reg32_write(VDEC_YCDEL, 0x00);
|
||||
|
||||
/* set the compensation in the chroma loop for the Hloop
|
||||
* set the ratio for the nonarithmetic 3d comb modes.*/
|
||||
reg32_write(VDEC_BASE + (0x1d*4), 0x90);
|
||||
|
||||
/* set the threshold for the nonarithmetic mode for the 2d comb
|
||||
* the higher the value the more Fc Fh offset
|
||||
* we will tolerate before turning off the comb. */
|
||||
reg32_write(VDEC_BASE + (0x33*4), 0xa0);
|
||||
|
||||
/* setup the bluescreen output colour */
|
||||
reg32_write(VDEC_BASE + (0x3d*4), 35);
|
||||
reg32_write(VDEC_BLSCRCR, 114);
|
||||
reg32_write(VDEC_BLSCRCB, 212);
|
||||
|
||||
/* disable the active blanking */
|
||||
reg32_write(VDEC_BASE + (0x15*4), 0x02);
|
||||
|
||||
/* setup the luma agc for automatic gain. */
|
||||
reg32_write(VDEC_LMAGC2, 0x5e);
|
||||
reg32_write(VDEC_LMAGC1, 0x81);
|
||||
|
||||
/* setup chroma agc */
|
||||
reg32_write(VDEC_CHAGC2, 0xa0);
|
||||
reg32_write(VDEC_CHAGC1, 0x01);
|
||||
|
||||
/* setup the MV thresh lower nibble
|
||||
* setup the sync top cap, upper nibble */
|
||||
reg32_write(VDEC_BASE + (0x3a*4), 0x80);
|
||||
reg32_write(VDEC_SHPIMP, 0x00);
|
||||
|
||||
/* setup the vsync block */
|
||||
reg32_write(VDEC_VSCON1, 0x87);
|
||||
|
||||
/* set the nosignal threshold
|
||||
* set the vsync threshold */
|
||||
reg32_write(VDEC_VSSGTH, 0x35);
|
||||
|
||||
/* set length for min hphase filter
|
||||
* (or saturate limit if saturate is chosen) */
|
||||
reg32_write(VDEC_BASE + (0x45*4), 0x40);
|
||||
|
||||
/* enable the internal resampler,
|
||||
* select min filter not saturate for
|
||||
* hphase noise filter for vcr detect.
|
||||
* enable vcr pause mode different field lengths */
|
||||
reg32_write(VDEC_BASE + (0x46*4), 0x90);
|
||||
|
||||
/* disable VCR detection, lock to the Hsync rather than the Vsync */
|
||||
reg32_write(VDEC_VSCON2, 0x04);
|
||||
|
||||
/* set tiplevel goal for dc clamp. */
|
||||
reg32_write(VDEC_BASE + (0x3c*4), 0xB0);
|
||||
|
||||
/* override SECAM detection and force SECAM off */
|
||||
reg32_write(VDEC_BASE + (0x2f*4), 0x20);
|
||||
|
||||
/* Set r3d_hardblend in 3D control2 reg */
|
||||
reg32_write(VDEC_BASE + (0x0c*4), 0x04);
|
||||
}
|
||||
|
||||
/* set Input selector & input pull-downs */
|
||||
static void vadc_s_routing(int vadc_in)
|
||||
{
|
||||
switch (vadc_in) {
|
||||
case 0:
|
||||
reg32_write(AFE_INPFLT, 0x02);
|
||||
reg32_write(AFE_OFFDRV, 0x00);
|
||||
reg32_write(AFE_INPCONFIG, 0x1e);
|
||||
break;
|
||||
case 1:
|
||||
reg32_write(AFE_INPFLT, 0x02);
|
||||
reg32_write(AFE_OFFDRV, 0x00);
|
||||
reg32_write(AFE_INPCONFIG, 0x2d);
|
||||
break;
|
||||
case 2:
|
||||
reg32_write(AFE_INPFLT, 0x02);
|
||||
reg32_write(AFE_OFFDRV, 0x00);
|
||||
reg32_write(AFE_INPCONFIG, 0x4b);
|
||||
break;
|
||||
case 3:
|
||||
reg32_write(AFE_INPFLT, 0x02);
|
||||
reg32_write(AFE_OFFDRV, 0x00);
|
||||
reg32_write(AFE_INPCONFIG, 0x87);
|
||||
break;
|
||||
default:
|
||||
pr_debug("error video input %d\n", vadc_in);
|
||||
}
|
||||
}
|
||||
|
||||
static void vadc_power_up(struct vadc_state *state)
|
||||
{
|
||||
/* Power on vadc analog */
|
||||
reg32clrbit(state->gpc_reg + GPC_CNTR, 17);
|
||||
|
||||
/* Power down vadc ext power */
|
||||
reg32clrbit(state->gpc_reg + GPC_CNTR, 18);
|
||||
|
||||
/* software reset afe */
|
||||
regmap_update_bits(state->gpr, IOMUXC_GPR1,
|
||||
IMX6SX_GPR1_VADC_SW_RST_MASK,
|
||||
IMX6SX_GPR1_VADC_SW_RST_RESET);
|
||||
|
||||
msleep(10);
|
||||
|
||||
/* clock config for vadc */
|
||||
reg32_write(VDEC_BASE + 0x320, 0xe3);
|
||||
reg32_write(VDEC_BASE + 0x324, 0x38);
|
||||
reg32_write(VDEC_BASE + 0x328, 0x8e);
|
||||
reg32_write(VDEC_BASE + 0x32c, 0x23);
|
||||
|
||||
/* Release reset bit */
|
||||
regmap_update_bits(state->gpr, IOMUXC_GPR1,
|
||||
IMX6SX_GPR1_VADC_SW_RST_MASK,
|
||||
IMX6SX_GPR1_VADC_SW_RST_RELEASE);
|
||||
|
||||
/* Power on vadc ext power */
|
||||
reg32setbit(state->gpc_reg + GPC_CNTR, 18);
|
||||
}
|
||||
|
||||
static void vadc_power_down(struct vadc_state *state)
|
||||
{
|
||||
/* Power down vadc analog */
|
||||
reg32setbit(state->gpc_reg + GPC_CNTR, 17);
|
||||
|
||||
/* Power down vadc ext power */
|
||||
reg32clrbit(state->gpc_reg + GPC_CNTR, 18);
|
||||
|
||||
}
|
||||
static void vadc_init(struct vadc_state *vadc)
|
||||
{
|
||||
pr_debug("%s\n", __func__);
|
||||
|
||||
vadc_power_up(vadc);
|
||||
|
||||
afe_init();
|
||||
|
||||
/* select Video Input 0-3 */
|
||||
vadc_s_routing(vadc->vadc_in);
|
||||
|
||||
afe_voltage_clampingmode();
|
||||
|
||||
vdec_init(vadc);
|
||||
|
||||
/*
|
||||
* current control loop will move sinewave input off below
|
||||
* the bottom of the signal range visible
|
||||
* when the testbus is viewed as magnitude,
|
||||
* so have to break before this point while capturing ENOB data:
|
||||
*/
|
||||
afe_alwayson_clampingmode();
|
||||
}
|
||||
|
||||
static inline struct vadc_state *to_state(struct v4l2_subdev *sd)
|
||||
{
|
||||
return container_of(sd, struct vadc_state, sd);
|
||||
}
|
||||
|
||||
static int vadc_g_std(struct v4l2_subdev *sd, v4l2_std_id *std)
|
||||
{
|
||||
struct vadc_state *state = to_state(sd);
|
||||
|
||||
*std = state->fmt->v4l2_std;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Return attributes of current video standard.
|
||||
* Since this device autodetects the current standard, this function also
|
||||
* sets the values that need to be changed if the standard changes.
|
||||
* There is no set std equivalent function.
|
||||
*
|
||||
* @return None.
|
||||
*/
|
||||
static int vadc_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
|
||||
{
|
||||
struct vadc_state *state = to_state(sd);
|
||||
int mod;
|
||||
int idx;
|
||||
int i;
|
||||
|
||||
/* Read auto mode detected result */
|
||||
printk(KERN_INFO"wait vadc auto detect video mode....\n");
|
||||
for (i = 0; i < 10; i++) {
|
||||
msleep(200);
|
||||
mod = reg32_read(VDEC_VIDMOD);
|
||||
/* Check video signal states */
|
||||
if ((mod & VDEC_VIDMOD_SIGNAL_MASK)
|
||||
== VDEC_VIDMOD_SIGNAL_DETECT)
|
||||
break;
|
||||
}
|
||||
if (i == 10)
|
||||
printk(KERN_INFO"Timeout detect video signal mod=0x%x\n", mod);
|
||||
|
||||
if ((mod & VDEC_VIDMOD_PAL_MASK) || (mod & VDEC_VIDMOD_M625_MASK))
|
||||
idx = VADC_PAL;
|
||||
else
|
||||
idx = VADC_NTSC;
|
||||
|
||||
*std = video_fmts[idx].v4l2_std;
|
||||
state->fmt = &video_fmts[idx];
|
||||
|
||||
printk(KERN_INFO"video mode %s\n", video_fmts[idx].name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vadc_enum_mbus_code(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_pad_config *cfg,
|
||||
struct v4l2_subdev_mbus_code_enum *code)
|
||||
{
|
||||
/* support only one format */
|
||||
if (code->pad || code->index >= 1)
|
||||
return -EINVAL;
|
||||
|
||||
code->code = MEDIA_BUS_FMT_AYUV8_1X32;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vadc_get_fmt(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_pad_config *cfg,
|
||||
struct v4l2_subdev_format *format)
|
||||
{
|
||||
struct vadc_state *state = to_state(sd);
|
||||
struct v4l2_mbus_framefmt *fmt = &format->format;
|
||||
|
||||
if (format->pad)
|
||||
return -EINVAL;
|
||||
|
||||
fmt->code = MEDIA_BUS_FMT_AYUV8_1X32;
|
||||
fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
|
||||
fmt->field = V4L2_FIELD_INTERLACED;
|
||||
fmt->width = 720;
|
||||
fmt->height = state->fmt->v4l2_std & V4L2_STD_NTSC ? 480 : 576;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vadc_set_fmt(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_pad_config *cfg,
|
||||
struct v4l2_subdev_format *format)
|
||||
{
|
||||
return vadc_get_fmt(sd, cfg, format);
|
||||
}
|
||||
|
||||
static int vadc_enum_framesizes(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_pad_config *cfg,
|
||||
struct v4l2_subdev_frame_size_enum *fse)
|
||||
{
|
||||
struct vadc_state *state = to_state(sd);
|
||||
if (fse->index >= 1)
|
||||
return -EINVAL;
|
||||
|
||||
fse->min_width = state->fmt->active_width;
|
||||
fse->max_width = state->fmt->active_width;
|
||||
fse->min_height = state->fmt->active_height;
|
||||
fse->max_height = state->fmt->active_height;
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int vadc_enum_frameintervals(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_pad_config *cfg,
|
||||
struct v4l2_subdev_frame_interval_enum *fie)
|
||||
{
|
||||
struct vadc_state *state = to_state(sd);
|
||||
|
||||
if (fie->index < 0 || fie->index >= 1)
|
||||
return -EINVAL;
|
||||
|
||||
fie->interval.numerator = 1;
|
||||
|
||||
fie->interval.denominator = state->fmt->framerates;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vadc_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
|
||||
{
|
||||
struct vadc_state *state = to_state(sd);
|
||||
|
||||
if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
|
||||
return -EINVAL;
|
||||
|
||||
if (parms->parm.capture.timeperframe.denominator
|
||||
!= state->fmt->framerates)
|
||||
parms->parm.capture.timeperframe.denominator
|
||||
= state->fmt->framerates;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct v4l2_subdev_video_ops vadc_video_ops = {
|
||||
.querystd = vadc_querystd,
|
||||
.s_parm = vadc_s_parm,
|
||||
.g_std = vadc_g_std,
|
||||
};
|
||||
|
||||
|
||||
static const struct v4l2_subdev_pad_ops vadc_pad_ops = {
|
||||
.get_fmt = vadc_get_fmt,
|
||||
.set_fmt = vadc_set_fmt,
|
||||
.enum_mbus_code = vadc_enum_mbus_code,
|
||||
.enum_frame_size = vadc_enum_framesizes,
|
||||
.enum_frame_interval = vadc_enum_frameintervals,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_ops vadc_ops = {
|
||||
.video = &vadc_video_ops,
|
||||
.pad = &vadc_pad_ops,
|
||||
};
|
||||
|
||||
static const struct of_device_id fsl_vadc_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx6sx-vadc", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fsl_vadc_dt_ids);
|
||||
|
||||
static int vadc_of_init(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device_node *gpc_np;
|
||||
struct vadc_state *state = platform_get_drvdata(pdev);
|
||||
int csi_id;
|
||||
int ret;
|
||||
|
||||
/* Get csi_id to setting vadc to csi mux in gpr */
|
||||
ret = of_property_read_u32(np, "csi_id", &csi_id);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to read of property csi_id\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
state->csi_id = csi_id;
|
||||
|
||||
/* remap GPR register */
|
||||
state->gpr = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
|
||||
"gpr");
|
||||
if (IS_ERR(state->gpr)) {
|
||||
dev_dbg(&pdev->dev, "can not get gpr\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Configuration vadc-to-csi 0 or 1 */
|
||||
if (csi_id) {
|
||||
regmap_update_bits(state->gpr, IOMUXC_GPR5,
|
||||
IMX6SX_GPR5_CSI2_MUX_CTRL_MASK,
|
||||
IMX6SX_GPR5_CSI2_MUX_CTRL_CVD);
|
||||
} else {
|
||||
regmap_update_bits(state->gpr, IOMUXC_GPR5,
|
||||
IMX6SX_GPR5_CSI1_MUX_CTRL_MASK,
|
||||
IMX6SX_GPR5_CSI1_MUX_CTRL_CVD);
|
||||
}
|
||||
|
||||
/* Get default vadc_in number */
|
||||
ret = of_property_read_u32(np, "vadc_in", &state->vadc_in);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to read of property vadc_in\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* map GPC register */
|
||||
gpc_np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-gpc");
|
||||
state->gpc_reg = of_iomap(gpc_np, 0);
|
||||
if (!state->gpc_reg) {
|
||||
dev_err(&pdev->dev, "ioremap failed with gpc base\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
error:
|
||||
iounmap(state->gpc_reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void vadc_v4l2_subdev_init(struct v4l2_subdev *sd,
|
||||
struct platform_device *pdev,
|
||||
const struct v4l2_subdev_ops *ops)
|
||||
{
|
||||
struct vadc_state *state = platform_get_drvdata(pdev);
|
||||
int ret = 0;
|
||||
|
||||
v4l2_subdev_init(sd, ops);
|
||||
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
|
||||
sd->owner = pdev->dev.driver->owner;
|
||||
sd->dev = &pdev->dev;
|
||||
|
||||
/* initialize name */
|
||||
snprintf(sd->name, sizeof(sd->name), "%s",
|
||||
pdev->dev.driver->name);
|
||||
|
||||
v4l2_set_subdevdata(sd, state);
|
||||
|
||||
ret = v4l2_async_register_subdev(sd);
|
||||
if (ret < 0)
|
||||
dev_err(&pdev->dev, "%s--Async register faialed, ret=%d\n", __func__, ret);
|
||||
}
|
||||
|
||||
static int vadc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct vadc_state *state;
|
||||
struct v4l2_subdev *sd;
|
||||
struct resource *res;
|
||||
int ret = 0;
|
||||
|
||||
state = devm_kzalloc(&pdev->dev, sizeof(struct vadc_state), GFP_KERNEL);
|
||||
if (!state) {
|
||||
dev_err(&pdev->dev, "Cannot allocate device data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Set initial values for the sensor struct. */
|
||||
state->fmt = &video_fmts[VADC_NTSC];
|
||||
|
||||
sd = &state->sd;
|
||||
|
||||
/* map vafe address */
|
||||
res = platform_get_resource_byname(pdev,
|
||||
IORESOURCE_MEM, VAFE_REGS_ADDR_RES_NAME);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "No vafe base address found.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
vafe_regbase = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (!vafe_regbase) {
|
||||
dev_err(&pdev->dev, "ioremap failed with vafe base\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* map vdec address */
|
||||
res = platform_get_resource_byname(pdev,
|
||||
IORESOURCE_MEM, VDEC_REGS_ADDR_RES_NAME);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "No vdec base address found.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
vdec_regbase = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (!vdec_regbase) {
|
||||
dev_err(&pdev->dev, "ioremap failed with vdec base\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Get clock */
|
||||
state->vadc_clk = devm_clk_get(&pdev->dev, "vadc");
|
||||
if (IS_ERR(state->vadc_clk)) {
|
||||
ret = PTR_ERR(state->vadc_clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
state->csi_clk = devm_clk_get(&pdev->dev, "csi");
|
||||
if (IS_ERR(state->csi_clk)) {
|
||||
ret = PTR_ERR(state->csi_clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* clock */
|
||||
clk_prepare_enable(state->csi_clk);
|
||||
clk_prepare_enable(state->vadc_clk);
|
||||
|
||||
platform_set_drvdata(pdev, state);
|
||||
|
||||
vadc_v4l2_subdev_init(sd, pdev, &vadc_ops);
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
/* Init VADC */
|
||||
ret = vadc_of_init(pdev);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
vadc_init(state);
|
||||
|
||||
pr_info("vadc driver loaded\n");
|
||||
|
||||
return 0;
|
||||
err:
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
v4l2_async_unregister_subdev(&state->sd);
|
||||
clk_disable_unprepare(state->csi_clk);
|
||||
clk_disable_unprepare(state->vadc_clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vadc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct vadc_state *state = platform_get_drvdata(pdev);
|
||||
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
v4l2_async_unregister_subdev(&state->sd);
|
||||
clk_disable_unprepare(state->csi_clk);
|
||||
clk_disable_unprepare(state->vadc_clk);
|
||||
|
||||
vadc_power_down(state);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int vadc_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct vadc_state *state = platform_get_drvdata(pdev);
|
||||
|
||||
clk_disable(state->csi_clk);
|
||||
clk_disable(state->vadc_clk);
|
||||
|
||||
vadc_power_down(state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vadc_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct vadc_state *state = platform_get_drvdata(pdev);
|
||||
|
||||
clk_enable(state->csi_clk);
|
||||
clk_enable(state->vadc_clk);
|
||||
|
||||
vadc_init(state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops vadc_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(vadc_suspend, vadc_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver vadc_driver = {
|
||||
.driver = {
|
||||
.name = "fsl_vadc",
|
||||
.of_match_table = of_match_ptr(fsl_vadc_dt_ids),
|
||||
.pm = &vadc_pm_ops,
|
||||
},
|
||||
.probe = vadc_probe,
|
||||
.remove = vadc_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(vadc_driver);
|
||||
|
||||
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
|
||||
MODULE_DESCRIPTION("fsl VADC/VDEC driver");
|
||||
MODULE_LICENSE("GPL");
|
239
drivers/media/platform/mxc/capture/mxc_vadc.h
Normal file
239
drivers/media/platform/mxc/capture/mxc_vadc.h
Normal file
|
@ -0,0 +1,239 @@
|
|||
/*
|
||||
* Copyright (C) 2011-2015 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MXC_VDEC_H
|
||||
#define MXC_VDEC_H
|
||||
|
||||
/*** define base address ***/
|
||||
#define VDEC_BASE vdec_regbase
|
||||
#define AFE_BASE vafe_regbase
|
||||
|
||||
/* AFE - Register offsets */
|
||||
#define AFE_BLOCK_ID_OFFSET 0x00000000
|
||||
#define AFE_PDBUF_OFFSET 0x00000004
|
||||
#define AFE_SWRST_OFFSET 0x00000008
|
||||
#define AFE_TSTSEL_OFFSET 0x0000000c
|
||||
#define AFE_TSTMSC_OFFSET 0x00000010
|
||||
#define AFE_ENPADIO_OFFSET 0x00000014
|
||||
#define AFE_BGREG_OFFSET 0x00000018
|
||||
#define AFE_ACCESSAR_ID_OFFSET 0x00000400
|
||||
#define AFE_PDADC_OFFSET 0x00000404
|
||||
#define AFE_PDSARH_OFFSET 0x00000408
|
||||
#define AFE_PDSARL_OFFSET 0x0000040C
|
||||
#define AFE_PDADCRFH_OFFSET 0x00000410
|
||||
#define AFE_PDADCRFL_OFFSET 0x00000414
|
||||
#define AFE_ACCTST_OFFSET 0x00000418
|
||||
#define AFE_ADCGN_OFFSET 0x0000041C
|
||||
#define AFE_ICTRL_OFFSET 0x00000420
|
||||
#define AFE_ICTLSTG_OFFSET 0x00000424
|
||||
#define AFE_RCTRLSTG_OFFSET 0x00000428
|
||||
#define AFE_TCTRLSTG_OFFSET 0x0000042c
|
||||
#define AFE_REFMOD_OFFSET 0x00000430
|
||||
#define AFE_REFTRIML_OFFSET 0x00000434
|
||||
#define AFE_REFTRIMH_OFFSET 0x00000438
|
||||
#define AFE_ADCR_OFFSET 0x0000043c
|
||||
#define AFE_DUMMY0_OFFSET 0x00000440
|
||||
#define AFE_DUMMY1_OFFSET 0x00000444
|
||||
#define AFE_DUMMY2_OFFSET 0x00000448
|
||||
#define AFE_DACAMP_OFFSET 0x0000044c
|
||||
#define AFE_CLMPTST_OFFSET 0x00000450
|
||||
#define AFE_CLMPDAT_OFFSET 0x00000454
|
||||
#define AFE_CLMPAMP_OFFSET 0x00000458
|
||||
#define AFE_CLAMP_OFFSET 0x0000045c
|
||||
#define AFE_INPBUF_OFFSET 0x00000460
|
||||
#define AFE_INPFLT_OFFSET 0x00000464
|
||||
#define AFE_ADCDGN_OFFSET 0x00000468
|
||||
#define AFE_OFFDRV_OFFSET 0x0000046c
|
||||
#define AFE_INPCONFIG_OFFSET 0x00000470
|
||||
#define AFE_PROGDELAY_OFFSET 0x00000474
|
||||
#define AFE_ADCOMT_OFFSET 0x00000478
|
||||
#define AFE_ALGDELAY_OFFSET 0x0000047c
|
||||
#define AFE_ACC_ID_OFFSET 0x00000800
|
||||
#define AFE_ACCSTA_OFFSET 0x00000804
|
||||
#define AFE_ACCNOSLI_OFFSET 0x00000808
|
||||
#define AFE_ACCCALCON_OFFSET 0x0000080c
|
||||
#define AFE_BWEWRICTRL_OFFSET 0x00000810
|
||||
#define AFE_SELSLI_OFFSET 0x00000814
|
||||
#define AFE_SELBYT_OFFSET 0x00000818
|
||||
#define AFE_REDVAL_OFFSET 0x00000820
|
||||
#define AFE_WRIBYT_OFFSET 0x00000824
|
||||
|
||||
/* AFE Register per module */
|
||||
#define AFE_BLOCK_ID (AFE_BASE + AFE_BLOCK_ID_OFFSET)
|
||||
#define AFE_PDBUF (AFE_BASE + AFE_PDBUF_OFFSET)
|
||||
#define AFE_SWRST (AFE_BASE + AFE_SWRST_OFFSET)
|
||||
#define AFE_TSTSEL (AFE_BASE + AFE_TSTSEL_OFFSET)
|
||||
#define AFE_TSTMSC (AFE_BASE + AFE_TSTMSC_OFFSET)
|
||||
#define AFE_ENPADIO (AFE_BASE + AFE_ENPADIO_OFFSET)
|
||||
#define AFE_BGREG (AFE_BASE + AFE_BGREG_OFFSET)
|
||||
#define AFE_ACCESSAR_ID (AFE_BASE + AFE_ACCESSAR_ID_OFFSET)
|
||||
#define AFE_PDADC (AFE_BASE + AFE_PDADC_OFFSET)
|
||||
#define AFE_PDSARH (AFE_BASE + AFE_PDSARH_OFFSET)
|
||||
#define AFE_PDSARL (AFE_BASE + AFE_PDSARL_OFFSET)
|
||||
#define AFE_PDADCRFH (AFE_BASE + AFE_PDADCRFH_OFFSET)
|
||||
#define AFE_PDADCRFL (AFE_BASE + AFE_PDADCRFL_OFFSET)
|
||||
#define AFE_ACCTST (AFE_BASE + AFE_ACCTST_OFFSET)
|
||||
#define AFE_ADCGN (AFE_BASE + AFE_ADCGN_OFFSET)
|
||||
#define AFE_ICTRL (AFE_BASE + AFE_ICTRL_OFFSET)
|
||||
#define AFE_ICTLSTG (AFE_BASE + AFE_ICTLSTG_OFFSET)
|
||||
#define AFE_RCTRLSTG (AFE_BASE + AFE_RCTRLSTG_OFFSET)
|
||||
#define AFE_TCTRLSTG (AFE_BASE + AFE_TCTRLSTG_OFFSET)
|
||||
#define AFE_REFMOD (AFE_BASE + AFE_REFMOD_OFFSET)
|
||||
#define AFE_REFTRIML (AFE_BASE + AFE_REFTRIML_OFFSET)
|
||||
#define AFE_REFTRIMH (AFE_BASE + AFE_REFTRIMH_OFFSET)
|
||||
#define AFE_ADCR (AFE_BASE + AFE_ADCR_OFFSET)
|
||||
#define AFE_DUMMY0 (AFE_BASE + AFE_DUMMY0_OFFSET)
|
||||
#define AFE_DUMMY1 (AFE_BASE + AFE_DUMMY1_OFFSET)
|
||||
#define AFE_DUMMY2 (AFE_BASE + AFE_DUMMY2_OFFSET)
|
||||
#define AFE_DACAMP (AFE_BASE + AFE_DACAMP_OFFSET)
|
||||
#define AFE_CLMPTST (AFE_BASE + AFE_CLMPTST_OFFSET)
|
||||
#define AFE_CLMPDAT (AFE_BASE + AFE_CLMPDAT_OFFSET)
|
||||
#define AFE_CLMPAMP (AFE_BASE + AFE_CLMPAMP_OFFSET)
|
||||
#define AFE_CLAMP (AFE_BASE + AFE_CLAMP_OFFSET)
|
||||
#define AFE_INPBUF (AFE_BASE + AFE_INPBUF_OFFSET)
|
||||
#define AFE_INPFLT (AFE_BASE + AFE_INPFLT_OFFSET)
|
||||
#define AFE_ADCDGN (AFE_BASE + AFE_ADCDGN_OFFSET)
|
||||
#define AFE_OFFDRV (AFE_BASE + AFE_OFFDRV_OFFSET)
|
||||
#define AFE_INPCONFIG (AFE_BASE + AFE_INPCONFIG_OFFSET)
|
||||
#define AFE_PROGDELAY (AFE_BASE + AFE_PROGDELAY_OFFSET)
|
||||
#define AFE_ADCOMT (AFE_BASE + AFE_ADCOMT_OFFSET)
|
||||
#define AFE_ALGDELAY (AFE_BASE + AFE_ALGDELAY_OFFSET)
|
||||
#define AFE_ACC_ID (AFE_BASE + AFE_ACC_ID_OFFSET)
|
||||
#define AFE_ACCSTA (AFE_BASE + AFE_ACCSTA_OFFSET)
|
||||
#define AFE_ACCNOSLI (AFE_BASE + AFE_ACCNOSLI_OFFSET)
|
||||
#define AFE_ACCCALCON (AFE_BASE + AFE_ACCCALCON_OFFSET)
|
||||
#define AFE_BWEWRICTRL (AFE_BASE + AFE_BWEWRICTRL_OFFSET)
|
||||
#define AFE_SELSLI (AFE_BASE + AFE_SELSLI_OFFSET)
|
||||
#define AFE_SELBYT (AFE_BASE + AFE_SELBYT_OFFSET)
|
||||
#define AFE_REDVAL (AFE_BASE + AFE_REDVAL_OFFSET)
|
||||
#define AFE_WRIBYT (AFE_BASE + AFE_WRIBYT_OFFSET)
|
||||
|
||||
/* VDEC - Register offsets */
|
||||
#define VDEC_CFC1_OFFSET 0x00000000
|
||||
#define VDEC_CFC2_OFFSET 0x00000004
|
||||
#define VDEC_BRSTGT_OFFSET 0x00000024
|
||||
#define VDEC_HZPOS_OFFSET 0x00000040
|
||||
#define VDEC_VRTPOS_OFFSET 0x00000044
|
||||
#define VDEC_HVSHIFT_OFFSET 0x00000054
|
||||
#define VDEC_HSIGS_OFFSET 0x00000058
|
||||
#define VDEC_HSIGE_OFFSET 0x0000005C
|
||||
#define VDEC_VSCON1_OFFSET 0x00000060
|
||||
#define VDEC_VSCON2_OFFSET 0x00000064
|
||||
#define VDEC_YCDEL_OFFSET 0x0000006C
|
||||
#define VDEC_AFTCLP_OFFSET 0x00000070
|
||||
#define VDEC_DCOFF_OFFSET 0x00000078
|
||||
#define VDEC_CSID_OFFSET 0x00000084
|
||||
#define VDEC_CBGN_OFFSET 0x00000088
|
||||
#define VDEC_CRGN_OFFSET 0x0000008C
|
||||
#define VDEC_CNTR_OFFSET 0x00000090
|
||||
#define VDEC_BRT_OFFSET 0x00000094
|
||||
#define VDEC_HUE_OFFSET 0x00000098
|
||||
#define VDEC_CHBTH_OFFSET 0x0000009C
|
||||
#define VDEC_SHPIMP_OFFSET 0x000000A4
|
||||
#define VDEC_CHPLLIM_OFFSET 0x000000A8
|
||||
#define VDEC_VIDMOD_OFFSET 0x000000AC
|
||||
#define VDEC_VIDSTS_OFFSET 0x000000B0
|
||||
#define VDEC_NOISE_OFFSET 0x000000B4
|
||||
#define VDEC_STDDBG_OFFSET 0x000000B8
|
||||
#define VDEC_MANOVR_OFFSET 0x000000BC
|
||||
#define VDEC_VSSGTH_OFFSET 0x000000C8
|
||||
#define VDEC_DBGFBH_OFFSET 0x000000D0
|
||||
#define VDEC_DBGFBL_OFFSET 0x000000D4
|
||||
#define VDEC_HACTS_OFFSET 0x000000D8
|
||||
#define VDEC_HACTE_OFFSET 0x000000DC
|
||||
#define VDEC_VACTS_OFFSET 0x000000E0
|
||||
#define VDEC_VACTE_OFFSET 0x000000E4
|
||||
#define VDEC_HSTIP_OFFSET 0x000000EC
|
||||
#define VDEC_BLSCRY_OFFSET 0x000000F4
|
||||
#define VDEC_BLSCRCR_OFFSET 0x000000F8
|
||||
#define VDEC_BLSCRCB_OFFSET 0x000000FC
|
||||
#define VDEC_LMAGC1_OFFSET 0x00000100
|
||||
#define VDEC_LMAGC2_OFFSET 0x00000104
|
||||
#define VDEC_CHAGC1_OFFSET 0x00000108
|
||||
#define VDEC_CHAGC2_OFFSET 0x0000010C
|
||||
#define VDEC_MINTH_OFFSET 0x00000114
|
||||
#define VDEC_VFRQOH_OFFSET 0x0000011C
|
||||
#define VDEC_VFRQOL_OFFSET 0x00000120
|
||||
#define VDEC_THSH1_OFFSET 0x00000124
|
||||
#define VDEC_THSH2_OFFSET 0x00000128
|
||||
#define VDEC_NCHTH_OFFSET 0x0000012C
|
||||
#define VDEC_TH1F_OFFSET 0x00000130
|
||||
|
||||
/* VDEC Register per module */
|
||||
#define VDEC_CFC1 (VDEC_BASE + VDEC_CFC1_OFFSET)
|
||||
#define VDEC_CFC2 (VDEC_BASE + VDEC_CFC2_OFFSET)
|
||||
#define VDEC_BRSTGT (VDEC_BASE + VDEC_BRSTGT_OFFSET)
|
||||
#define VDEC_HZPOS (VDEC_BASE + VDEC_HZPOS_OFFSET)
|
||||
#define VDEC_VRTPOS (VDEC_BASE + VDEC_VRTPOS_OFFSET)
|
||||
#define VDEC_HVSHIFT (VDEC_BASE + VDEC_HVSHIFT_OFFSET)
|
||||
#define VDEC_HSIGS (VDEC_BASE + VDEC_HSIGS_OFFSET)
|
||||
#define VDEC_HSIGE (VDEC_BASE + VDEC_HSIGE_OFFSET)
|
||||
#define VDEC_VSCON1 (VDEC_BASE + VDEC_VSCON1_OFFSET)
|
||||
#define VDEC_VSCON2 (VDEC_BASE + VDEC_VSCON2_OFFSET)
|
||||
#define VDEC_YCDEL (VDEC_BASE + VDEC_YCDEL_OFFSET)
|
||||
#define VDEC_AFTCLP (VDEC_BASE + VDEC_AFTCLP_OFFSET)
|
||||
#define VDEC_DCOFF (VDEC_BASE + VDEC_DCOFF_OFFSET)
|
||||
#define VDEC_CSID (VDEC_BASE + VDEC_CSID_OFFSET)
|
||||
#define VDEC_CBGN (VDEC_BASE + VDEC_CBGN_OFFSET)
|
||||
#define VDEC_CRGN (VDEC_BASE + VDEC_CRGN_OFFSET)
|
||||
#define VDEC_CNTR (VDEC_BASE + VDEC_CNTR_OFFSET)
|
||||
#define VDEC_BRT (VDEC_BASE + VDEC_BRT_OFFSET)
|
||||
#define VDEC_HUE (VDEC_BASE + VDEC_HUE_OFFSET)
|
||||
#define VDEC_CHBTH (VDEC_BASE + VDEC_CHBTH_OFFSET)
|
||||
#define VDEC_SHPIMP (VDEC_BASE + VDEC_SHPIMP_OFFSET)
|
||||
#define VDEC_CHPLLIM (VDEC_BASE + VDEC_CHPLLIM_OFFSET)
|
||||
#define VDEC_VIDMOD (VDEC_BASE + VDEC_VIDMOD_OFFSET)
|
||||
#define VDEC_VIDSTS (VDEC_BASE + VDEC_VIDSTS_OFFSET)
|
||||
#define VDEC_NOISE (VDEC_BASE + VDEC_NOISE_OFFSET)
|
||||
#define VDEC_STDDBG (VDEC_BASE + VDEC_STDDBG_OFFSET)
|
||||
#define VDEC_MANOVR (VDEC_BASE + VDEC_MANOVR_OFFSET)
|
||||
#define VDEC_VSSGTH (VDEC_BASE + VDEC_VSSGTH_OFFSET)
|
||||
#define VDEC_DBGFBH (VDEC_BASE + VDEC_DBGFBH_OFFSET)
|
||||
#define VDEC_DBGFBL (VDEC_BASE + VDEC_DBGFBL_OFFSET)
|
||||
#define VDEC_HACTS (VDEC_BASE + VDEC_HACTS_OFFSET)
|
||||
#define VDEC_HACTE (VDEC_BASE + VDEC_HACTE_OFFSET)
|
||||
#define VDEC_VACTS (VDEC_BASE + VDEC_VACTS_OFFSET)
|
||||
#define VDEC_VACTE (VDEC_BASE + VDEC_VACTE_OFFSET)
|
||||
#define VDEC_HSTIP (VDEC_BASE + VDEC_HSTIP_OFFSET)
|
||||
#define VDEC_BLSCRY (VDEC_BASE + VDEC_BLSCRY_OFFSET)
|
||||
#define VDEC_BLSCRCR (VDEC_BASE + VDEC_BLSCRCR_OFFSET)
|
||||
#define VDEC_BLSCRCB (VDEC_BASE + VDEC_BLSCRCB_OFFSET)
|
||||
#define VDEC_LMAGC1 (VDEC_BASE + VDEC_LMAGC1_OFFSET)
|
||||
#define VDEC_LMAGC2 (VDEC_BASE + VDEC_LMAGC2_OFFSET)
|
||||
#define VDEC_CHAGC1 (VDEC_BASE + VDEC_CHAGC1_OFFSET)
|
||||
#define VDEC_CHAGC2 (VDEC_BASE + VDEC_CHAGC2_OFFSET)
|
||||
#define VDEC_MINTH (VDEC_BASE + VDEC_MINTH_OFFSET)
|
||||
#define VDEC_VFRQOH (VDEC_BASE + VDEC_VFRQOH_OFFSET)
|
||||
#define VDEC_VFRQOL (VDEC_BASE + VDEC_VFRQOL_OFFSET)
|
||||
#define VDEC_THSH1 (VDEC_BASE + VDEC_THSH1_OFFSET)
|
||||
#define VDEC_THSH2 (VDEC_BASE + VDEC_THSH2_OFFSET)
|
||||
#define VDEC_NCHTH (VDEC_BASE + VDEC_NCHTH_OFFSET)
|
||||
#define VDEC_TH1F (VDEC_BASE + VDEC_TH1F_OFFSET)
|
||||
|
||||
#define VDEC_VIDMOD_SIGNAL_MASK 0x0F
|
||||
#define VDEC_VIDMOD_SIGNAL_DETECT 0x0F
|
||||
|
||||
#define VDEC_VIDMOD_M625_SHIFT 4
|
||||
#define VDEC_VIDMOD_M625_MASK (1 << VDEC_VIDMOD_M625_SHIFT)
|
||||
|
||||
#define VDEC_VIDMOD_PAL_SHIFT 7
|
||||
#define VDEC_VIDMOD_PAL_MASK (1 << VDEC_VIDMOD_PAL_SHIFT)
|
||||
/*** define base address ***/
|
||||
|
||||
#endif
|
1965
drivers/media/platform/mxc/capture/ov5640.c
Normal file
1965
drivers/media/platform/mxc/capture/ov5640.c
Normal file
File diff suppressed because it is too large
Load Diff
2157
drivers/media/platform/mxc/capture/ov5640_mipi.c
Normal file
2157
drivers/media/platform/mxc/capture/ov5640_mipi.c
Normal file
File diff suppressed because it is too large
Load Diff
1832
drivers/media/platform/mxc/capture/ov5640_mipi_v2.c
Normal file
1832
drivers/media/platform/mxc/capture/ov5640_mipi_v2.c
Normal file
File diff suppressed because it is too large
Load Diff
1917
drivers/media/platform/mxc/capture/ov5640_v2.c
Normal file
1917
drivers/media/platform/mxc/capture/ov5640_v2.c
Normal file
File diff suppressed because it is too large
Load Diff
1714
drivers/media/platform/mxc/capture/ov5647_mipi.c
Normal file
1714
drivers/media/platform/mxc/capture/ov5647_mipi.c
Normal file
File diff suppressed because it is too large
Load Diff
152
drivers/media/platform/mxc/capture/v4l2-int-device.c
Normal file
152
drivers/media/platform/mxc/capture/v4l2-int-device.c
Normal file
|
@ -0,0 +1,152 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* drivers/media/video/v4l2-int-device.c
|
||||
*
|
||||
* V4L2 internal ioctl interface.
|
||||
*
|
||||
* Copyright 2005-2014 Freescale Semiconductor, Inc.
|
||||
* Copyright (C) 2007 Nokia Corporation.
|
||||
*
|
||||
* Contact: Sakari Ailus <sakari.ailus@nokia.com>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/sort.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "v4l2-int-device.h"
|
||||
|
||||
static DEFINE_MUTEX(mutex);
|
||||
static LIST_HEAD(int_list);
|
||||
|
||||
void v4l2_int_device_try_attach_all(void)
|
||||
{
|
||||
struct v4l2_int_device *m, *s;
|
||||
|
||||
list_for_each_entry(m, &int_list, head) {
|
||||
if (m->type != v4l2_int_type_master)
|
||||
continue;
|
||||
|
||||
list_for_each_entry(s, &int_list, head) {
|
||||
if (s->type != v4l2_int_type_slave)
|
||||
continue;
|
||||
|
||||
/* Slave is connected? */
|
||||
if (s->u.slave->master)
|
||||
continue;
|
||||
|
||||
/* Slave wants to attach to master? */
|
||||
if (s->u.slave->attach_to[0] != 0
|
||||
&& strncmp(m->name, s->u.slave->attach_to,
|
||||
V4L2NAMESIZE))
|
||||
continue;
|
||||
|
||||
if (!try_module_get(m->module))
|
||||
continue;
|
||||
|
||||
s->u.slave->master = m;
|
||||
if (m->u.master->attach(s)) {
|
||||
s->u.slave->master = NULL;
|
||||
module_put(m->module);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(v4l2_int_device_try_attach_all);
|
||||
|
||||
static int ioctl_sort_cmp(const void *a, const void *b)
|
||||
{
|
||||
const struct v4l2_int_ioctl_desc *d1 = a, *d2 = b;
|
||||
|
||||
if (d1->num > d2->num)
|
||||
return 1;
|
||||
|
||||
if (d1->num < d2->num)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int v4l2_int_device_register(struct v4l2_int_device *d)
|
||||
{
|
||||
if (d->type == v4l2_int_type_slave)
|
||||
sort(d->u.slave->ioctls, d->u.slave->num_ioctls,
|
||||
sizeof(struct v4l2_int_ioctl_desc),
|
||||
&ioctl_sort_cmp, NULL);
|
||||
mutex_lock(&mutex);
|
||||
list_add(&d->head, &int_list);
|
||||
v4l2_int_device_try_attach_all();
|
||||
mutex_unlock(&mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(v4l2_int_device_register);
|
||||
|
||||
void v4l2_int_device_unregister(struct v4l2_int_device *d)
|
||||
{
|
||||
mutex_lock(&mutex);
|
||||
list_del(&d->head);
|
||||
if (d->type == v4l2_int_type_slave
|
||||
&& d->u.slave->master != NULL) {
|
||||
d->u.slave->master->u.master->detach(d);
|
||||
module_put(d->u.slave->master->module);
|
||||
d->u.slave->master = NULL;
|
||||
}
|
||||
mutex_unlock(&mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(v4l2_int_device_unregister);
|
||||
|
||||
/* Adapted from search_extable in extable.c. */
|
||||
static v4l2_int_ioctl_func *find_ioctl(struct v4l2_int_slave *slave, int cmd,
|
||||
v4l2_int_ioctl_func *no_such_ioctl)
|
||||
{
|
||||
const struct v4l2_int_ioctl_desc *first = slave->ioctls;
|
||||
const struct v4l2_int_ioctl_desc *last =
|
||||
first + slave->num_ioctls - 1;
|
||||
|
||||
while (first <= last) {
|
||||
const struct v4l2_int_ioctl_desc *mid;
|
||||
|
||||
mid = (last - first) / 2 + first;
|
||||
|
||||
if (mid->num < cmd)
|
||||
first = mid + 1;
|
||||
else if (mid->num > cmd)
|
||||
last = mid - 1;
|
||||
else
|
||||
return mid->func;
|
||||
}
|
||||
|
||||
return no_such_ioctl;
|
||||
}
|
||||
|
||||
static int no_such_ioctl_0(struct v4l2_int_device *d)
|
||||
{
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
|
||||
int v4l2_int_ioctl_0(struct v4l2_int_device *d, int cmd)
|
||||
{
|
||||
return ((v4l2_int_ioctl_func_0 *)
|
||||
find_ioctl(d->u.slave, cmd,
|
||||
(v4l2_int_ioctl_func *)no_such_ioctl_0))(d);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(v4l2_int_ioctl_0);
|
||||
|
||||
static int no_such_ioctl_1(struct v4l2_int_device *d, void *arg)
|
||||
{
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
|
||||
int v4l2_int_ioctl_1(struct v4l2_int_device *d, int cmd, void *arg)
|
||||
{
|
||||
return ((v4l2_int_ioctl_func_1 *)
|
||||
find_ioctl(d->u.slave, cmd,
|
||||
(v4l2_int_ioctl_func *)no_such_ioctl_1))(d, arg);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(v4l2_int_ioctl_1);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
309
drivers/media/platform/mxc/capture/v4l2-int-device.h
Normal file
309
drivers/media/platform/mxc/capture/v4l2-int-device.h
Normal file
|
@ -0,0 +1,309 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* include/media/v4l2-int-device.h
|
||||
*
|
||||
* V4L2 internal ioctl interface.
|
||||
*
|
||||
* Copyright 2005-2014 Freescale Semiconductor, Inc.
|
||||
* Copyright (C) 2007 Nokia Corporation.
|
||||
*
|
||||
* Contact: Sakari Ailus <sakari.ailus@nokia.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef V4L2_INT_DEVICE_H
|
||||
#define V4L2_INT_DEVICE_H
|
||||
|
||||
#include <media/v4l2-common.h>
|
||||
|
||||
#define V4L2NAMESIZE 32
|
||||
|
||||
/*
|
||||
*
|
||||
* The internal V4L2 device interface core.
|
||||
*
|
||||
*/
|
||||
|
||||
enum v4l2_int_type {
|
||||
v4l2_int_type_master = 1,
|
||||
v4l2_int_type_slave
|
||||
};
|
||||
|
||||
struct module;
|
||||
|
||||
struct v4l2_int_device;
|
||||
|
||||
struct v4l2_int_master {
|
||||
int (*attach)(struct v4l2_int_device *slave);
|
||||
void (*detach)(struct v4l2_int_device *slave);
|
||||
};
|
||||
|
||||
typedef int (v4l2_int_ioctl_func)(struct v4l2_int_device *);
|
||||
typedef int (v4l2_int_ioctl_func_0)(struct v4l2_int_device *);
|
||||
typedef int (v4l2_int_ioctl_func_1)(struct v4l2_int_device *, void *);
|
||||
|
||||
struct v4l2_int_ioctl_desc {
|
||||
int num;
|
||||
v4l2_int_ioctl_func *func;
|
||||
};
|
||||
|
||||
struct v4l2_int_slave {
|
||||
/* Don't touch master. */
|
||||
struct v4l2_int_device *master;
|
||||
|
||||
char attach_to[V4L2NAMESIZE];
|
||||
|
||||
int num_ioctls;
|
||||
struct v4l2_int_ioctl_desc *ioctls;
|
||||
};
|
||||
|
||||
struct v4l2_int_device {
|
||||
/* Don't touch head. */
|
||||
struct list_head head;
|
||||
|
||||
struct module *module;
|
||||
|
||||
char name[V4L2NAMESIZE];
|
||||
|
||||
enum v4l2_int_type type;
|
||||
union {
|
||||
struct v4l2_int_master *master;
|
||||
struct v4l2_int_slave *slave;
|
||||
} u;
|
||||
|
||||
void *priv;
|
||||
};
|
||||
|
||||
void v4l2_int_device_try_attach_all(void);
|
||||
|
||||
int v4l2_int_device_register(struct v4l2_int_device *d);
|
||||
void v4l2_int_device_unregister(struct v4l2_int_device *d);
|
||||
|
||||
int v4l2_int_ioctl_0(struct v4l2_int_device *d, int cmd);
|
||||
int v4l2_int_ioctl_1(struct v4l2_int_device *d, int cmd, void *arg);
|
||||
|
||||
/*
|
||||
*
|
||||
* Types and definitions for IOCTL commands.
|
||||
*
|
||||
*/
|
||||
|
||||
enum v4l2_power {
|
||||
V4L2_POWER_OFF = 0,
|
||||
V4L2_POWER_ON,
|
||||
V4L2_POWER_STANDBY,
|
||||
};
|
||||
|
||||
/* Slave interface type. */
|
||||
enum v4l2_if_type {
|
||||
/*
|
||||
* Parallel 8-, 10- or 12-bit interface, used by for example
|
||||
* on certain image sensors.
|
||||
*/
|
||||
V4L2_IF_TYPE_BT656,
|
||||
};
|
||||
|
||||
enum v4l2_if_type_bt656_mode {
|
||||
/*
|
||||
* Modes without Bt synchronisation codes. Separate
|
||||
* synchronisation signal lines are used.
|
||||
*/
|
||||
V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT,
|
||||
V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT,
|
||||
V4L2_IF_TYPE_BT656_MODE_NOBT_12BIT,
|
||||
/*
|
||||
* Use Bt synchronisation codes. The vertical and horizontal
|
||||
* synchronisation is done based on synchronisation codes.
|
||||
*/
|
||||
V4L2_IF_TYPE_BT656_MODE_BT_8BIT,
|
||||
V4L2_IF_TYPE_BT656_MODE_BT_10BIT,
|
||||
};
|
||||
|
||||
struct v4l2_if_type_bt656 {
|
||||
/*
|
||||
* 0: Frame begins when vsync is high.
|
||||
* 1: Frame begins when vsync changes from low to high.
|
||||
*/
|
||||
unsigned frame_start_on_rising_vs:1;
|
||||
/* Use Bt synchronisation codes for sync correction. */
|
||||
unsigned bt_sync_correct:1;
|
||||
/* Swap every two adjacent image data elements. */
|
||||
unsigned swap:1;
|
||||
/* Inverted latch clock polarity from slave. */
|
||||
unsigned latch_clk_inv:1;
|
||||
/* Hs polarity. 0 is active high, 1 active low. */
|
||||
unsigned nobt_hs_inv:1;
|
||||
/* Vs polarity. 0 is active high, 1 active low. */
|
||||
unsigned nobt_vs_inv:1;
|
||||
enum v4l2_if_type_bt656_mode mode;
|
||||
/* Minimum accepted bus clock for slave (in Hz). */
|
||||
u32 clock_min;
|
||||
/* Maximum accepted bus clock for slave. */
|
||||
u32 clock_max;
|
||||
/*
|
||||
* Current wish of the slave. May only change in response to
|
||||
* ioctls that affect image capture.
|
||||
*/
|
||||
u32 clock_curr;
|
||||
};
|
||||
|
||||
struct v4l2_ifparm {
|
||||
enum v4l2_if_type if_type;
|
||||
union {
|
||||
struct v4l2_if_type_bt656 bt656;
|
||||
} u;
|
||||
};
|
||||
|
||||
/* IOCTL command numbers. */
|
||||
enum v4l2_int_ioctl_num {
|
||||
/*
|
||||
*
|
||||
* "Proper" V4L ioctls, as in struct video_device.
|
||||
*
|
||||
*/
|
||||
vidioc_int_enum_fmt_cap_num = 1,
|
||||
vidioc_int_g_fmt_cap_num,
|
||||
vidioc_int_s_fmt_cap_num,
|
||||
vidioc_int_try_fmt_cap_num,
|
||||
vidioc_int_queryctrl_num,
|
||||
vidioc_int_g_ctrl_num,
|
||||
vidioc_int_s_ctrl_num,
|
||||
vidioc_int_cropcap_num,
|
||||
vidioc_int_g_crop_num,
|
||||
vidioc_int_s_crop_num,
|
||||
vidioc_int_g_parm_num,
|
||||
vidioc_int_s_parm_num,
|
||||
vidioc_int_querystd_num,
|
||||
vidioc_int_s_std_num,
|
||||
vidioc_int_s_video_routing_num,
|
||||
|
||||
/*
|
||||
*
|
||||
* Strictly internal ioctls.
|
||||
*
|
||||
*/
|
||||
/* Initialise the device when slave attaches to the master. */
|
||||
vidioc_int_dev_init_num = 1000,
|
||||
/* Delinitialise the device at slave detach. */
|
||||
vidioc_int_dev_exit_num,
|
||||
/* Set device power state. */
|
||||
vidioc_int_s_power_num,
|
||||
/*
|
||||
* Get slave private data, e.g. platform-specific slave
|
||||
* configuration used by the master.
|
||||
*/
|
||||
vidioc_int_g_priv_num,
|
||||
/* Get slave interface parameters. */
|
||||
vidioc_int_g_ifparm_num,
|
||||
/* Does the slave need to be reset after VIDIOC_DQBUF? */
|
||||
vidioc_int_g_needs_reset_num,
|
||||
vidioc_int_enum_framesizes_num,
|
||||
vidioc_int_enum_frameintervals_num,
|
||||
|
||||
/*
|
||||
*
|
||||
* VIDIOC_INT_* ioctls.
|
||||
*
|
||||
*/
|
||||
/* VIDIOC_INT_RESET */
|
||||
vidioc_int_reset_num,
|
||||
/* VIDIOC_INT_INIT */
|
||||
vidioc_int_init_num,
|
||||
/* VIDIOC_DBG_G_CHIP_IDENT */
|
||||
vidioc_int_g_chip_ident_num,
|
||||
|
||||
/*
|
||||
*
|
||||
* Start of private ioctls.
|
||||
*
|
||||
*/
|
||||
vidioc_int_priv_start_num = 2000,
|
||||
};
|
||||
|
||||
/*
|
||||
*
|
||||
* IOCTL wrapper functions for better type checking.
|
||||
*
|
||||
*/
|
||||
|
||||
#define V4L2_INT_WRAPPER_0(name) \
|
||||
static inline int vidioc_int_##name(struct v4l2_int_device *d) \
|
||||
{ \
|
||||
return v4l2_int_ioctl_0(d, vidioc_int_##name##_num); \
|
||||
} \
|
||||
\
|
||||
static inline struct v4l2_int_ioctl_desc \
|
||||
vidioc_int_##name##_cb(int (*func) \
|
||||
(struct v4l2_int_device *)) \
|
||||
{ \
|
||||
struct v4l2_int_ioctl_desc desc; \
|
||||
\
|
||||
desc.num = vidioc_int_##name##_num; \
|
||||
desc.func = (v4l2_int_ioctl_func *)func; \
|
||||
\
|
||||
return desc; \
|
||||
}
|
||||
|
||||
#define V4L2_INT_WRAPPER_1(name, arg_type, asterisk) \
|
||||
static inline int vidioc_int_##name(struct v4l2_int_device *d, \
|
||||
arg_type asterisk arg) \
|
||||
{ \
|
||||
return v4l2_int_ioctl_1(d, vidioc_int_##name##_num, \
|
||||
(void *)(unsigned long)arg); \
|
||||
} \
|
||||
\
|
||||
static inline struct v4l2_int_ioctl_desc \
|
||||
vidioc_int_##name##_cb(int (*func) \
|
||||
(struct v4l2_int_device *, \
|
||||
arg_type asterisk)) \
|
||||
{ \
|
||||
struct v4l2_int_ioctl_desc desc; \
|
||||
\
|
||||
desc.num = vidioc_int_##name##_num; \
|
||||
desc.func = (v4l2_int_ioctl_func *)func; \
|
||||
\
|
||||
return desc; \
|
||||
}
|
||||
|
||||
V4L2_INT_WRAPPER_1(enum_fmt_cap, struct v4l2_fmtdesc, *);
|
||||
V4L2_INT_WRAPPER_1(g_fmt_cap, struct v4l2_format, *);
|
||||
V4L2_INT_WRAPPER_1(s_fmt_cap, struct v4l2_format, *);
|
||||
V4L2_INT_WRAPPER_1(try_fmt_cap, struct v4l2_format, *);
|
||||
V4L2_INT_WRAPPER_1(queryctrl, struct v4l2_queryctrl, *);
|
||||
V4L2_INT_WRAPPER_1(g_ctrl, struct v4l2_control, *);
|
||||
V4L2_INT_WRAPPER_1(s_ctrl, struct v4l2_control, *);
|
||||
V4L2_INT_WRAPPER_1(cropcap, struct v4l2_cropcap, *);
|
||||
V4L2_INT_WRAPPER_1(g_crop, struct v4l2_crop, *);
|
||||
V4L2_INT_WRAPPER_1(s_crop, struct v4l2_crop, *);
|
||||
V4L2_INT_WRAPPER_1(g_parm, struct v4l2_streamparm, *);
|
||||
V4L2_INT_WRAPPER_1(s_parm, struct v4l2_streamparm, *);
|
||||
V4L2_INT_WRAPPER_1(querystd, v4l2_std_id, *);
|
||||
V4L2_INT_WRAPPER_1(s_std, v4l2_std_id, *);
|
||||
|
||||
V4L2_INT_WRAPPER_0(dev_init);
|
||||
V4L2_INT_WRAPPER_0(dev_exit);
|
||||
V4L2_INT_WRAPPER_1(s_power, enum v4l2_power, /*dummy arg*/);
|
||||
V4L2_INT_WRAPPER_1(g_priv, void, *);
|
||||
V4L2_INT_WRAPPER_1(g_ifparm, struct v4l2_ifparm, *);
|
||||
V4L2_INT_WRAPPER_1(g_needs_reset, void, *);
|
||||
V4L2_INT_WRAPPER_1(enum_framesizes, struct v4l2_frmsizeenum, *);
|
||||
V4L2_INT_WRAPPER_1(enum_frameintervals, struct v4l2_frmivalenum, *);
|
||||
|
||||
V4L2_INT_WRAPPER_0(reset);
|
||||
V4L2_INT_WRAPPER_0(init);
|
||||
V4L2_INT_WRAPPER_1(g_chip_ident, int, *);
|
||||
|
||||
#endif
|
|
@ -579,6 +579,7 @@ static void determine_valid_ioctls(struct video_device *vdev)
|
|||
set_bit(_IOC_NR(VIDIOC_DBG_G_REGISTER), valid_ioctls);
|
||||
set_bit(_IOC_NR(VIDIOC_DBG_S_REGISTER), valid_ioctls);
|
||||
#endif
|
||||
SET_VALID_IOCTL(ops, VIDIOC_DBG_G_CHIP_IDENT, vidioc_g_chip_ident);
|
||||
/* yes, really vidioc_subscribe_event */
|
||||
SET_VALID_IOCTL(ops, VIDIOC_DQEVENT, vidioc_subscribe_event);
|
||||
SET_VALID_IOCTL(ops, VIDIOC_SUBSCRIBE_EVENT, vidioc_subscribe_event);
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
#include <media/videobuf2-v4l2.h>
|
||||
#include <media/v4l2-mc.h>
|
||||
#include <media/v4l2-mem2mem.h>
|
||||
#include <media/v4l2-chip-ident.h>
|
||||
#include <media/videobuf2-core.h>
|
||||
|
||||
#include <trace/events/v4l2.h>
|
||||
|
||||
|
@ -674,6 +676,20 @@ static void v4l_print_decoder_cmd(const void *arg, bool write_only)
|
|||
pr_info("pts=%llu\n", p->stop.pts);
|
||||
}
|
||||
|
||||
static void v4l_print_dbg_chip_ident(const void *arg, bool write_only)
|
||||
{
|
||||
const struct v4l2_dbg_chip_ident *p = arg;
|
||||
|
||||
pr_cont("type=%u, ", p->match.type);
|
||||
if (p->match.type == V4L2_CHIP_MATCH_I2C_DRIVER)
|
||||
pr_cont("name=%.*s, ",
|
||||
(int)sizeof(p->match.name), p->match.name);
|
||||
else
|
||||
pr_cont("addr=%u, ", p->match.addr);
|
||||
pr_cont("chip_ident=%u, revision=0x%x\n",
|
||||
p->ident, p->revision);
|
||||
}
|
||||
|
||||
static void v4l_print_dbg_chip_info(const void *arg, bool write_only)
|
||||
{
|
||||
const struct v4l2_dbg_chip_info *p = arg;
|
||||
|
@ -1236,6 +1252,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
|
|||
case V4L2_PIX_FMT_YUV444: descr = "16-bit A/XYUV 4-4-4-4"; break;
|
||||
case V4L2_PIX_FMT_YUV555: descr = "16-bit A/XYUV 1-5-5-5"; break;
|
||||
case V4L2_PIX_FMT_YUV565: descr = "16-bit YUV 5-6-5"; break;
|
||||
case V4L2_PIX_FMT_YUV24: descr = "24-bit YUV 4:4:4 8-8-8"; break;
|
||||
case V4L2_PIX_FMT_YUV32: descr = "32-bit A/XYUV 8-8-8-8"; break;
|
||||
case V4L2_PIX_FMT_AYUV32: descr = "32-bit AYUV 8-8-8-8"; break;
|
||||
case V4L2_PIX_FMT_XYUV32: descr = "32-bit XYUV 8-8-8-8"; break;
|
||||
|
@ -2049,22 +2066,7 @@ static int v4l_s_parm(const struct v4l2_ioctl_ops *ops,
|
|||
struct v4l2_streamparm *p = arg;
|
||||
int ret = check_fmt(file, p->type);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Note: extendedmode is never used in drivers */
|
||||
if (V4L2_TYPE_IS_OUTPUT(p->type)) {
|
||||
memset(p->parm.output.reserved, 0,
|
||||
sizeof(p->parm.output.reserved));
|
||||
p->parm.output.extendedmode = 0;
|
||||
p->parm.output.outputmode &= V4L2_MODE_HIGHQUALITY;
|
||||
} else {
|
||||
memset(p->parm.capture.reserved, 0,
|
||||
sizeof(p->parm.capture.reserved));
|
||||
p->parm.capture.extendedmode = 0;
|
||||
p->parm.capture.capturemode &= V4L2_MODE_HIGHQUALITY;
|
||||
}
|
||||
return ops->vidioc_s_parm(file, fh, p);
|
||||
return ret ? ret : ops->vidioc_s_parm(file, fh, p);
|
||||
}
|
||||
|
||||
static int v4l_queryctrl(const struct v4l2_ioctl_ops *ops,
|
||||
|
@ -2478,6 +2480,18 @@ static int v4l_dbg_s_register(const struct v4l2_ioctl_ops *ops,
|
|||
#endif
|
||||
}
|
||||
|
||||
static int v4l_dbg_g_chip_ident(const struct v4l2_ioctl_ops *ops,
|
||||
struct file *file, void *fh, void *arg)
|
||||
{
|
||||
struct v4l2_dbg_chip_ident *p = arg;
|
||||
|
||||
p->ident = V4L2_IDENT_NONE;
|
||||
p->revision = 0;
|
||||
if (p->match.type == V4L2_CHIP_MATCH_SUBDEV)
|
||||
return -EINVAL;
|
||||
return ops->vidioc_g_chip_ident(file, fh, p);
|
||||
}
|
||||
|
||||
static int v4l_dbg_g_chip_info(const struct v4l2_ioctl_ops *ops,
|
||||
struct file *file, void *fh, void *arg)
|
||||
{
|
||||
|
@ -2755,6 +2769,7 @@ static const struct v4l2_ioctl_info v4l2_ioctls[] = {
|
|||
IOCTL_INFO(VIDIOC_TRY_DECODER_CMD, v4l_stub_try_decoder_cmd, v4l_print_decoder_cmd, 0),
|
||||
IOCTL_INFO(VIDIOC_DBG_S_REGISTER, v4l_dbg_s_register, v4l_print_dbg_register, 0),
|
||||
IOCTL_INFO(VIDIOC_DBG_G_REGISTER, v4l_dbg_g_register, v4l_print_dbg_register, 0),
|
||||
IOCTL_INFO(VIDIOC_DBG_G_CHIP_IDENT, v4l_dbg_g_chip_ident, v4l_print_dbg_chip_ident, 0),
|
||||
IOCTL_INFO(VIDIOC_S_HW_FREQ_SEEK, v4l_s_hw_freq_seek, v4l_print_hw_freq_seek, INFO_FL_PRIO),
|
||||
IOCTL_INFO(VIDIOC_S_DV_TIMINGS, v4l_stub_s_dv_timings, v4l_print_dv_timings, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_dv_timings, bt.flags)),
|
||||
IOCTL_INFO(VIDIOC_G_DV_TIMINGS, v4l_stub_g_dv_timings, v4l_print_dv_timings, 0),
|
||||
|
@ -3129,6 +3144,7 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
|
|||
kvfree(mbuf);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(video_usercopy);
|
||||
|
||||
long video_ioctl2(struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
|
|
1
drivers/mxc/Kconfig
Normal file
1
drivers/mxc/Kconfig
Normal file
|
@ -0,0 +1 @@
|
|||
source "drivers/mxc/mipi/Kconfig"
|
1
drivers/mxc/Makefile
Normal file
1
drivers/mxc/Makefile
Normal file
|
@ -0,0 +1 @@
|
|||
obj-$(CONFIG_MXC_MIPI_CSI2) += mipi/
|
14
drivers/mxc/mipi/Kconfig
Normal file
14
drivers/mxc/mipi/Kconfig
Normal file
|
@ -0,0 +1,14 @@
|
|||
#
|
||||
# MIPI configuration
|
||||
#
|
||||
|
||||
menu "MXC MIPI Support"
|
||||
|
||||
config MXC_MIPI_CSI2
|
||||
tristate "MIPI CSI2 support"
|
||||
depends on (SOC_IMX6 || SOC_IMX7D)
|
||||
default n
|
||||
---help---
|
||||
Say Y to get the MIPI CSI2 support.
|
||||
|
||||
endmenu
|
4
drivers/mxc/mipi/Makefile
Normal file
4
drivers/mxc/mipi/Makefile
Normal file
|
@ -0,0 +1,4 @@
|
|||
#
|
||||
# Makefile for the mipi interface driver
|
||||
#
|
||||
obj-$(CONFIG_MXC_MIPI_CSI2) += mxc_mipi_csi2.o
|
528
drivers/mxc/mipi/mxc_mipi_csi2.c
Normal file
528
drivers/mxc/mipi/mxc_mipi_csi2.c
Normal file
|
@ -0,0 +1,528 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2011-2014 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
* Copyright 2019 NXP
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdesc.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/fsl_devices.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <linux/mipi_csi2.h>
|
||||
|
||||
#include "mxc_mipi_csi2.h"
|
||||
|
||||
static struct mipi_csi2_info *gmipi_csi2;
|
||||
|
||||
void _mipi_csi2_lock(struct mipi_csi2_info *info)
|
||||
{
|
||||
if (!in_irq() && !in_softirq())
|
||||
mutex_lock(&info->mutex_lock);
|
||||
}
|
||||
|
||||
void _mipi_csi2_unlock(struct mipi_csi2_info *info)
|
||||
{
|
||||
if (!in_irq() && !in_softirq())
|
||||
mutex_unlock(&info->mutex_lock);
|
||||
}
|
||||
|
||||
static inline void mipi_csi2_write(struct mipi_csi2_info *info,
|
||||
unsigned value, unsigned offset)
|
||||
{
|
||||
writel(value, info->mipi_csi2_base + offset);
|
||||
}
|
||||
|
||||
static inline unsigned int mipi_csi2_read(struct mipi_csi2_info *info,
|
||||
unsigned offset)
|
||||
{
|
||||
return readl(info->mipi_csi2_base + offset);
|
||||
}
|
||||
|
||||
/*!
|
||||
* This function is called to enable the mipi csi2 interface.
|
||||
*
|
||||
* @param info mipi csi2 hander
|
||||
* @return Returns setted value
|
||||
*/
|
||||
bool mipi_csi2_enable(struct mipi_csi2_info *info)
|
||||
{
|
||||
bool status;
|
||||
|
||||
_mipi_csi2_lock(info);
|
||||
|
||||
if (!info->mipi_en) {
|
||||
info->mipi_en = true;
|
||||
clk_prepare_enable(info->cfg_clk);
|
||||
clk_prepare_enable(info->dphy_clk);
|
||||
} else
|
||||
mipi_dbg("mipi csi2 already enabled!\n");
|
||||
|
||||
status = info->mipi_en;
|
||||
|
||||
_mipi_csi2_unlock(info);
|
||||
|
||||
return status;
|
||||
}
|
||||
EXPORT_SYMBOL(mipi_csi2_enable);
|
||||
|
||||
/*!
|
||||
* This function is called to disable the mipi csi2 interface.
|
||||
*
|
||||
* @param info mipi csi2 hander
|
||||
* @return Returns setted value
|
||||
*/
|
||||
bool mipi_csi2_disable(struct mipi_csi2_info *info)
|
||||
{
|
||||
bool status;
|
||||
|
||||
_mipi_csi2_lock(info);
|
||||
|
||||
if (info->mipi_en) {
|
||||
info->mipi_en = false;
|
||||
clk_disable_unprepare(info->dphy_clk);
|
||||
clk_disable_unprepare(info->cfg_clk);
|
||||
} else
|
||||
mipi_dbg("mipi csi2 already disabled!\n");
|
||||
|
||||
status = info->mipi_en;
|
||||
|
||||
_mipi_csi2_unlock(info);
|
||||
|
||||
return status;
|
||||
}
|
||||
EXPORT_SYMBOL(mipi_csi2_disable);
|
||||
|
||||
/*!
|
||||
* This function is called to get mipi csi2 disable/enable status.
|
||||
*
|
||||
* @param info mipi csi2 hander
|
||||
* @return Returns mipi csi2 status
|
||||
*/
|
||||
bool mipi_csi2_get_status(struct mipi_csi2_info *info)
|
||||
{
|
||||
bool status;
|
||||
|
||||
_mipi_csi2_lock(info);
|
||||
status = info->mipi_en;
|
||||
_mipi_csi2_unlock(info);
|
||||
|
||||
return status;
|
||||
}
|
||||
EXPORT_SYMBOL(mipi_csi2_get_status);
|
||||
|
||||
/*!
|
||||
* This function is called to set mipi lanes.
|
||||
*
|
||||
* @param info mipi csi2 hander
|
||||
* @return Returns setted value
|
||||
*/
|
||||
unsigned int mipi_csi2_set_lanes(struct mipi_csi2_info *info)
|
||||
{
|
||||
unsigned int lanes;
|
||||
|
||||
_mipi_csi2_lock(info);
|
||||
mipi_csi2_write(info, info->lanes - 1, MIPI_CSI2_N_LANES);
|
||||
lanes = mipi_csi2_read(info, MIPI_CSI2_N_LANES);
|
||||
_mipi_csi2_unlock(info);
|
||||
|
||||
return lanes;
|
||||
}
|
||||
EXPORT_SYMBOL(mipi_csi2_set_lanes);
|
||||
|
||||
/*!
|
||||
* This function is called to set mipi data type.
|
||||
*
|
||||
* @param info mipi csi2 hander
|
||||
* @return Returns setted value
|
||||
*/
|
||||
unsigned int mipi_csi2_set_datatype(struct mipi_csi2_info *info,
|
||||
unsigned int datatype)
|
||||
{
|
||||
unsigned int dtype;
|
||||
|
||||
_mipi_csi2_lock(info);
|
||||
info->datatype = datatype;
|
||||
dtype = info->datatype;
|
||||
_mipi_csi2_unlock(info);
|
||||
|
||||
return dtype;
|
||||
}
|
||||
EXPORT_SYMBOL(mipi_csi2_set_datatype);
|
||||
|
||||
/*!
|
||||
* This function is called to get mipi data type.
|
||||
*
|
||||
* @param info mipi csi2 hander
|
||||
* @return Returns mipi data type
|
||||
*/
|
||||
unsigned int mipi_csi2_get_datatype(struct mipi_csi2_info *info)
|
||||
{
|
||||
unsigned int dtype;
|
||||
|
||||
_mipi_csi2_lock(info);
|
||||
dtype = info->datatype;
|
||||
_mipi_csi2_unlock(info);
|
||||
|
||||
return dtype;
|
||||
}
|
||||
EXPORT_SYMBOL(mipi_csi2_get_datatype);
|
||||
|
||||
/*!
|
||||
* This function is called to get mipi csi2 dphy status.
|
||||
*
|
||||
* @param info mipi csi2 hander
|
||||
* @return Returns dphy status
|
||||
*/
|
||||
unsigned int mipi_csi2_dphy_status(struct mipi_csi2_info *info)
|
||||
{
|
||||
unsigned int status;
|
||||
|
||||
_mipi_csi2_lock(info);
|
||||
status = mipi_csi2_read(info, MIPI_CSI2_PHY_STATE);
|
||||
_mipi_csi2_unlock(info);
|
||||
|
||||
return status;
|
||||
}
|
||||
EXPORT_SYMBOL(mipi_csi2_dphy_status);
|
||||
|
||||
/*!
|
||||
* This function is called to get mipi csi2 error1 status.
|
||||
*
|
||||
* @param info mipi csi2 hander
|
||||
* @return Returns error1 value
|
||||
*/
|
||||
unsigned int mipi_csi2_get_error1(struct mipi_csi2_info *info)
|
||||
{
|
||||
unsigned int err1;
|
||||
|
||||
_mipi_csi2_lock(info);
|
||||
err1 = mipi_csi2_read(info, MIPI_CSI2_ERR1);
|
||||
_mipi_csi2_unlock(info);
|
||||
|
||||
return err1;
|
||||
}
|
||||
EXPORT_SYMBOL(mipi_csi2_get_error1);
|
||||
|
||||
/*!
|
||||
* This function is called to get mipi csi2 error1 status.
|
||||
*
|
||||
* @param info mipi csi2 hander
|
||||
* @return Returns error1 value
|
||||
*/
|
||||
unsigned int mipi_csi2_get_error2(struct mipi_csi2_info *info)
|
||||
{
|
||||
unsigned int err2;
|
||||
|
||||
_mipi_csi2_lock(info);
|
||||
err2 = mipi_csi2_read(info, MIPI_CSI2_ERR2);
|
||||
_mipi_csi2_unlock(info);
|
||||
|
||||
return err2;
|
||||
}
|
||||
EXPORT_SYMBOL(mipi_csi2_get_error2);
|
||||
|
||||
/*!
|
||||
* This function is called to enable mipi to ipu pixel clock.
|
||||
*
|
||||
* @param info mipi csi2 hander
|
||||
* @return Returns 0 on success or negative error code on fail
|
||||
*/
|
||||
int mipi_csi2_pixelclk_enable(struct mipi_csi2_info *info)
|
||||
{
|
||||
return clk_prepare_enable(info->pixel_clk);
|
||||
}
|
||||
EXPORT_SYMBOL(mipi_csi2_pixelclk_enable);
|
||||
|
||||
/*!
|
||||
* This function is called to disable mipi to ipu pixel clock.
|
||||
*
|
||||
* @param info mipi csi2 hander
|
||||
* @return Returns 0 on success or negative error code on fail
|
||||
*/
|
||||
void mipi_csi2_pixelclk_disable(struct mipi_csi2_info *info)
|
||||
{
|
||||
clk_disable_unprepare(info->pixel_clk);
|
||||
}
|
||||
EXPORT_SYMBOL(mipi_csi2_pixelclk_disable);
|
||||
|
||||
/*!
|
||||
* This function is called to power on mipi csi2.
|
||||
*
|
||||
* @param info mipi csi2 hander
|
||||
* @return Returns 0 on success or negative error code on fail
|
||||
*/
|
||||
int mipi_csi2_reset(struct mipi_csi2_info *info)
|
||||
{
|
||||
_mipi_csi2_lock(info);
|
||||
|
||||
mipi_csi2_write(info, 0x0, MIPI_CSI2_PHY_SHUTDOWNZ);
|
||||
mipi_csi2_write(info, 0x0, MIPI_CSI2_DPHY_RSTZ);
|
||||
mipi_csi2_write(info, 0x0, MIPI_CSI2_CSI2_RESETN);
|
||||
|
||||
mipi_csi2_write(info, 0x00000001, MIPI_CSI2_PHY_TST_CTRL0);
|
||||
mipi_csi2_write(info, 0x00000000, MIPI_CSI2_PHY_TST_CTRL1);
|
||||
mipi_csi2_write(info, 0x00000000, MIPI_CSI2_PHY_TST_CTRL0);
|
||||
mipi_csi2_write(info, 0x00000002, MIPI_CSI2_PHY_TST_CTRL0);
|
||||
mipi_csi2_write(info, 0x00010044, MIPI_CSI2_PHY_TST_CTRL1);
|
||||
mipi_csi2_write(info, 0x00000000, MIPI_CSI2_PHY_TST_CTRL0);
|
||||
mipi_csi2_write(info, 0x00000014, MIPI_CSI2_PHY_TST_CTRL1);
|
||||
mipi_csi2_write(info, 0x00000002, MIPI_CSI2_PHY_TST_CTRL0);
|
||||
mipi_csi2_write(info, 0x00000000, MIPI_CSI2_PHY_TST_CTRL0);
|
||||
|
||||
mipi_csi2_write(info, 0xffffffff, MIPI_CSI2_PHY_SHUTDOWNZ);
|
||||
mipi_csi2_write(info, 0xffffffff, MIPI_CSI2_DPHY_RSTZ);
|
||||
mipi_csi2_write(info, 0xffffffff, MIPI_CSI2_CSI2_RESETN);
|
||||
|
||||
_mipi_csi2_unlock(info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mipi_csi2_reset);
|
||||
|
||||
/*!
|
||||
* This function is called to get mipi csi2 info.
|
||||
*
|
||||
* @return Returns mipi csi2 info struct pointor
|
||||
*/
|
||||
struct mipi_csi2_info *mipi_csi2_get_info(void)
|
||||
{
|
||||
return gmipi_csi2;
|
||||
}
|
||||
EXPORT_SYMBOL(mipi_csi2_get_info);
|
||||
|
||||
/*!
|
||||
* This function is called to get mipi csi2 bind ipu num.
|
||||
*
|
||||
* @return Returns mipi csi2 bind ipu num
|
||||
*/
|
||||
int mipi_csi2_get_bind_ipu(struct mipi_csi2_info *info)
|
||||
{
|
||||
int ipu_id;
|
||||
|
||||
_mipi_csi2_lock(info);
|
||||
ipu_id = info->ipu_id;
|
||||
_mipi_csi2_unlock(info);
|
||||
|
||||
return ipu_id;
|
||||
}
|
||||
EXPORT_SYMBOL(mipi_csi2_get_bind_ipu);
|
||||
|
||||
/*!
|
||||
* This function is called to get mipi csi2 bind csi num.
|
||||
*
|
||||
* @return Returns mipi csi2 bind csi num
|
||||
*/
|
||||
unsigned int mipi_csi2_get_bind_csi(struct mipi_csi2_info *info)
|
||||
{
|
||||
unsigned int csi_id;
|
||||
|
||||
_mipi_csi2_lock(info);
|
||||
csi_id = info->csi_id;
|
||||
_mipi_csi2_unlock(info);
|
||||
|
||||
return csi_id;
|
||||
}
|
||||
EXPORT_SYMBOL(mipi_csi2_get_bind_csi);
|
||||
|
||||
/*!
|
||||
* This function is called to get mipi csi2 virtual channel.
|
||||
*
|
||||
* @return Returns mipi csi2 virtual channel num
|
||||
*/
|
||||
unsigned int mipi_csi2_get_virtual_channel(struct mipi_csi2_info *info)
|
||||
{
|
||||
unsigned int v_channel;
|
||||
|
||||
_mipi_csi2_lock(info);
|
||||
v_channel = info->v_channel;
|
||||
_mipi_csi2_unlock(info);
|
||||
|
||||
return v_channel;
|
||||
}
|
||||
EXPORT_SYMBOL(mipi_csi2_get_virtual_channel);
|
||||
|
||||
/**
|
||||
* This function is called by the driver framework to initialize the MIPI CSI2
|
||||
* device.
|
||||
*
|
||||
* @param pdev The device structure for the MIPI CSI2 passed in by the
|
||||
* driver framework.
|
||||
*
|
||||
* @return Returns 0 on success or negative error code on error
|
||||
*/
|
||||
static int mipi_csi2_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct resource *res;
|
||||
u32 mipi_csi2_dphy_ver;
|
||||
int ret;
|
||||
|
||||
gmipi_csi2 = kmalloc(sizeof(struct mipi_csi2_info), GFP_KERNEL);
|
||||
if (!gmipi_csi2) {
|
||||
ret = -ENOMEM;
|
||||
goto alloc_failed;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "ipu_id", &(gmipi_csi2->ipu_id));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "ipu_id missing or invalid\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "csi_id", &(gmipi_csi2->csi_id));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "csi_id missing or invalid\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "v_channel", &(gmipi_csi2->v_channel));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "v_channel missing or invalid\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "lanes", &(gmipi_csi2->lanes));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "lanes missing or invalid\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if ((gmipi_csi2->ipu_id < 0) || (gmipi_csi2->ipu_id > 1) ||
|
||||
(gmipi_csi2->csi_id > 1) || (gmipi_csi2->v_channel > 3) ||
|
||||
(gmipi_csi2->lanes > 4)) {
|
||||
dev_err(&pdev->dev, "invalid param for mipi csi2!\n");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* initialize mutex */
|
||||
mutex_init(&gmipi_csi2->mutex_lock);
|
||||
|
||||
/* get mipi csi2 informaiton */
|
||||
gmipi_csi2->pdev = pdev;
|
||||
gmipi_csi2->mipi_en = false;
|
||||
|
||||
gmipi_csi2->cfg_clk = devm_clk_get(dev, "cfg_clk");
|
||||
if (IS_ERR(gmipi_csi2->cfg_clk)) {
|
||||
dev_err(&pdev->dev, "failed to get cfg_clk\n");
|
||||
ret = PTR_ERR(gmipi_csi2->cfg_clk);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* get mipi dphy clk */
|
||||
gmipi_csi2->dphy_clk = devm_clk_get(dev, "dphy_clk");
|
||||
if (IS_ERR(gmipi_csi2->dphy_clk)) {
|
||||
dev_err(&pdev->dev, "failed to get dphy pll_ref_clk\n");
|
||||
ret = PTR_ERR(gmipi_csi2->dphy_clk);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* get mipi to ipu pixel clk */
|
||||
gmipi_csi2->pixel_clk = devm_clk_get(dev, "pixel_clk");
|
||||
if (IS_ERR(gmipi_csi2->pixel_clk)) {
|
||||
dev_err(&pdev->dev, "failed to get mipi pixel clk\n");
|
||||
ret = PTR_ERR(gmipi_csi2->pixel_clk);
|
||||
goto err;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* mipi register mapping */
|
||||
gmipi_csi2->mipi_csi2_base = ioremap(res->start, PAGE_SIZE);
|
||||
if (!gmipi_csi2->mipi_csi2_base) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* mipi dphy clk enable for register access */
|
||||
clk_prepare_enable(gmipi_csi2->dphy_clk);
|
||||
/* get mipi csi2 dphy version */
|
||||
mipi_csi2_dphy_ver = mipi_csi2_read(gmipi_csi2, MIPI_CSI2_VERSION);
|
||||
|
||||
clk_disable_unprepare(gmipi_csi2->dphy_clk);
|
||||
|
||||
platform_set_drvdata(pdev, gmipi_csi2);
|
||||
|
||||
dev_info(&pdev->dev, "i.MX MIPI CSI2 driver probed\n");
|
||||
dev_info(&pdev->dev, "i.MX MIPI CSI2 dphy version is 0x%x\n",
|
||||
mipi_csi2_dphy_ver);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
kfree(gmipi_csi2);
|
||||
alloc_failed:
|
||||
dev_err(&pdev->dev, "i.MX MIPI CSI2 driver probed - error\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mipi_csi2_remove(struct platform_device *pdev)
|
||||
{
|
||||
/* unmapping mipi register */
|
||||
iounmap(gmipi_csi2->mipi_csi2_base);
|
||||
|
||||
kfree(gmipi_csi2);
|
||||
|
||||
dev_set_drvdata(&pdev->dev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id imx_mipi_csi2_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx6q-mipi-csi2", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static struct platform_driver mipi_csi2_driver = {
|
||||
.driver = {
|
||||
.name = "mxc_mipi_csi2",
|
||||
.of_match_table = imx_mipi_csi2_dt_ids,
|
||||
},
|
||||
.probe = mipi_csi2_probe,
|
||||
.remove = mipi_csi2_remove,
|
||||
};
|
||||
|
||||
static int __init mipi_csi2_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = platform_driver_register(&mipi_csi2_driver);
|
||||
if (err) {
|
||||
pr_err("mipi_csi2_driver register failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
pr_info("MIPI CSI2 driver module loaded\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit mipi_csi2_cleanup(void)
|
||||
{
|
||||
platform_driver_unregister(&mipi_csi2_driver);
|
||||
}
|
||||
|
||||
subsys_initcall(mipi_csi2_init);
|
||||
module_exit(mipi_csi2_cleanup);
|
||||
|
||||
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
|
||||
MODULE_DESCRIPTION("i.MX MIPI CSI2 driver");
|
||||
MODULE_LICENSE("GPL");
|
34
drivers/mxc/mipi/mxc_mipi_csi2.h
Normal file
34
drivers/mxc/mipi/mxc_mipi_csi2.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2011-2014 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
* Copyright 2019 NXP
|
||||
*/
|
||||
|
||||
#ifndef __MXC_MIPI_CSI2_H__
|
||||
#define __MXC_MIPI_CSI2_H__
|
||||
|
||||
#ifdef DEBUG
|
||||
#define mipi_dbg(fmt, ...) \
|
||||
printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
|
||||
#else
|
||||
#define mipi_dbg(fmt, ...)
|
||||
#endif
|
||||
|
||||
/* driver private data */
|
||||
struct mipi_csi2_info {
|
||||
bool mipi_en;
|
||||
int ipu_id;
|
||||
unsigned int csi_id;
|
||||
unsigned int v_channel;
|
||||
unsigned int lanes;
|
||||
unsigned int datatype;
|
||||
struct clk *cfg_clk;
|
||||
struct clk *dphy_clk;
|
||||
struct clk *pixel_clk;
|
||||
void __iomem *mipi_csi2_base;
|
||||
struct platform_device *pdev;
|
||||
|
||||
struct mutex mutex_lock;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,6 +1,6 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_VIDEO_ALLEGRO_DVT) += allegro-dvt/
|
||||
obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx/
|
||||
obj-$(CONFIG_VIDEO_IMX_CAPTURE) += imx/
|
||||
obj-$(CONFIG_VIDEO_MESON_VDEC) += meson/vdec/
|
||||
obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/
|
||||
obj-$(CONFIG_VIDEO_SUNXI) += sunxi/
|
||||
|
|
|
@ -31,3 +31,77 @@ config VIDEO_IMX7_CSI
|
|||
i.MX6UL/L or i.MX7.
|
||||
endmenu
|
||||
endif
|
||||
|
||||
config VIDEO_IMX_CAPTURE
|
||||
tristate "i.MX V4L2 media core driver"
|
||||
depends on ARCH_MXC || COMPILE_TEST
|
||||
depends on MEDIA_CONTROLLER && VIDEO_V4L2
|
||||
depends on VIDEO_V4L2_SUBDEV_API
|
||||
depends on HAS_DMA
|
||||
select VIDEOBUF2_DMA_CONTIG
|
||||
select V4L2_FWNODE
|
||||
help
|
||||
Say yes here to enable support for video4linux media controller
|
||||
driver for the i.MX5/6 SOC.
|
||||
|
||||
if VIDEO_IMX_CAPTURE
|
||||
menu "i.MX8QXP/QM Camera ISI/MIPI Features support"
|
||||
|
||||
config IMX8_MEDIA_DEVICE
|
||||
tristate "IMX8 Media Device Driver"
|
||||
select V4L2_FWNODE
|
||||
default y
|
||||
help
|
||||
This media device is a virtual device which used to manage
|
||||
all modules in image subsystem of imx8qxp/qm platform.
|
||||
|
||||
config IMX8_ISI_CORE
|
||||
bool "IMX8 Image Sensor Interface Core Driver"
|
||||
depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
||||
default y
|
||||
|
||||
config IMX8_ISI_CAPTURE
|
||||
bool "IMX8 Image Sensor Interface Capture Device Driver"
|
||||
depends on IMX8_ISI_CORE
|
||||
select VIDEOBUF2_DMA_CONTIG
|
||||
default y
|
||||
|
||||
config IMX8_ISI_M2M
|
||||
bool "IMX8 Image Sensor Interface Memory to Memory Device Driver"
|
||||
select V4L2_MEM2MEM_DEV
|
||||
depends on IMX8_ISI_CORE
|
||||
default y
|
||||
|
||||
config IMX8_MIPI_CSI2
|
||||
tristate "IMX8 MIPI CSI2 Controller"
|
||||
depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
||||
default y
|
||||
help
|
||||
Enable support for video4linux camera sensor interface driver for
|
||||
i.MX8QM/QXP platform.
|
||||
|
||||
config IMX8_MIPI_CSI2_SAM
|
||||
tristate "IMX8 MIPI CSI2 SAMSUNG Controller"
|
||||
depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
||||
default y
|
||||
help
|
||||
Enable support for video4linux MIPI CSI2 Samsung driver for
|
||||
i.MX8MN platform.
|
||||
|
||||
config IMX8_PARALLEL_CSI
|
||||
tristate "IMX8 Parallel Capture Controller"
|
||||
depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
||||
default y
|
||||
help
|
||||
Enable support for video4linux parallel camera sensor interface
|
||||
driver for i.MX8QM/QXP platform.
|
||||
|
||||
config GMSL_MAX9286
|
||||
tristate "GMSL MAX8286"
|
||||
depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
|
||||
default y
|
||||
help
|
||||
Enable support for video4linux camera sensor driver for GMSL MAX9286
|
||||
|
||||
endmenu
|
||||
endif #VIDEO_IMX_CAPTURE
|
||||
|
|
|
@ -3,6 +3,8 @@ 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 \
|
||||
imx-media-csc-scaler.o
|
||||
|
||||
imx8-capture-objs := imx8-isi-core.o imx8-isi-hw.o
|
||||
|
||||
imx-media-common-objs := imx-media-capture.o imx-media-dev-common.o \
|
||||
imx-media-of.o imx-media-utils.o
|
||||
|
||||
|
@ -16,3 +18,11 @@ obj-$(CONFIG_VIDEO_IMX_CSI) += imx6-mipi-csi2.o
|
|||
|
||||
obj-$(CONFIG_VIDEO_IMX7_CSI) += imx7-media-csi.o
|
||||
obj-$(CONFIG_VIDEO_IMX7_CSI) += imx7-mipi-csis.o
|
||||
obj-$(CONFIG_IMX8_MEDIA_DEVICE) += imx8-media-dev.o
|
||||
obj-$(CONFIG_IMX8_ISI_CORE) += imx8-capture.o
|
||||
obj-$(CONFIG_IMX8_ISI_CAPTURE) += imx8-isi-cap.o
|
||||
obj-$(CONFIG_IMX8_ISI_M2M) += imx8-isi-m2m.o
|
||||
obj-$(CONFIG_IMX8_MIPI_CSI2) += imx8-mipi-csi2.o
|
||||
obj-$(CONFIG_IMX8_MIPI_CSI2_SAM) += imx8-mipi-csi2-sam.o
|
||||
obj-$(CONFIG_IMX8_PARALLEL_CSI) += imx8-parallel-csi.o
|
||||
obj-$(CONFIG_GMSL_MAX9286) += gmsl-max9286.o
|
||||
|
|
3244
drivers/staging/media/imx/gmsl-max9286.c
Normal file
3244
drivers/staging/media/imx/gmsl-max9286.c
Normal file
File diff suppressed because it is too large
Load Diff
99
drivers/staging/media/imx/imx8-common.h
Normal file
99
drivers/staging/media/imx/imx8-common.h
Normal file
|
@ -0,0 +1,99 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* V4L2 Capture ISI subdev for i.MX8QXP/QM platform
|
||||
*
|
||||
* ISI is a Image Sensor Interface of i.MX8QXP/QM platform, which
|
||||
* used to process image from camera sensor to memory or DC
|
||||
*
|
||||
* Copyright (c) 2019 NXP Semiconductor
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MXC_COMMON_H__
|
||||
#define __MXC_COMMON_H__
|
||||
|
||||
#define ISI_OF_NODE_NAME "isi"
|
||||
#define MIPI_CSI2_OF_NODE_NAME "csi"
|
||||
#define PARALLEL_OF_NODE_NAME "pcsi"
|
||||
|
||||
#define MXC_ISI_MAX_DEVS 8
|
||||
#define MXC_MIPI_CSI2_MAX_DEVS 2
|
||||
#define MXC_MAX_SENSORS 3
|
||||
|
||||
/* ISI PADS */
|
||||
#define MXC_ISI_SD_PAD_SINK_MIPI0_VC0 0
|
||||
#define MXC_ISI_SD_PAD_SINK_MIPI0_VC1 1
|
||||
#define MXC_ISI_SD_PAD_SINK_MIPI0_VC2 2
|
||||
#define MXC_ISI_SD_PAD_SINK_MIPI0_VC3 3
|
||||
#define MXC_ISI_SD_PAD_SINK_MIPI1_VC0 4
|
||||
#define MXC_ISI_SD_PAD_SINK_MIPI1_VC1 5
|
||||
#define MXC_ISI_SD_PAD_SINK_MIPI1_VC2 6
|
||||
#define MXC_ISI_SD_PAD_SINK_MIPI1_VC3 7
|
||||
|
||||
#define MXC_ISI_SD_PAD_SINK_DC0 8
|
||||
#define MXC_ISI_SD_PAD_SINK_DC1 9
|
||||
#define MXC_ISI_SD_PAD_SINK_HDMI 10
|
||||
#define MXC_ISI_SD_PAD_SINK_MEM 11
|
||||
#define MXC_ISI_SD_PAD_SOURCE_MEM 12
|
||||
#define MXC_ISI_SD_PAD_SOURCE_DC0 13
|
||||
#define MXC_ISI_SD_PAD_SOURCE_DC1 14
|
||||
#define MXC_ISI_SD_PAD_SINK_PARALLEL_CSI 15
|
||||
#define MXC_ISI_SD_PADS_NUM 16
|
||||
|
||||
/* MIPI CSI PADS */
|
||||
#define MXC_MIPI_CSI2_VC0_PAD_SINK 0
|
||||
#define MXC_MIPI_CSI2_VC1_PAD_SINK 1
|
||||
#define MXC_MIPI_CSI2_VC2_PAD_SINK 2
|
||||
#define MXC_MIPI_CSI2_VC3_PAD_SINK 3
|
||||
|
||||
#define MXC_MIPI_CSI2_VC0_PAD_SOURCE 4
|
||||
#define MXC_MIPI_CSI2_VC1_PAD_SOURCE 5
|
||||
#define MXC_MIPI_CSI2_VC2_PAD_SOURCE 6
|
||||
#define MXC_MIPI_CSI2_VC3_PAD_SOURCE 7
|
||||
#define MXC_MIPI_CSI2_VCX_PADS_NUM 8
|
||||
|
||||
/* Parallel CSI PADS */
|
||||
#define MXC_PARALLEL_CSI_PAD_SOURCE 0
|
||||
#define MXC_PARALLEL_CSI_PAD_SINK 1
|
||||
#define MXC_PARALLEL_CSI_PADS_NUM 2
|
||||
|
||||
#define ISI_2K 2048
|
||||
|
||||
enum {
|
||||
IN_PORT,
|
||||
SUB_IN_PORT,
|
||||
OUT_PORT,
|
||||
MAX_PORTS,
|
||||
};
|
||||
|
||||
enum isi_input_interface {
|
||||
ISI_INPUT_INTERFACE_DC0 = 0,
|
||||
ISI_INPUT_INTERFACE_DC1,
|
||||
ISI_INPUT_INTERFACE_MIPI0_CSI2,
|
||||
ISI_INPUT_INTERFACE_MIPI1_CSI2,
|
||||
ISI_INPUT_INTERFACE_HDMI,
|
||||
ISI_INPUT_INTERFACE_MEM,
|
||||
ISI_INPUT_INTERFACE_PARALLEL_CSI,
|
||||
ISI_INPUT_INTERFACE_MAX,
|
||||
};
|
||||
|
||||
enum isi_input_sub_interface {
|
||||
ISI_INPUT_SUB_INTERFACE_VC0 = 0,
|
||||
ISI_INPUT_SUB_INTERFACE_VC1,
|
||||
ISI_INPUT_SUB_INTERFACE_VC2,
|
||||
ISI_INPUT_SUB_INTERFACE_VC3,
|
||||
};
|
||||
|
||||
enum isi_output_interface {
|
||||
ISI_OUTPUT_INTERFACE_DC0 = 0,
|
||||
ISI_OUTPUT_INTERFACE_DC1,
|
||||
ISI_OUTPUT_INTERFACE_MEM,
|
||||
ISI_OUTPUT_INTERFACE_MAX,
|
||||
};
|
||||
|
||||
enum mxc_isi_buf_id {
|
||||
MXC_ISI_BUF1 = 0x0,
|
||||
MXC_ISI_BUF2,
|
||||
};
|
||||
|
||||
#endif /* MXC_ISI_CORE_H_ */
|
1766
drivers/staging/media/imx/imx8-isi-cap.c
Normal file
1766
drivers/staging/media/imx/imx8-isi-cap.c
Normal file
File diff suppressed because it is too large
Load Diff
513
drivers/staging/media/imx/imx8-isi-core.c
Normal file
513
drivers/staging/media/imx/imx8-isi-core.c
Normal file
|
@ -0,0 +1,513 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2019 NXP Semiconductor
|
||||
*
|
||||
*/
|
||||
|
||||
#include "imx8-isi-hw.h"
|
||||
|
||||
static const struct of_device_id mxc_isi_of_match[];
|
||||
|
||||
struct mxc_isi_dev *mxc_isi_get_hostdata(struct platform_device *pdev)
|
||||
{
|
||||
struct mxc_isi_dev *mxc_isi;
|
||||
|
||||
if (!pdev || !pdev->dev.parent)
|
||||
return NULL;
|
||||
|
||||
device_lock(pdev->dev.parent);
|
||||
mxc_isi = (struct mxc_isi_dev *)dev_get_drvdata(pdev->dev.parent);
|
||||
if (!mxc_isi) {
|
||||
dev_err(&pdev->dev, "Cann't get host data\n");
|
||||
device_unlock(pdev->dev.parent);
|
||||
return NULL;
|
||||
}
|
||||
device_unlock(pdev->dev.parent);
|
||||
|
||||
return mxc_isi;
|
||||
}
|
||||
|
||||
struct device *mxc_isi_dev_get_parent(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *parent;
|
||||
struct platform_device *parent_pdev;
|
||||
|
||||
if (!pdev)
|
||||
return NULL;
|
||||
|
||||
/* Get parent for isi capture device */
|
||||
parent = of_get_parent(dev->of_node);
|
||||
parent_pdev = of_find_device_by_node(parent);
|
||||
if (!parent_pdev) {
|
||||
of_node_put(parent);
|
||||
return NULL;
|
||||
}
|
||||
of_node_put(parent);
|
||||
|
||||
return &parent_pdev->dev;
|
||||
}
|
||||
|
||||
static irqreturn_t mxc_isi_irq_handler(int irq, void *priv)
|
||||
{
|
||||
struct mxc_isi_dev *mxc_isi = priv;
|
||||
struct device *dev = &mxc_isi->pdev->dev;
|
||||
u32 status;
|
||||
|
||||
spin_lock(&mxc_isi->slock);
|
||||
|
||||
status = mxc_isi_get_irq_status(mxc_isi);
|
||||
mxc_isi->status = status;
|
||||
mxc_isi_clean_irq_status(mxc_isi, status);
|
||||
|
||||
if (status & CHNL_STS_FRM_STRD_MASK) {
|
||||
if (mxc_isi->m2m_enabled)
|
||||
mxc_isi_m2m_frame_write_done(mxc_isi);
|
||||
else
|
||||
mxc_isi_cap_frame_write_done(mxc_isi);
|
||||
}
|
||||
|
||||
if (status & (CHNL_STS_AXI_WR_ERR_Y_MASK |
|
||||
CHNL_STS_AXI_WR_ERR_U_MASK |
|
||||
CHNL_STS_AXI_WR_ERR_V_MASK))
|
||||
dev_dbg(dev, "%s, IRQ AXI Error stat=0x%X\n", __func__, status);
|
||||
|
||||
if (status & (CHNL_STS_OFLW_PANIC_Y_BUF_MASK |
|
||||
CHNL_STS_OFLW_PANIC_U_BUF_MASK |
|
||||
CHNL_STS_OFLW_PANIC_V_BUF_MASK))
|
||||
dev_dbg(dev, "%s, IRQ Panic OFLW Error stat=0x%X\n", __func__, status);
|
||||
|
||||
if (status & (CHNL_STS_OFLW_Y_BUF_MASK |
|
||||
CHNL_STS_OFLW_U_BUF_MASK |
|
||||
CHNL_STS_OFLW_V_BUF_MASK))
|
||||
dev_dbg(dev, "%s, IRQ OFLW Error stat=0x%X\n", __func__, status);
|
||||
|
||||
if (status & (CHNL_STS_EXCS_OFLW_Y_BUF_MASK |
|
||||
CHNL_STS_EXCS_OFLW_U_BUF_MASK |
|
||||
CHNL_STS_EXCS_OFLW_V_BUF_MASK))
|
||||
dev_dbg(dev, "%s, IRQ EXCS OFLW Error stat=0x%X\n", __func__, status);
|
||||
|
||||
spin_unlock(&mxc_isi->slock);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int disp_mix_sft_rstn(struct reset_control *reset, bool enable)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!reset)
|
||||
return 0;
|
||||
|
||||
ret = enable ? reset_control_assert(reset) :
|
||||
reset_control_deassert(reset);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int disp_mix_clks_enable(struct reset_control *reset, bool enable)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!reset)
|
||||
return 0;
|
||||
|
||||
ret = enable ? reset_control_assert(reset) :
|
||||
reset_control_deassert(reset);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mxc_imx8_clk_get(struct mxc_isi_dev *mxc_isi)
|
||||
{
|
||||
struct device *dev = &mxc_isi->pdev->dev;
|
||||
|
||||
mxc_isi->clk = devm_clk_get(dev, NULL);
|
||||
|
||||
if (IS_ERR(mxc_isi->clk)) {
|
||||
dev_err(dev, "failed to get isi clk\n");
|
||||
return PTR_ERR(mxc_isi->clk);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxc_imx8_clk_enable(struct mxc_isi_dev *mxc_isi)
|
||||
{
|
||||
struct device *dev = &mxc_isi->pdev->dev;
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(mxc_isi->clk);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s, enable clk error\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mxc_imx8_clk_disable(struct mxc_isi_dev *mxc_isi)
|
||||
{
|
||||
clk_disable_unprepare(mxc_isi->clk);
|
||||
}
|
||||
|
||||
static struct mxc_isi_dev_ops mxc_imx8_data = {
|
||||
.clk_get = mxc_imx8_clk_get,
|
||||
.clk_enable = mxc_imx8_clk_enable,
|
||||
.clk_disable = mxc_imx8_clk_disable,
|
||||
};
|
||||
|
||||
static int mxc_imx8mn_clk_get(struct mxc_isi_dev *mxc_isi)
|
||||
{
|
||||
struct device *dev = &mxc_isi->pdev->dev;
|
||||
|
||||
mxc_isi->clk_disp_axi = devm_clk_get(dev, "disp_axi");
|
||||
if (IS_ERR(mxc_isi->clk_disp_axi)) {
|
||||
dev_err(dev, "failed to get disp_axi clk\n");
|
||||
return PTR_ERR(mxc_isi->clk_disp_axi);
|
||||
}
|
||||
|
||||
mxc_isi->clk_disp_apb = devm_clk_get(dev, "disp_apb");
|
||||
if (IS_ERR(mxc_isi->clk_disp_apb)) {
|
||||
dev_err(dev, "failed to get disp_apb clk\n");
|
||||
return PTR_ERR(mxc_isi->clk_disp_apb);
|
||||
}
|
||||
|
||||
mxc_isi->clk_root_disp_axi = devm_clk_get(dev, "disp_axi_root");
|
||||
if (IS_ERR(mxc_isi->clk_root_disp_axi)) {
|
||||
dev_err(dev, "failed to get disp axi root clk\n");
|
||||
return PTR_ERR(mxc_isi->clk_root_disp_axi);
|
||||
}
|
||||
|
||||
mxc_isi->clk_root_disp_apb = devm_clk_get(dev, "disp_apb_root");
|
||||
if (IS_ERR(mxc_isi->clk_root_disp_apb)) {
|
||||
dev_err(dev, "failed to get disp apb root clk\n");
|
||||
return PTR_ERR(mxc_isi->clk_root_disp_apb);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxc_imx8mn_clk_enable(struct mxc_isi_dev *mxc_isi)
|
||||
{
|
||||
struct device *dev = &mxc_isi->pdev->dev;
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(mxc_isi->clk_disp_axi);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "prepare and enable axi clk error\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(mxc_isi->clk_disp_apb);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "prepare and enable abp clk error\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(mxc_isi->clk_root_disp_axi);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "prepare and enable axi root clk error\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(mxc_isi->clk_root_disp_apb);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "prepare and enable apb root clk error\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mxc_imx8mn_clk_disable(struct mxc_isi_dev *mxc_isi)
|
||||
{
|
||||
clk_disable_unprepare(mxc_isi->clk_root_disp_axi);
|
||||
clk_disable_unprepare(mxc_isi->clk_root_disp_apb);
|
||||
clk_disable_unprepare(mxc_isi->clk_disp_axi);
|
||||
clk_disable_unprepare(mxc_isi->clk_disp_apb);
|
||||
}
|
||||
|
||||
static struct mxc_isi_dev_ops mxc_imx8mn_data = {
|
||||
.clk_get = mxc_imx8mn_clk_get,
|
||||
.clk_enable = mxc_imx8mn_clk_enable,
|
||||
.clk_disable = mxc_imx8mn_clk_disable,
|
||||
};
|
||||
|
||||
static int mxc_isi_parse_dt(struct mxc_isi_dev *mxc_isi)
|
||||
{
|
||||
struct device *dev = &mxc_isi->pdev->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
int ret = 0;
|
||||
|
||||
mxc_isi->id = of_alias_get_id(node, "isi");
|
||||
mxc_isi->chain_buf = of_property_read_bool(node, "fsl,chain_buf");
|
||||
|
||||
ret = of_property_read_u32_array(node, "interface", mxc_isi->interface, 3);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dev_dbg(dev, "%s, isi_%d,interface(%d, %d, %d)\n", __func__,
|
||||
mxc_isi->id,
|
||||
mxc_isi->interface[0],
|
||||
mxc_isi->interface[1],
|
||||
mxc_isi->interface[2]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxc_isi_clk_get(struct mxc_isi_dev *mxc_isi)
|
||||
{
|
||||
const struct mxc_isi_dev_ops *ops = mxc_isi->ops;
|
||||
|
||||
if (!ops && !ops->clk_get)
|
||||
return -EINVAL;
|
||||
|
||||
return ops->clk_get(mxc_isi);
|
||||
}
|
||||
|
||||
static int mxc_isi_clk_enable(struct mxc_isi_dev *mxc_isi)
|
||||
{
|
||||
const struct mxc_isi_dev_ops *ops = mxc_isi->ops;
|
||||
|
||||
if (!ops && !ops->clk_enable)
|
||||
return -EINVAL;
|
||||
|
||||
return ops->clk_enable(mxc_isi);
|
||||
}
|
||||
|
||||
static void mxc_isi_clk_disable(struct mxc_isi_dev *mxc_isi)
|
||||
{
|
||||
const struct mxc_isi_dev_ops *ops = mxc_isi->ops;
|
||||
|
||||
if (!ops && !ops->clk_disable)
|
||||
return;
|
||||
|
||||
ops->clk_disable(mxc_isi);
|
||||
}
|
||||
|
||||
static int mxc_isi_of_parse_resets(struct mxc_isi_dev *mxc_isi)
|
||||
{
|
||||
int ret;
|
||||
struct device *dev = &mxc_isi->pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct device_node *parent, *child;
|
||||
struct of_phandle_args args;
|
||||
struct reset_control *rstc;
|
||||
const char *compat;
|
||||
uint32_t len, rstc_num = 0;
|
||||
|
||||
ret = of_parse_phandle_with_args(np, "resets", "#reset-cells",
|
||||
0, &args);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
parent = args.np;
|
||||
for_each_child_of_node(parent, child) {
|
||||
compat = of_get_property(child, "compatible", NULL);
|
||||
if (!compat)
|
||||
continue;
|
||||
|
||||
rstc = of_reset_control_array_get(child, false, false, true);
|
||||
if (IS_ERR(rstc))
|
||||
continue;
|
||||
|
||||
len = strlen(compat);
|
||||
if (!of_compat_cmp("isi,soft-resetn", compat, len)) {
|
||||
mxc_isi->soft_resetn = rstc;
|
||||
rstc_num++;
|
||||
} else if (!of_compat_cmp("isi,clk-enable", compat, len)) {
|
||||
mxc_isi->clk_enable = rstc;
|
||||
rstc_num++;
|
||||
} else {
|
||||
dev_warn(dev, "invalid isi reset node: %s\n", compat);
|
||||
}
|
||||
}
|
||||
|
||||
if (!rstc_num) {
|
||||
dev_err(dev, "no invalid reset control exists\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
of_node_put(parent);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxc_isi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct mxc_isi_dev *mxc_isi;
|
||||
struct resource *res;
|
||||
const struct of_device_id *of_id;
|
||||
int ret = 0;
|
||||
|
||||
|
||||
mxc_isi = devm_kzalloc(dev, sizeof(*mxc_isi), GFP_KERNEL);
|
||||
if (!mxc_isi)
|
||||
return -ENOMEM;
|
||||
|
||||
mxc_isi->pdev = pdev;
|
||||
of_id = of_match_node(mxc_isi_of_match, dev->of_node);
|
||||
if (!of_id)
|
||||
return -EINVAL;
|
||||
|
||||
mxc_isi->ops = of_id->data;
|
||||
if (!mxc_isi->ops) {
|
||||
dev_err(dev, "Can't get platform device data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = mxc_isi_parse_dt(mxc_isi);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (mxc_isi->id >= MXC_ISI_MAX_DEVS || mxc_isi->id < 0) {
|
||||
dev_err(dev, "Invalid driver data or device id (%d)\n",
|
||||
mxc_isi->id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spin_lock_init(&mxc_isi->slock);
|
||||
mutex_init(&mxc_isi->lock);
|
||||
atomic_set(&mxc_isi->usage_count, 0);
|
||||
|
||||
if (of_device_is_compatible(dev->of_node, "fsl,imx8mn-isi")) {
|
||||
ret = mxc_isi_of_parse_resets(mxc_isi);
|
||||
if (ret) {
|
||||
dev_warn(dev, "Can not parse reset control\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = mxc_isi_clk_get(mxc_isi);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "ISI_%d get clocks fail\n", mxc_isi->id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
mxc_isi->regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(mxc_isi->regs)) {
|
||||
dev_err(dev, "Failed to get ISI register map\n");
|
||||
return PTR_ERR(mxc_isi->regs);
|
||||
}
|
||||
|
||||
ret = mxc_isi_clk_enable(mxc_isi);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "ISI_%d enable clocks fail\n", mxc_isi->id);
|
||||
return ret;
|
||||
}
|
||||
disp_mix_sft_rstn(mxc_isi->soft_resetn, false);
|
||||
disp_mix_clks_enable(mxc_isi->clk_enable, true);
|
||||
|
||||
mxc_isi_clean_registers(mxc_isi);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "Failed to get IRQ resource\n");
|
||||
goto err;
|
||||
}
|
||||
ret = devm_request_irq(dev, res->start, mxc_isi_irq_handler,
|
||||
0, dev_name(dev), mxc_isi);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to install irq (%d)\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
mxc_isi_channel_set_chain_buf(mxc_isi);
|
||||
|
||||
ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
|
||||
if (ret < 0)
|
||||
dev_warn(dev, "Populate child platform device fail\n");
|
||||
|
||||
mxc_isi_clk_disable(mxc_isi);
|
||||
|
||||
platform_set_drvdata(pdev, mxc_isi);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
dev_info(dev, "mxc_isi.%d registered successfully\n", mxc_isi->id);
|
||||
return 0;
|
||||
|
||||
err:
|
||||
disp_mix_clks_enable(mxc_isi->clk_enable, false);
|
||||
disp_mix_sft_rstn(mxc_isi->soft_resetn, true);
|
||||
mxc_isi_clk_disable(mxc_isi);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static int mxc_isi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
pm_runtime_disable(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxc_isi_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct mxc_isi_dev *mxc_isi = dev_get_drvdata(dev);
|
||||
|
||||
if (mxc_isi->is_streaming) {
|
||||
dev_warn(dev, "running, prevent entering suspend.\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
return pm_runtime_force_suspend(dev);
|
||||
}
|
||||
|
||||
static int mxc_isi_pm_resume(struct device *dev)
|
||||
{
|
||||
return pm_runtime_force_resume(dev);
|
||||
}
|
||||
|
||||
static int mxc_isi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct mxc_isi_dev *mxc_isi = dev_get_drvdata(dev);
|
||||
|
||||
disp_mix_clks_enable(mxc_isi->clk_enable, false);
|
||||
mxc_isi_clk_disable(mxc_isi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxc_isi_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct mxc_isi_dev *mxc_isi = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = mxc_isi_clk_enable(mxc_isi);
|
||||
if (ret) {
|
||||
dev_err(dev, "%s clk enable fail\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
disp_mix_sft_rstn(mxc_isi->soft_resetn, false);
|
||||
disp_mix_clks_enable(mxc_isi->clk_enable, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops mxc_isi_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(mxc_isi_pm_suspend, mxc_isi_pm_resume)
|
||||
SET_RUNTIME_PM_OPS(mxc_isi_runtime_suspend, mxc_isi_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct of_device_id mxc_isi_of_match[] = {
|
||||
{.compatible = "fsl,imx8-isi", .data = &mxc_imx8_data },
|
||||
{.compatible = "fsl,imx8mn-isi", .data = &mxc_imx8mn_data },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mxc_isi_of_match);
|
||||
|
||||
static struct platform_driver mxc_isi_driver = {
|
||||
.probe = mxc_isi_probe,
|
||||
.remove = mxc_isi_remove,
|
||||
.driver = {
|
||||
.of_match_table = mxc_isi_of_match,
|
||||
.name = MXC_ISI_DRIVER_NAME,
|
||||
.pm = &mxc_isi_pm_ops,
|
||||
}
|
||||
};
|
||||
module_platform_driver(mxc_isi_driver);
|
||||
|
||||
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
|
||||
MODULE_DESCRIPTION("IMX8 Image Subsystem driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("ISI");
|
||||
MODULE_VERSION("1.0");
|
364
drivers/staging/media/imx/imx8-isi-core.h
Normal file
364
drivers/staging/media/imx/imx8-isi-core.h
Normal file
|
@ -0,0 +1,364 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2019 NXP Semiconductor
|
||||
*/
|
||||
|
||||
#ifndef __MXC_ISI_CORE_H__
|
||||
#define __MXC_ISI_CORE_H__
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk.h>
|
||||
#include <media/media-device.h>
|
||||
#include <media/media-entity.h>
|
||||
#include <media/v4l2-subdev.h>
|
||||
#include <media/v4l2-ioctl.h>
|
||||
#include <media/v4l2-mem2mem.h>
|
||||
#include <media/videobuf2-core.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <media/v4l2-subdev.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
#include <media/videobuf2-core.h>
|
||||
#include <media/videobuf2-dma-contig.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include "imx8-common.h"
|
||||
|
||||
#define MXC_ISI_DRIVER_NAME "mxc-isi"
|
||||
#define MXC_ISI_CAPTURE "mxc-isi-cap"
|
||||
#define MXC_ISI_M2M "mxc-isi-m2m"
|
||||
#define MXC_MAX_PLANES 3
|
||||
|
||||
struct mxc_isi_dev;
|
||||
|
||||
enum mxc_isi_out_fmt {
|
||||
MXC_ISI_OUT_FMT_RGBA32 = 0x0,
|
||||
MXC_ISI_OUT_FMT_ABGR32,
|
||||
MXC_ISI_OUT_FMT_ARGB32,
|
||||
MXC_ISI_OUT_FMT_RGBX32,
|
||||
MXC_ISI_OUT_FMT_XBGR32,
|
||||
MXC_ISI_OUT_FMT_XRGB32,
|
||||
MXC_ISI_OUT_FMT_RGB32P,
|
||||
MXC_ISI_OUT_FMT_BGR32P,
|
||||
MXC_ISI_OUT_FMT_A2BGR10,
|
||||
MXC_ISI_OUT_FMT_A2RGB10,
|
||||
MXC_ISI_OUT_FMT_RGB565,
|
||||
MXC_ISI_OUT_FMT_RAW8,
|
||||
MXC_ISI_OUT_FMT_RAW10,
|
||||
MXC_ISI_OUT_FMT_RAW10P,
|
||||
MXC_ISI_OUT_FMT_RAW12,
|
||||
MXC_ISI_OUT_FMT_RAW16,
|
||||
MXC_ISI_OUT_FMT_YUV444_1P8P,
|
||||
MXC_ISI_OUT_FMT_YUV444_2P8P,
|
||||
MXC_ISI_OUT_FMT_YUV444_3P8P,
|
||||
MXC_ISI_OUT_FMT_YUV444_1P8,
|
||||
MXC_ISI_OUT_FMT_YUV444_1P10,
|
||||
MXC_ISI_OUT_FMT_YUV444_2P10,
|
||||
MXC_ISI_OUT_FMT_YUV444_3P10,
|
||||
MXC_ISI_OUT_FMT_YUV444_1P10P = 0x18,
|
||||
MXC_ISI_OUT_FMT_YUV444_2P10P,
|
||||
MXC_ISI_OUT_FMT_YUV444_3P10P,
|
||||
MXC_ISI_OUT_FMT_YUV444_1P12 = 0x1C,
|
||||
MXC_ISI_OUT_FMT_YUV444_2P12,
|
||||
MXC_ISI_OUT_FMT_YUV444_3P12,
|
||||
MXC_ISI_OUT_FMT_YUV422_1P8P = 0x20,
|
||||
MXC_ISI_OUT_FMT_YUV422_2P8P,
|
||||
MXC_ISI_OUT_FMT_YUV422_3P8P,
|
||||
MXC_ISI_OUT_FMT_YUV422_1P10 = 0x24,
|
||||
MXC_ISI_OUT_FMT_YUV422_2P10,
|
||||
MXC_ISI_OUT_FMT_YUV422_3P10,
|
||||
MXC_ISI_OUT_FMT_YUV422_1P10P = 0x28,
|
||||
MXC_ISI_OUT_FMT_YUV422_2P10P,
|
||||
MXC_ISI_OUT_FMT_YUV422_3P10P,
|
||||
MXC_ISI_OUT_FMT_YUV422_1P12 = 0x2C,
|
||||
MXC_ISI_OUT_FMT_YUV422_2P12,
|
||||
MXC_ISI_OUT_FMT_YUV422_3P12,
|
||||
MXC_ISI_OUT_FMT_YUV420_2P8P = 0x31,
|
||||
MXC_ISI_OUT_FMT_YUV420_3P8P,
|
||||
MXC_ISI_OUT_FMT_YUV420_2P10 = 0x35,
|
||||
MXC_ISI_OUT_FMT_YUV420_3P10,
|
||||
MXC_ISI_OUT_FMT_YUV420_2P10P = 0x39,
|
||||
MXC_ISI_OUT_FMT_YUV420_3P10P,
|
||||
MXC_ISI_OUT_FMT_YUV420_2P12 = 0x3D,
|
||||
MXC_ISI_OUT_FMT_YUV420_3P12,
|
||||
};
|
||||
|
||||
enum mxc_isi_in_fmt {
|
||||
MXC_ISI_IN_FMT_BGR8P = 0x0,
|
||||
};
|
||||
|
||||
enum mxc_isi_m2m_in_fmt {
|
||||
MXC_ISI_M2M_IN_FMT_BGR8P = 0x0,
|
||||
MXC_ISI_M2M_IN_FMT_RGB8P,
|
||||
MXC_ISI_M2M_IN_FMT_XRGB8,
|
||||
MXC_ISI_M2M_IN_FMT_RGBX8,
|
||||
MXC_ISI_M2M_IN_FMT_XBGR8,
|
||||
MXC_ISI_M2M_IN_FMT_RGB565,
|
||||
MXC_ISI_M2M_IN_FMT_A2BGR10,
|
||||
MXC_ISI_M2M_IN_FMT_A2RGB10,
|
||||
MXC_ISI_M2M_IN_FMT_YUV444_1P8P,
|
||||
MXC_ISI_M2M_IN_FMT_YUV444_1P10,
|
||||
MXC_ISI_M2M_IN_FMT_YUV444_1P10P,
|
||||
MXC_ISI_M2M_IN_FMT_YUV444_1P12,
|
||||
MXC_ISI_M2M_IN_FMT_YUV444_1P8,
|
||||
MXC_ISI_M2M_IN_FMT_YUV422_1P8P,
|
||||
MXC_ISI_M2M_IN_FMT_YUV422_1P10,
|
||||
MXC_ISI_M2M_IN_FMT_YUV422_1P10P,
|
||||
};
|
||||
|
||||
struct mxc_isi_fmt {
|
||||
char *name;
|
||||
u32 mbus_code;
|
||||
u32 fourcc;
|
||||
u32 color;
|
||||
u16 memplanes;
|
||||
u16 colplanes;
|
||||
u8 colorspace;
|
||||
u8 depth[MXC_MAX_PLANES];
|
||||
u16 mdataplanes;
|
||||
u16 flags;
|
||||
};
|
||||
|
||||
struct mxc_isi_ctrls {
|
||||
struct v4l2_ctrl_handler handler;
|
||||
struct v4l2_ctrl *hflip;
|
||||
struct v4l2_ctrl *vflip;
|
||||
struct v4l2_ctrl *alpha;
|
||||
struct v4l2_ctrl *num_cap_buf;
|
||||
struct v4l2_ctrl *num_out_buf;
|
||||
bool ready;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct addr - physical address set for DMA
|
||||
* @y: luminance plane physical address
|
||||
* @cb: Cb plane physical address
|
||||
* @cr: Cr plane physical address
|
||||
*/
|
||||
struct frame_addr {
|
||||
u32 y;
|
||||
u32 cb;
|
||||
u32 cr;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mxc_isi_frame - source/target frame properties
|
||||
* o_width: original image width from sensor
|
||||
* o_height: original image height from sensor
|
||||
* c_width: crop image width set by g_selection
|
||||
* c_height: crop image height set by g_selection
|
||||
* h_off: crop horizontal pixel offset
|
||||
* v_off: crop vertical pixel offset
|
||||
* width: out image pixel width
|
||||
* height: out image pixel weight
|
||||
* bytesperline: bytesperline value for each plane
|
||||
* paddr: image frame buffer physical addresses
|
||||
* fmt: color format pointer
|
||||
*/
|
||||
struct mxc_isi_frame {
|
||||
u32 o_width;
|
||||
u32 o_height;
|
||||
u32 c_width;
|
||||
u32 c_height;
|
||||
u32 h_off;
|
||||
u32 v_off;
|
||||
u32 width;
|
||||
u32 height;
|
||||
unsigned int sizeimage[MXC_MAX_PLANES];
|
||||
unsigned int bytesperline[MXC_MAX_PLANES];
|
||||
struct mxc_isi_fmt *fmt;
|
||||
};
|
||||
|
||||
struct mxc_isi_roi_alpha {
|
||||
u8 alpha;
|
||||
struct v4l2_rect rect;
|
||||
};
|
||||
|
||||
struct mxc_isi_buffer {
|
||||
struct vb2_v4l2_buffer v4l2_buf;
|
||||
struct list_head list;
|
||||
struct frame_addr paddr;
|
||||
enum mxc_isi_buf_id id;
|
||||
bool discard;
|
||||
};
|
||||
|
||||
struct mxc_isi_m2m_dev {
|
||||
struct platform_device *pdev;
|
||||
|
||||
struct video_device vdev;
|
||||
struct v4l2_device v4l2_dev;
|
||||
struct v4l2_m2m_dev *m2m_dev;
|
||||
struct v4l2_fh fh;
|
||||
struct v4l2_pix_format_mplane pix;
|
||||
|
||||
struct list_head out_active;
|
||||
struct mxc_isi_ctrls ctrls;
|
||||
|
||||
struct mxc_isi_frame src_f;
|
||||
struct mxc_isi_frame dst_f;
|
||||
|
||||
struct mutex lock;
|
||||
spinlock_t slock;
|
||||
|
||||
unsigned int aborting;
|
||||
unsigned int frame_count;
|
||||
|
||||
u32 req_cap_buf_num;
|
||||
u32 req_out_buf_num;
|
||||
|
||||
u8 id;
|
||||
};
|
||||
|
||||
struct mxc_isi_ctx {
|
||||
struct mxc_isi_m2m_dev *isi_m2m;
|
||||
struct v4l2_fh fh;
|
||||
};
|
||||
|
||||
struct mxc_isi_dev_ops {
|
||||
int (*clk_get)(struct mxc_isi_dev *mxc_isi);
|
||||
int (*clk_enable)(struct mxc_isi_dev *mxc_isi);
|
||||
void (*clk_disable)(struct mxc_isi_dev *mxc_isi);
|
||||
};
|
||||
|
||||
struct mxc_isi_cap_dev {
|
||||
struct v4l2_subdev sd;
|
||||
struct video_device vdev;
|
||||
struct v4l2_fh fh;
|
||||
struct vb2_queue vb2_q;
|
||||
struct v4l2_pix_format_mplane pix;
|
||||
|
||||
struct mxc_isi_dev *mxc_isi;
|
||||
struct platform_device *pdev;
|
||||
struct mxc_isi_ctrls ctrls;
|
||||
struct mxc_isi_buffer buf_discard[2];
|
||||
|
||||
struct media_pad cap_pad;
|
||||
struct media_pad sd_pads[MXC_ISI_SD_PADS_NUM];
|
||||
|
||||
struct list_head out_pending;
|
||||
struct list_head out_active;
|
||||
struct list_head out_discard;
|
||||
|
||||
struct mxc_isi_frame src_f;
|
||||
struct mxc_isi_frame dst_f;
|
||||
|
||||
u32 frame_count;
|
||||
u32 id;
|
||||
|
||||
struct mutex lock;
|
||||
spinlock_t slock;
|
||||
|
||||
/* dirty buffer */
|
||||
size_t discard_size[MXC_MAX_PLANES];
|
||||
void *discard_buffer[MXC_MAX_PLANES];
|
||||
dma_addr_t discard_buffer_dma[MXC_MAX_PLANES];
|
||||
};
|
||||
|
||||
struct mxc_isi_dev {
|
||||
/* Pointer to isi capture child device driver data */
|
||||
struct mxc_isi_cap_dev *isi_cap;
|
||||
|
||||
/* Pointer to isi m2m child device driver data */
|
||||
struct mxc_isi_m2m_dev *isi_m2m;
|
||||
|
||||
struct platform_device *pdev;
|
||||
|
||||
/* clk for imx8qxp/qm platform */
|
||||
struct clk *clk;
|
||||
|
||||
/* clks for imx8mn platform */
|
||||
struct clk *clk_disp_axi;
|
||||
struct clk *clk_disp_apb;
|
||||
struct clk *clk_root_disp_axi;
|
||||
struct clk *clk_root_disp_apb;
|
||||
|
||||
const struct mxc_isi_dev_ops *ops;
|
||||
|
||||
struct reset_control *soft_resetn;
|
||||
struct reset_control *clk_enable;
|
||||
|
||||
struct mutex lock;
|
||||
spinlock_t slock;
|
||||
|
||||
void __iomem *regs;
|
||||
|
||||
u8 chain_buf;
|
||||
u8 alpha;
|
||||
bool m2m_enabled;
|
||||
|
||||
/* manage share ISI channel resource */
|
||||
atomic_t usage_count;
|
||||
|
||||
/* scale factor */
|
||||
u32 xfactor;
|
||||
u32 yfactor;
|
||||
u32 pre_dec_x;
|
||||
u32 pre_dec_y;
|
||||
|
||||
u32 status;
|
||||
|
||||
u32 interface[MAX_PORTS];
|
||||
int id;
|
||||
|
||||
unsigned int hflip:1;
|
||||
unsigned int vflip:1;
|
||||
unsigned int cscen:1;
|
||||
unsigned int scale:1;
|
||||
unsigned int alphaen:1;
|
||||
unsigned int crop:1;
|
||||
unsigned int deinterlace:1;
|
||||
unsigned int is_streaming:1;
|
||||
};
|
||||
|
||||
static inline void set_frame_bounds(struct mxc_isi_frame *f,
|
||||
u32 width, u32 height)
|
||||
{
|
||||
f->o_width = width;
|
||||
f->o_height = height;
|
||||
f->c_width = width;
|
||||
f->c_height = height;
|
||||
f->width = width;
|
||||
f->height = height;
|
||||
}
|
||||
|
||||
static inline void set_frame_out(struct mxc_isi_frame *f,
|
||||
u32 width, u32 height)
|
||||
{
|
||||
f->c_width = width;
|
||||
f->c_height = height;
|
||||
f->width = width;
|
||||
f->height = height;
|
||||
}
|
||||
|
||||
static inline void set_frame_crop(struct mxc_isi_frame *f,
|
||||
u32 left, u32 top, u32 width, u32 height)
|
||||
{
|
||||
f->h_off = left;
|
||||
f->v_off = top;
|
||||
f->c_width = width;
|
||||
f->c_height = height;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_IMX8_ISI_CORE)
|
||||
struct mxc_isi_dev *mxc_isi_get_hostdata(struct platform_device *pdev);
|
||||
struct device *mxc_isi_dev_get_parent(struct platform_device *pdev);
|
||||
#else
|
||||
static inline struct mxc_isi_dev *mxc_isi_get_hostdata(struct platform_device *pdev) {}
|
||||
static inline struct struct device *mxc_isi_dev_get_parent(struct platform_device *pdev) {}
|
||||
#endif
|
||||
|
||||
#endif /* __MXC_ISI_CORE_H__ */
|
734
drivers/staging/media/imx/imx8-isi-hw.c
Normal file
734
drivers/staging/media/imx/imx8-isi-hw.c
Normal file
|
@ -0,0 +1,734 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2019 NXP Semiconductor
|
||||
*
|
||||
*/
|
||||
|
||||
#include <dt-bindings/pinctrl/pads-imx8qxp.h>
|
||||
|
||||
#include "imx8-isi-hw.h"
|
||||
#include "imx8-common.h"
|
||||
|
||||
#define ISI_DOWNSCALE_THRESHOLD 0x4000
|
||||
|
||||
#ifdef DEBUG
|
||||
void dump_isi_regs(struct mxc_isi_dev *mxc_isi)
|
||||
{
|
||||
struct device *dev = &mxc_isi->pdev->dev;
|
||||
struct {
|
||||
u32 offset;
|
||||
const char *const name[64];
|
||||
} registers[] = {
|
||||
{ 0x00h, "CHNL_CTRL" },
|
||||
{ 0x04h, "CHNL_IMG_CTRL" },
|
||||
{ 0x08h, "CHNL_OUT_BUF_CTRL" },
|
||||
{ 0x0Ch, "CHNL_IMG_CFG" },
|
||||
{ 0x10h, "CHNL_IER" },
|
||||
{ 0x14h, "CHNL_STS" },
|
||||
{ 0x18h, "CHNL_SCALE_FACTOR" },
|
||||
{ 0x1Ch, "CHNL_SCALE_OFFSET" },
|
||||
{ 0x20h, "CHNL_CROP_ULC" },
|
||||
{ 0x24h, "CHNL_CROP_LRC" },
|
||||
{ 0x28h, "CHNL_CSC_COEFF0" },
|
||||
{ 0x2Ch, "CHNL_CSC_COEFF1" },
|
||||
{ 0x30h, "CHNL_CSC_COEFF2" },
|
||||
{ 0x34h, "CHNL_CSC_COEFF3" },
|
||||
{ 0x38h, "CHNL_CSC_COEFF4" },
|
||||
{ 0x3Ch, "CHNL_CSC_COEFF5" },
|
||||
{ 0x40h, "CHNL_ROI_0_ALPHA" },
|
||||
{ 0x44h, "CHNL_ROI_0_ULC" },
|
||||
{ 0x48h, "CHNL_ROI_0_LRC" },
|
||||
{ 0x4Ch, "CHNL_ROI_1_ALPHA" },
|
||||
{ 0x50h, "CHNL_ROI_1_ULC" },
|
||||
{ 0x54h, "CHNL_ROI_1_LRC" },
|
||||
{ 0x58h, "CHNL_ROI_2_ALPHA" },
|
||||
{ 0x5Ch, "CHNL_ROI_2_ULC" },
|
||||
{ 0x60h, "CHNL_ROI_2_LRC" },
|
||||
{ 0x64h, "CHNL_ROI_3_ALPHA" },
|
||||
{ 0x68h, "CHNL_ROI_3_ULC" },
|
||||
{ 0x6Ch, "CHNL_ROI_3_LRC" },
|
||||
{ 0x70h, "CHNL_OUT_BUF1_ADDR_Y" },
|
||||
{ 0x74h, "CHNL_OUT_BUF1_ADDR_U" },
|
||||
{ 0x78h, "CHNL_OUT_BUF1_ADDR_V" },
|
||||
{ 0x7Ch, "CHNL_OUT_BUF_PITCH" },
|
||||
{ 0x80h, "CHNL_IN_BUF_ADDR" },
|
||||
{ 0x84h, "CHNL_IN_BUF_PITCH" },
|
||||
{ 0x88h, "CHNL_MEM_RD_CTRL" },
|
||||
{ 0x8Ch, "CHNL_OUT_BUF2_ADDR_Y" },
|
||||
{ 0x90h, "CHNL_OUT_BUF2_ADDR_U" },
|
||||
{ 0x94h, "CHNL_OUT_BUF2_ADDR_V" },
|
||||
{ 0x98h, "CHNL_SCL_IMG_CFG" },
|
||||
{ 0x9Ch, "CHNL_FLOW_CTRL" },
|
||||
};
|
||||
u32 i;
|
||||
|
||||
dev_dbg(dev, "ISI CHNLC register dump, isi%d\n", mxc_isi->id);
|
||||
for (i = 0; i < ARRAY_SIZE(registers); i++) {
|
||||
u32 reg = readl(mxc_isi->regs + registers.offset);
|
||||
dev_dbg(dev, "%20s[0x%.2x]: %.2x\n",
|
||||
registers.name, registers.offset, reg);
|
||||
}
|
||||
}
|
||||
#else
|
||||
void dump_isi_regs(struct mxc_isi_dev *mxc_isi)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* A2,A1, B1, A3, B3, B2,
|
||||
* C2, C1, D1, C3, D3, D2
|
||||
*/
|
||||
static const u32 coeffs[2][6] = {
|
||||
/* YUV2RGB */
|
||||
{ 0x0000012A, 0x012A0198, 0x0730079C,
|
||||
0x0204012A, 0x01F00000, 0x01800180 },
|
||||
|
||||
/* RGB->YUV */
|
||||
{ 0x00810041, 0x07db0019, 0x007007b6,
|
||||
0x07a20070, 0x001007ee, 0x00800080 },
|
||||
};
|
||||
|
||||
static void printk_pixelformat(char *prefix, int val)
|
||||
{
|
||||
pr_info("%s %c%c%c%c\n", prefix ? prefix : "pixelformat",
|
||||
val & 0xff,
|
||||
(val >> 8) & 0xff,
|
||||
(val >> 16) & 0xff,
|
||||
(val >> 24) & 0xff);
|
||||
}
|
||||
|
||||
static bool is_rgb(u32 pix_fmt)
|
||||
{
|
||||
if ((pix_fmt == V4L2_PIX_FMT_RGB565) ||
|
||||
(pix_fmt == V4L2_PIX_FMT_RGB24) ||
|
||||
(pix_fmt == V4L2_PIX_FMT_RGB32) ||
|
||||
(pix_fmt == V4L2_PIX_FMT_BGR32) ||
|
||||
(pix_fmt == V4L2_PIX_FMT_XRGB32) ||
|
||||
(pix_fmt == V4L2_PIX_FMT_XBGR32) ||
|
||||
(pix_fmt == V4L2_PIX_FMT_BGR24) ||
|
||||
(pix_fmt == V4L2_PIX_FMT_RGBA) ||
|
||||
(pix_fmt == V4L2_PIX_FMT_ABGR32) ||
|
||||
(pix_fmt == V4L2_PIX_FMT_ARGB32))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool is_yuv(u32 pix_fmt)
|
||||
{
|
||||
if ((pix_fmt == V4L2_PIX_FMT_YUYV) ||
|
||||
(pix_fmt == V4L2_PIX_FMT_YUV32) ||
|
||||
(pix_fmt == V4L2_PIX_FMT_YUV444M) ||
|
||||
(pix_fmt == V4L2_PIX_FMT_YUV24) ||
|
||||
(pix_fmt == V4L2_PIX_FMT_NV12))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static void chain_buf(struct mxc_isi_dev *mxc_isi, struct mxc_isi_frame *frm)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
if (frm->o_width > ISI_2K) {
|
||||
val = readl(mxc_isi->regs + CHNL_CTRL);
|
||||
val &= ~CHNL_CTRL_CHAIN_BUF_MASK;
|
||||
val |= (CHNL_CTRL_CHAIN_BUF_2_CHAIN << CHNL_CTRL_CHAIN_BUF_OFFSET);
|
||||
writel(val, mxc_isi->regs + CHNL_CTRL);
|
||||
} else if (!mxc_isi->chain_buf) {
|
||||
val = readl(mxc_isi->regs + CHNL_CTRL);
|
||||
val &= ~CHNL_CTRL_CHAIN_BUF_MASK;
|
||||
writel(val, mxc_isi->regs + CHNL_CTRL);
|
||||
}
|
||||
}
|
||||
|
||||
void mxc_isi_channel_set_outbuf(struct mxc_isi_dev *mxc_isi,
|
||||
struct mxc_isi_buffer *buf)
|
||||
{
|
||||
struct vb2_buffer *vb2_buf = &buf->v4l2_buf.vb2_buf;
|
||||
u32 framecount = buf->v4l2_buf.sequence;
|
||||
struct frame_addr *paddr = &buf->paddr;
|
||||
struct mxc_isi_cap_dev *isi_cap;
|
||||
struct v4l2_pix_format_mplane *pix;
|
||||
int val = 0;
|
||||
|
||||
if (buf->discard) {
|
||||
isi_cap = mxc_isi->isi_cap;
|
||||
pix = &isi_cap->pix;
|
||||
paddr->y = isi_cap->discard_buffer_dma[0];
|
||||
if (pix->num_planes == 2)
|
||||
paddr->cb = isi_cap->discard_buffer_dma[1];
|
||||
if (pix->num_planes == 3) {
|
||||
paddr->cb = isi_cap->discard_buffer_dma[1];
|
||||
paddr->cr = isi_cap->discard_buffer_dma[2];
|
||||
}
|
||||
} else {
|
||||
paddr->y = vb2_dma_contig_plane_dma_addr(vb2_buf, 0);
|
||||
|
||||
if (vb2_buf->num_planes == 2)
|
||||
paddr->cb = vb2_dma_contig_plane_dma_addr(vb2_buf, 1);
|
||||
if (vb2_buf->num_planes == 3) {
|
||||
paddr->cb = vb2_dma_contig_plane_dma_addr(vb2_buf, 1);
|
||||
paddr->cr = vb2_dma_contig_plane_dma_addr(vb2_buf, 2);
|
||||
}
|
||||
}
|
||||
|
||||
val = readl(mxc_isi->regs + CHNL_OUT_BUF_CTRL);
|
||||
|
||||
if (framecount == 0 || ((mxc_isi->status & 0x100) && (framecount != 1))) {
|
||||
writel(paddr->y, mxc_isi->regs + CHNL_OUT_BUF1_ADDR_Y);
|
||||
writel(paddr->cb, mxc_isi->regs + CHNL_OUT_BUF1_ADDR_U);
|
||||
writel(paddr->cr, mxc_isi->regs + CHNL_OUT_BUF1_ADDR_V);
|
||||
val ^= CHNL_OUT_BUF_CTRL_LOAD_BUF1_ADDR_MASK;
|
||||
buf->id = MXC_ISI_BUF1;
|
||||
} else if (framecount == 1 || mxc_isi->status & 0x200) {
|
||||
writel(paddr->y, mxc_isi->regs + CHNL_OUT_BUF2_ADDR_Y);
|
||||
writel(paddr->cb, mxc_isi->regs + CHNL_OUT_BUF2_ADDR_U);
|
||||
writel(paddr->cr, mxc_isi->regs + CHNL_OUT_BUF2_ADDR_V);
|
||||
val ^= CHNL_OUT_BUF_CTRL_LOAD_BUF2_ADDR_MASK;
|
||||
buf->id = MXC_ISI_BUF2;
|
||||
}
|
||||
writel(val, mxc_isi->regs + CHNL_OUT_BUF_CTRL);
|
||||
}
|
||||
|
||||
void mxc_isi_channel_set_m2m_src_addr(struct mxc_isi_dev *mxc_isi,
|
||||
struct mxc_isi_buffer *buf)
|
||||
{
|
||||
struct vb2_buffer *vb2_buf = &buf->v4l2_buf.vb2_buf;
|
||||
struct frame_addr *paddr = &buf->paddr;
|
||||
|
||||
/* Only support one plane */
|
||||
paddr->y = vb2_dma_contig_plane_dma_addr(vb2_buf, 0);
|
||||
writel(paddr->y, mxc_isi->regs + CHNL_IN_BUF_ADDR);
|
||||
}
|
||||
|
||||
void mxc_isi_channel_sw_reset(struct mxc_isi_dev *mxc_isi)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl(mxc_isi->regs + CHNL_CTRL);
|
||||
val |= CHNL_CTRL_SW_RST;
|
||||
writel(val, mxc_isi->regs + CHNL_CTRL);
|
||||
mdelay(5);
|
||||
val &= ~CHNL_CTRL_SW_RST;
|
||||
writel(val, mxc_isi->regs + CHNL_CTRL);
|
||||
}
|
||||
|
||||
void mxc_isi_channel_source_config(struct mxc_isi_dev *mxc_isi)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl(mxc_isi->regs + CHNL_CTRL);
|
||||
val &= ~(CHNL_CTRL_MIPI_VC_ID_MASK |
|
||||
CHNL_CTRL_SRC_INPUT_MASK | CHNL_CTRL_SRC_TYPE_MASK);
|
||||
|
||||
switch (mxc_isi->interface[IN_PORT]) {
|
||||
case ISI_INPUT_INTERFACE_MIPI0_CSI2:
|
||||
val |= CHNL_CTRL_SRC_INPUT_MIPI0;
|
||||
if (mxc_isi->interface[SUB_IN_PORT] <= CHNL_CTRL_MIPI_VC_ID_VC3 &&
|
||||
mxc_isi->interface[SUB_IN_PORT] >= CHNL_CTRL_MIPI_VC_ID_VC0)
|
||||
val |= (mxc_isi->interface[SUB_IN_PORT] << CHNL_CTRL_MIPI_VC_ID_OFFSET);
|
||||
break;
|
||||
case ISI_INPUT_INTERFACE_MIPI1_CSI2:
|
||||
val |= CHNL_CTRL_SRC_INPUT_MIPI1;
|
||||
if (mxc_isi->interface[SUB_IN_PORT] <= CHNL_CTRL_MIPI_VC_ID_VC3 &&
|
||||
mxc_isi->interface[SUB_IN_PORT] >= CHNL_CTRL_MIPI_VC_ID_VC0)
|
||||
val |= (mxc_isi->interface[SUB_IN_PORT] << CHNL_CTRL_MIPI_VC_ID_OFFSET);
|
||||
break;
|
||||
case ISI_INPUT_INTERFACE_DC0:
|
||||
val |= CHNL_CTRL_SRC_INPUT_DC0;
|
||||
break;
|
||||
case ISI_INPUT_INTERFACE_DC1:
|
||||
val |= CHNL_CTRL_SRC_INPUT_DC1;
|
||||
break;
|
||||
case ISI_INPUT_INTERFACE_HDMI:
|
||||
val |= CHNL_CTRL_SRC_INPUT_HDMI;
|
||||
break;
|
||||
case ISI_INPUT_INTERFACE_PARALLEL_CSI:
|
||||
val |= CHNL_CTRL_SRC_INPUT_CSI;
|
||||
break;
|
||||
case ISI_INPUT_INTERFACE_MEM:
|
||||
val |= CHNL_CTRL_SRC_INPUT_MEMORY;
|
||||
val |= (CHNL_CTRL_SRC_TYPE_MEMORY << CHNL_CTRL_SRC_TYPE_OFFSET);
|
||||
break;
|
||||
default:
|
||||
dev_err(&mxc_isi->pdev->dev, "invalid interface\n");
|
||||
break;
|
||||
}
|
||||
|
||||
writel(val, mxc_isi->regs + CHNL_CTRL);
|
||||
}
|
||||
|
||||
void mxc_isi_channel_set_flip(struct mxc_isi_dev *mxc_isi)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl(mxc_isi->regs + CHNL_IMG_CTRL);
|
||||
val &= ~(CHNL_IMG_CTRL_VFLIP_EN_MASK | CHNL_IMG_CTRL_HFLIP_EN_MASK);
|
||||
|
||||
if (mxc_isi->vflip)
|
||||
val |= (CHNL_IMG_CTRL_VFLIP_EN_ENABLE << CHNL_IMG_CTRL_VFLIP_EN_OFFSET);
|
||||
if (mxc_isi->hflip)
|
||||
val |= (CHNL_IMG_CTRL_HFLIP_EN_ENABLE << CHNL_IMG_CTRL_HFLIP_EN_OFFSET);
|
||||
|
||||
writel(val, mxc_isi->regs + CHNL_IMG_CTRL);
|
||||
}
|
||||
|
||||
void mxc_isi_channel_set_csc(struct mxc_isi_dev *mxc_isi,
|
||||
struct mxc_isi_frame *src_f,
|
||||
struct mxc_isi_frame *dst_f)
|
||||
{
|
||||
struct mxc_isi_fmt *src_fmt = src_f->fmt;
|
||||
struct mxc_isi_fmt *dst_fmt = dst_f->fmt;
|
||||
u32 val, csc = 0;
|
||||
|
||||
val = readl(mxc_isi->regs + CHNL_IMG_CTRL);
|
||||
val &= ~(CHNL_IMG_CTRL_FORMAT_MASK |
|
||||
CHNL_IMG_CTRL_YCBCR_MODE_MASK |
|
||||
CHNL_IMG_CTRL_CSC_BYPASS_MASK |
|
||||
CHNL_IMG_CTRL_CSC_MODE_MASK);
|
||||
|
||||
/* set outbuf format */
|
||||
val |= dst_fmt->color << CHNL_IMG_CTRL_FORMAT_OFFSET;
|
||||
|
||||
mxc_isi->cscen = 1;
|
||||
|
||||
if (is_yuv(src_fmt->fourcc) && is_rgb(dst_fmt->fourcc)) {
|
||||
/* YUV2RGB */
|
||||
csc = YUV2RGB;
|
||||
/* YCbCr enable??? */
|
||||
val |= (CHNL_IMG_CTRL_CSC_MODE_YCBCR2RGB << CHNL_IMG_CTRL_CSC_MODE_OFFSET);
|
||||
val |= (CHNL_IMG_CTRL_YCBCR_MODE_ENABLE << CHNL_IMG_CTRL_YCBCR_MODE_OFFSET);
|
||||
} else if (is_rgb(src_fmt->fourcc) && is_yuv(dst_fmt->fourcc)) {
|
||||
/* RGB2YUV */
|
||||
csc = RGB2YUV;
|
||||
val |= (CHNL_IMG_CTRL_CSC_MODE_RGB2YCBCR << CHNL_IMG_CTRL_CSC_MODE_OFFSET);
|
||||
} else {
|
||||
/* Bypass CSC */
|
||||
pr_info("bypass csc\n");
|
||||
mxc_isi->cscen = 0;
|
||||
val |= CHNL_IMG_CTRL_CSC_BYPASS_ENABLE;
|
||||
}
|
||||
|
||||
printk_pixelformat("input fmt", src_fmt->fourcc);
|
||||
printk_pixelformat("output fmt", dst_fmt->fourcc);
|
||||
|
||||
if (mxc_isi->cscen) {
|
||||
writel(coeffs[csc][0], mxc_isi->regs + CHNL_CSC_COEFF0);
|
||||
writel(coeffs[csc][1], mxc_isi->regs + CHNL_CSC_COEFF1);
|
||||
writel(coeffs[csc][2], mxc_isi->regs + CHNL_CSC_COEFF2);
|
||||
writel(coeffs[csc][3], mxc_isi->regs + CHNL_CSC_COEFF3);
|
||||
writel(coeffs[csc][4], mxc_isi->regs + CHNL_CSC_COEFF4);
|
||||
writel(coeffs[csc][5], mxc_isi->regs + CHNL_CSC_COEFF5);
|
||||
}
|
||||
|
||||
writel(val, mxc_isi->regs + CHNL_IMG_CTRL);
|
||||
}
|
||||
|
||||
void mxc_isi_channel_set_alpha_roi0(struct mxc_isi_dev *mxc_isi,
|
||||
struct v4l2_rect *rect)
|
||||
{
|
||||
u32 val0, val1;
|
||||
|
||||
val0 = (rect->left << 16) | rect->top;
|
||||
writel(val0, mxc_isi->regs + CHNL_ROI_0_ULC);
|
||||
val1 = (rect->width << 16) | rect->height;
|
||||
writel(val0 + val1, mxc_isi->regs + CHNL_ROI_0_LRC);
|
||||
}
|
||||
|
||||
void mxc_isi_channel_set_alpha(struct mxc_isi_dev *mxc_isi)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl(mxc_isi->regs + CHNL_IMG_CTRL);
|
||||
val &= ~(CHNL_IMG_CTRL_GBL_ALPHA_VAL_MASK | CHNL_IMG_CTRL_GBL_ALPHA_EN_MASK);
|
||||
|
||||
if (mxc_isi->alphaen)
|
||||
val |= ((mxc_isi->alpha << CHNL_IMG_CTRL_GBL_ALPHA_VAL_OFFSET) |
|
||||
(CHNL_IMG_CTRL_GBL_ALPHA_EN_ENABLE << CHNL_IMG_CTRL_GBL_ALPHA_EN_OFFSET));
|
||||
|
||||
writel(val, mxc_isi->regs + CHNL_IMG_CTRL);
|
||||
}
|
||||
|
||||
void mxc_isi_channel_set_chain_buf(struct mxc_isi_dev *mxc_isi)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
if (mxc_isi->chain_buf) {
|
||||
val = readl(mxc_isi->regs + CHNL_CTRL);
|
||||
val &= ~CHNL_CTRL_CHAIN_BUF_MASK;
|
||||
val |= (CHNL_CTRL_CHAIN_BUF_2_CHAIN << CHNL_CTRL_CHAIN_BUF_OFFSET);
|
||||
|
||||
writel(val, mxc_isi->regs + CHNL_CTRL);
|
||||
}
|
||||
}
|
||||
|
||||
void mxc_isi_channel_deinterlace_init(struct mxc_isi_dev *mxc_isi)
|
||||
{
|
||||
/* Config for Blending deinterlace */
|
||||
}
|
||||
|
||||
void mxc_isi_channel_set_deinterlace(struct mxc_isi_dev *mxc_isi)
|
||||
{
|
||||
/* de-interlacing method
|
||||
* Weaving-------------Yes
|
||||
* Line Doubling-------No
|
||||
* Blending -----------TODO
|
||||
*/
|
||||
u32 val;
|
||||
|
||||
val = readl(mxc_isi->regs + CHNL_IMG_CTRL);
|
||||
val &= ~CHNL_IMG_CTRL_DEINT_MASK;
|
||||
if (mxc_isi->deinterlace)
|
||||
val |= mxc_isi->deinterlace << CHNL_IMG_CTRL_DEINT_OFFSET;
|
||||
if (mxc_isi->deinterlace == CHNL_IMG_CTRL_DEINT_LDOUBLE_ODD_EVEN ||
|
||||
mxc_isi->deinterlace == CHNL_IMG_CTRL_DEINT_LDOUBLE_EVEN_ODD)
|
||||
mxc_isi_channel_deinterlace_init(mxc_isi);
|
||||
|
||||
writel(val, mxc_isi->regs + CHNL_IMG_CTRL);
|
||||
}
|
||||
|
||||
void mxc_isi_channel_set_crop(struct mxc_isi_dev *mxc_isi)
|
||||
{
|
||||
struct mxc_isi_frame *src_f = &mxc_isi->isi_cap->src_f;
|
||||
struct v4l2_rect crop;
|
||||
u32 val, val0, val1, temp;
|
||||
|
||||
val = readl(mxc_isi->regs + CHNL_IMG_CTRL);
|
||||
val &= ~CHNL_IMG_CTRL_CROP_EN_MASK;
|
||||
|
||||
if ((src_f->o_height == src_f->height) &&
|
||||
(src_f->o_width == src_f->width)) {
|
||||
mxc_isi->crop = 0;
|
||||
writel(val, mxc_isi->regs + CHNL_IMG_CTRL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mxc_isi->scale) {
|
||||
temp = (src_f->h_off << 12) / mxc_isi->xfactor;
|
||||
crop.left = temp >> mxc_isi->pre_dec_x;
|
||||
temp = (src_f->v_off << 12) / mxc_isi->yfactor;
|
||||
crop.top = temp >> mxc_isi->pre_dec_y;
|
||||
temp = (src_f->width << 12) / mxc_isi->xfactor;
|
||||
crop.width = temp >> mxc_isi->pre_dec_x;
|
||||
temp = (src_f->height << 12) / mxc_isi->yfactor;
|
||||
crop.height = temp >> mxc_isi->pre_dec_y;
|
||||
} else {
|
||||
crop.left = src_f->h_off;
|
||||
crop.top = src_f->v_off;
|
||||
crop.width = src_f->width;
|
||||
crop.height = src_f->height;
|
||||
}
|
||||
|
||||
mxc_isi->crop = 1;
|
||||
val |= (CHNL_IMG_CTRL_CROP_EN_ENABLE << CHNL_IMG_CTRL_CROP_EN_OFFSET);
|
||||
val0 = crop.top | (crop.left << CHNL_CROP_ULC_X_OFFSET);
|
||||
val1 = crop.height | (crop.width << CHNL_CROP_LRC_X_OFFSET);
|
||||
|
||||
writel(val0, mxc_isi->regs + CHNL_CROP_ULC);
|
||||
writel((val1 + val0), mxc_isi->regs + CHNL_CROP_LRC);
|
||||
writel(val, mxc_isi->regs + CHNL_IMG_CTRL);
|
||||
}
|
||||
|
||||
static void mxc_isi_channel_clear_scaling(struct mxc_isi_dev *mxc_isi)
|
||||
{
|
||||
u32 val0;
|
||||
|
||||
writel(0x10001000, mxc_isi->regs + CHNL_SCALE_FACTOR);
|
||||
|
||||
val0 = readl(mxc_isi->regs + CHNL_IMG_CTRL);
|
||||
val0 &= ~(CHNL_IMG_CTRL_DEC_X_MASK | CHNL_IMG_CTRL_DEC_Y_MASK);
|
||||
writel(val0, mxc_isi->regs + CHNL_IMG_CTRL);
|
||||
}
|
||||
|
||||
void mxc_isi_channel_set_scaling(struct mxc_isi_dev *mxc_isi,
|
||||
struct mxc_isi_frame *src_f,
|
||||
struct mxc_isi_frame *dst_f)
|
||||
{
|
||||
u32 decx, decy;
|
||||
u32 xscale, yscale;
|
||||
u32 xdec = 0, ydec = 0;
|
||||
u32 val0, val1;
|
||||
|
||||
if (dst_f->height == src_f->height ||
|
||||
dst_f->width == src_f->width) {
|
||||
mxc_isi->scale = 0;
|
||||
mxc_isi_channel_clear_scaling(mxc_isi);
|
||||
dev_dbg(&mxc_isi->pdev->dev, "%s: no scale\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
dev_info(&mxc_isi->pdev->dev, "input_size(%d,%d), output_size(%d,%d)\n",
|
||||
src_f->width, src_f->height, dst_f->width, dst_f->height);
|
||||
|
||||
mxc_isi->scale = 1;
|
||||
|
||||
decx = src_f->width / dst_f->width;
|
||||
decy = src_f->height / dst_f->height;
|
||||
|
||||
if (decx > 1) {
|
||||
/* Down */
|
||||
if (decx >= 2 && decx < 4) {
|
||||
decx = 2;
|
||||
xdec = 1;
|
||||
} else if (decx >= 4 && decx < 8) {
|
||||
decx = 4;
|
||||
xdec = 2;
|
||||
} else if (decx >= 8) {
|
||||
decx = 8;
|
||||
xdec = 3;
|
||||
}
|
||||
xscale = src_f->width * 0x1000 / (dst_f->width * decx);
|
||||
} else {
|
||||
/* Up */
|
||||
xscale = src_f->width * 0x1000 / dst_f->width;
|
||||
}
|
||||
|
||||
if (decy > 1) {
|
||||
if (decy >= 2 && decy < 4) {
|
||||
decy = 2;
|
||||
ydec = 1;
|
||||
} else if (decy >= 4 && decy < 8) {
|
||||
decy = 4;
|
||||
ydec = 2;
|
||||
} else if (decy >= 8) {
|
||||
decy = 8;
|
||||
ydec = 3;
|
||||
}
|
||||
yscale = src_f->height * 0x1000 / (dst_f->height * decy);
|
||||
} else {
|
||||
yscale = src_f->height * 0x1000 / dst_f->height;
|
||||
}
|
||||
|
||||
val0 = readl(mxc_isi->regs + CHNL_IMG_CTRL);
|
||||
val0 |= CHNL_IMG_CTRL_YCBCR_MODE_MASK;//YCbCr Sandor???
|
||||
val0 &= ~(CHNL_IMG_CTRL_DEC_X_MASK | CHNL_IMG_CTRL_DEC_Y_MASK);
|
||||
val0 |= (xdec << CHNL_IMG_CTRL_DEC_X_OFFSET) |
|
||||
(ydec << CHNL_IMG_CTRL_DEC_Y_OFFSET);
|
||||
writel(val0, mxc_isi->regs + CHNL_IMG_CTRL);
|
||||
|
||||
if (xscale > ISI_DOWNSCALE_THRESHOLD)
|
||||
xscale = ISI_DOWNSCALE_THRESHOLD;
|
||||
if (yscale > ISI_DOWNSCALE_THRESHOLD)
|
||||
yscale = ISI_DOWNSCALE_THRESHOLD;
|
||||
|
||||
val1 = xscale | (yscale << CHNL_SCALE_FACTOR_Y_SCALE_OFFSET);
|
||||
|
||||
writel(val1, mxc_isi->regs + CHNL_SCALE_FACTOR);
|
||||
|
||||
/* Update scale config if scaling enabled */
|
||||
val1 = dst_f->o_width | (dst_f->o_height << CHNL_SCL_IMG_CFG_HEIGHT_OFFSET);
|
||||
writel(val1, mxc_isi->regs + CHNL_SCL_IMG_CFG);
|
||||
|
||||
writel(0, mxc_isi->regs + CHNL_SCALE_OFFSET);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void mxc_isi_channel_init(struct mxc_isi_dev *mxc_isi)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/* sw reset */
|
||||
mxc_isi_channel_sw_reset(mxc_isi);
|
||||
|
||||
/* Init channel clk first */
|
||||
val = readl(mxc_isi->regs + CHNL_CTRL);
|
||||
val |= (CHNL_CTRL_CLK_EN_ENABLE << CHNL_CTRL_CLK_EN_OFFSET);
|
||||
writel(val, mxc_isi->regs + CHNL_CTRL);
|
||||
}
|
||||
|
||||
void mxc_isi_channel_deinit(struct mxc_isi_dev *mxc_isi)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/* sw reset */
|
||||
mxc_isi_channel_sw_reset(mxc_isi);
|
||||
|
||||
/* deinit channel clk first */
|
||||
val = (CHNL_CTRL_CLK_EN_ENABLE << CHNL_CTRL_CLK_EN_OFFSET);
|
||||
writel(val, mxc_isi->regs + CHNL_CTRL);
|
||||
}
|
||||
|
||||
void mxc_isi_channel_config(struct mxc_isi_dev *mxc_isi,
|
||||
struct mxc_isi_frame *src_f,
|
||||
struct mxc_isi_frame *dst_f)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/* images having higher than 2048 horizontal resolution */
|
||||
chain_buf(mxc_isi, src_f);
|
||||
|
||||
/* config output frame size and format */
|
||||
val = src_f->o_width | (src_f->o_height << CHNL_IMG_CFG_HEIGHT_OFFSET);
|
||||
writel(val, mxc_isi->regs + CHNL_IMG_CFG);
|
||||
|
||||
/* scale size need to equal input size when scaling disabled*/
|
||||
writel(val, mxc_isi->regs + CHNL_SCL_IMG_CFG);
|
||||
|
||||
/* check csc and scaling */
|
||||
mxc_isi_channel_set_csc(mxc_isi, src_f, dst_f);
|
||||
|
||||
mxc_isi_channel_set_scaling(mxc_isi, src_f, dst_f);
|
||||
|
||||
/* select the source input / src type / virtual channel for mipi*/
|
||||
mxc_isi_channel_source_config(mxc_isi);
|
||||
|
||||
/* line pitch */
|
||||
val = dst_f->bytesperline[0];
|
||||
writel(val, mxc_isi->regs + CHNL_OUT_BUF_PITCH);
|
||||
|
||||
/* TODO */
|
||||
mxc_isi_channel_set_flip(mxc_isi);
|
||||
|
||||
mxc_isi_channel_set_alpha(mxc_isi);
|
||||
|
||||
val = readl(mxc_isi->regs + CHNL_CTRL);
|
||||
val &= ~CHNL_CTRL_CHNL_BYPASS_MASK;
|
||||
|
||||
/* Bypass channel */
|
||||
if (!mxc_isi->cscen && !mxc_isi->scale)
|
||||
val |= (CHNL_CTRL_CHNL_BYPASS_ENABLE << CHNL_CTRL_CHNL_BYPASS_OFFSET);
|
||||
|
||||
writel(val, mxc_isi->regs + CHNL_CTRL);
|
||||
}
|
||||
|
||||
void mxc_isi_clean_registers(struct mxc_isi_dev *mxc_isi)
|
||||
{
|
||||
u32 status;
|
||||
|
||||
status = mxc_isi_get_irq_status(mxc_isi);
|
||||
mxc_isi_clean_irq_status(mxc_isi, status);
|
||||
}
|
||||
|
||||
void mxc_isi_channel_enable(struct mxc_isi_dev *mxc_isi, bool m2m_enabled)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl(mxc_isi->regs + CHNL_CTRL);
|
||||
val |= 0xff << CHNL_CTRL_BLANK_PXL_OFFSET;
|
||||
|
||||
if (m2m_enabled) {
|
||||
val &= ~(CHNL_CTRL_SRC_TYPE_MASK | CHNL_CTRL_SRC_INPUT_MASK);
|
||||
val |= (CHNL_CTRL_SRC_INPUT_MEMORY << CHNL_CTRL_SRC_INPUT_OFFSET |
|
||||
CHNL_CTRL_SRC_TYPE_MEMORY << CHNL_CTRL_SRC_TYPE_OFFSET);
|
||||
}
|
||||
|
||||
val &= ~CHNL_CTRL_CHNL_EN_MASK;
|
||||
val |= CHNL_CTRL_CHNL_EN_ENABLE << CHNL_CTRL_CHNL_EN_OFFSET;
|
||||
writel(val, mxc_isi->regs + CHNL_CTRL);
|
||||
|
||||
mxc_isi_clean_registers(mxc_isi);
|
||||
mxc_isi_enable_irq(mxc_isi);
|
||||
|
||||
if (m2m_enabled) {
|
||||
mxc_isi_m2m_start_read(mxc_isi);
|
||||
return;
|
||||
}
|
||||
|
||||
dump_isi_regs(mxc_isi);
|
||||
msleep(300);
|
||||
}
|
||||
|
||||
void mxc_isi_channel_disable(struct mxc_isi_dev *mxc_isi)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
mxc_isi_disable_irq(mxc_isi);
|
||||
|
||||
val = readl(mxc_isi->regs + CHNL_CTRL);
|
||||
val &= ~(CHNL_CTRL_CHNL_EN_MASK | CHNL_CTRL_CLK_EN_MASK);
|
||||
val |= (CHNL_CTRL_CHNL_EN_DISABLE << CHNL_CTRL_CHNL_EN_OFFSET);
|
||||
val |= (CHNL_CTRL_CLK_EN_DISABLE << CHNL_CTRL_CLK_EN_OFFSET);
|
||||
writel(val, mxc_isi->regs + CHNL_CTRL);
|
||||
}
|
||||
|
||||
void mxc_isi_enable_irq(struct mxc_isi_dev *mxc_isi)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = CHNL_IER_FRM_RCVD_EN_MASK |
|
||||
CHNL_IER_OFLW_Y_BUF_EN_MASK |
|
||||
CHNL_IER_AXI_WR_ERR_U_EN_MASK |
|
||||
CHNL_IER_AXI_WR_ERR_V_EN_MASK |
|
||||
CHNL_IER_AXI_WR_ERR_Y_EN_MASK |
|
||||
CHNL_IER_OFLW_PANIC_V_BUF_EN_MASK |
|
||||
CHNL_IER_EXCS_OFLW_V_BUF_EN_MASK |
|
||||
CHNL_IER_OFLW_V_BUF_EN_MASK |
|
||||
CHNL_IER_OFLW_PANIC_U_BUF_EN_MASK |
|
||||
CHNL_IER_EXCS_OFLW_U_BUF_EN_MASK |
|
||||
CHNL_IER_OFLW_U_BUF_EN_MASK |
|
||||
CHNL_IER_OFLW_PANIC_Y_BUF_EN_MASK |
|
||||
CHNL_IER_EXCS_OFLW_Y_BUF_EN_MASK |
|
||||
CHNL_IER_OFLW_Y_BUF_EN_MASK;
|
||||
|
||||
writel(val, mxc_isi->regs + CHNL_IER);
|
||||
}
|
||||
|
||||
void mxc_isi_disable_irq(struct mxc_isi_dev *mxc_isi)
|
||||
{
|
||||
writel(0, mxc_isi->regs + CHNL_IER);
|
||||
}
|
||||
|
||||
u32 mxc_isi_get_irq_status(struct mxc_isi_dev *mxc_isi)
|
||||
{
|
||||
return readl(mxc_isi->regs + CHNL_STS);
|
||||
}
|
||||
|
||||
void mxc_isi_clean_irq_status(struct mxc_isi_dev *mxc_isi, u32 val)
|
||||
{
|
||||
writel(val, mxc_isi->regs + CHNL_STS);
|
||||
}
|
||||
|
||||
void mxc_isi_m2m_config_src(struct mxc_isi_dev *mxc_isi,
|
||||
struct mxc_isi_frame *src_f)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/* source format */
|
||||
val = readl(mxc_isi->regs + CHNL_MEM_RD_CTRL);
|
||||
val &= ~CHNL_MEM_RD_CTRL_IMG_TYPE_MASK;
|
||||
val |= src_f->fmt->color << CHNL_MEM_RD_CTRL_IMG_TYPE_OFFSET;
|
||||
writel(val, mxc_isi->regs + CHNL_MEM_RD_CTRL);
|
||||
|
||||
/* source image width and height */
|
||||
val = (src_f->width << CHNL_IMG_CFG_WIDTH_OFFSET |
|
||||
src_f->height << CHNL_IMG_CFG_HEIGHT_OFFSET);
|
||||
writel(val, mxc_isi->regs + CHNL_IMG_CFG);
|
||||
|
||||
/* source pitch */
|
||||
val = src_f->bytesperline[0] << CHNL_IN_BUF_PITCH_LINE_PITCH_OFFSET;
|
||||
writel(val, mxc_isi->regs + CHNL_IN_BUF_PITCH);
|
||||
}
|
||||
|
||||
void mxc_isi_m2m_config_dst(struct mxc_isi_dev *mxc_isi,
|
||||
struct mxc_isi_frame *dst_f)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/* out format */
|
||||
val = readl(mxc_isi->regs + CHNL_IMG_CTRL);
|
||||
val &= ~CHNL_IMG_CTRL_FORMAT_MASK;
|
||||
val |= dst_f->fmt->color << CHNL_IMG_CTRL_FORMAT_OFFSET;
|
||||
writel(val, mxc_isi->regs + CHNL_IMG_CTRL);
|
||||
|
||||
/* out pitch */
|
||||
val = readl(mxc_isi->regs + CHNL_OUT_BUF_PITCH);
|
||||
val &= ~CHNL_IN_BUF_PITCH_LINE_PITCH_MASK;
|
||||
val |= dst_f->bytesperline[0] << CHNL_OUT_BUF_PITCH_LINE_PITCH_OFFSET;
|
||||
writel(val, mxc_isi->regs + CHNL_OUT_BUF_PITCH);
|
||||
}
|
||||
|
||||
void mxc_isi_m2m_start_read(struct mxc_isi_dev *mxc_isi)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl(mxc_isi->regs + CHNL_MEM_RD_CTRL);
|
||||
val &= ~ CHNL_MEM_RD_CTRL_READ_MEM_MASK;
|
||||
writel(val, mxc_isi->regs + CHNL_MEM_RD_CTRL);
|
||||
udelay(300);
|
||||
|
||||
val |= CHNL_MEM_RD_CTRL_READ_MEM_ENABLE << CHNL_MEM_RD_CTRL_READ_MEM_OFFSET;
|
||||
writel(val, mxc_isi->regs + CHNL_MEM_RD_CTRL);
|
||||
}
|
521
drivers/staging/media/imx/imx8-isi-hw.h
Normal file
521
drivers/staging/media/imx/imx8-isi-hw.h
Normal file
|
@ -0,0 +1,521 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2019 NXP Semiconductor
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MXC_ISI_HW_H__
|
||||
#define __MXC_ISI_HW_H__
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#include "imx8-isi-core.h"
|
||||
|
||||
/* ISI Registers Define */
|
||||
/* Channel Control Register */
|
||||
#define CHNL_CTRL 0x0
|
||||
#define CHNL_CTRL_CHNL_EN_OFFSET 31
|
||||
#define CHNL_CTRL_CHNL_EN_MASK 0x80000000
|
||||
#define CHNL_CTRL_CHNL_EN_DISABLE 0
|
||||
#define CHNL_CTRL_CHNL_EN_ENABLE 1
|
||||
#define CHNL_CTRL_CLK_EN_OFFSET 30
|
||||
#define CHNL_CTRL_CLK_EN_MASK 0x40000000
|
||||
#define CHNL_CTRL_CLK_EN_DISABLE 0
|
||||
#define CHNL_CTRL_CLK_EN_ENABLE 1
|
||||
#define CHNL_CTRL_CHNL_BYPASS_OFFSET 29
|
||||
#define CHNL_CTRL_CHNL_BYPASS_MASK 0x20000000
|
||||
#define CHNL_CTRL_CHNL_BYPASS_ENABLE 1
|
||||
#define CHNL_CTRL_CHAIN_BUF_OFFSET 25
|
||||
#define CHNL_CTRL_CHAIN_BUF_MASK 0x60000
|
||||
#define CHNL_CTRL_CHAIN_BUF_NO_CHAIN 0
|
||||
#define CHNL_CTRL_CHAIN_BUF_2_CHAIN 1
|
||||
#define CHNL_CTRL_SW_RST_OFFSET 24
|
||||
#define CHNL_CTRL_SW_RST_MASK 0x1000000
|
||||
#define CHNL_CTRL_SW_RST 0x1000000
|
||||
#define CHNL_CTRL_BLANK_PXL_OFFSET 16
|
||||
#define CHNL_CTRL_MIPI_VC_ID_OFFSET 6
|
||||
#define CHNL_CTRL_MIPI_VC_ID_MASK 0xc0
|
||||
#define CHNL_CTRL_MIPI_VC_ID_VC0 0
|
||||
#define CHNL_CTRL_MIPI_VC_ID_VC1 1
|
||||
#define CHNL_CTRL_MIPI_VC_ID_VC2 2
|
||||
#define CHNL_CTRL_MIPI_VC_ID_VC3 3
|
||||
#define CHNL_CTRL_SRC_TYPE_OFFSET 4
|
||||
#define CHNL_CTRL_SRC_TYPE_MASK 0x10
|
||||
#define CHNL_CTRL_SRC_TYPE_DEVICE 0
|
||||
#define CHNL_CTRL_SRC_TYPE_MEMORY 1
|
||||
#define CHNL_CTRL_SRC_INPUT_OFFSET 0
|
||||
#define CHNL_CTRL_SRC_INPUT_MASK 0x7
|
||||
#define CHNL_CTRL_SRC_INPUT_DC0 0
|
||||
#define CHNL_CTRL_SRC_INPUT_DC1 1
|
||||
#define CHNL_CTRL_SRC_INPUT_MIPI0 2
|
||||
#define CHNL_CTRL_SRC_INPUT_MIPI1 3
|
||||
#define CHNL_CTRL_SRC_INPUT_HDMI 4
|
||||
#define CHNL_CTRL_SRC_INPUT_CSI 4
|
||||
#define CHNL_CTRL_SRC_INPUT_MEMORY 5
|
||||
|
||||
/* Channel Image Control Register */
|
||||
#define CHNL_IMG_CTRL 0x4
|
||||
#define CHNL_IMG_CTRL_FORMAT_OFFSET 24
|
||||
#define CHNL_IMG_CTRL_FORMAT_MASK 0x3F000000
|
||||
#define CHNL_IMG_CTRL_GBL_ALPHA_VAL_OFFSET 16
|
||||
#define CHNL_IMG_CTRL_GBL_ALPHA_VAL_MASK 0xFF0000
|
||||
#define CHNL_IMG_CTRL_GBL_ALPHA_EN_OFFSET 15
|
||||
#define CHNL_IMG_CTRL_GBL_ALPHA_EN_ENABLE 1
|
||||
#define CHNL_IMG_CTRL_GBL_ALPHA_EN_MASK 0x8000
|
||||
#define CHNL_IMG_CTRL_DEINT_OFFSET 12
|
||||
#define CHNL_IMG_CTRL_DEINT_MASK 0x7000
|
||||
#define CHNL_IMG_CTRL_DEINT_WEAVE_ODD_EVEN 2
|
||||
#define CHNL_IMG_CTRL_DEINT_WEAVE_EVEN_ODD 3
|
||||
#define CHNL_IMG_CTRL_DEINT_BLEND_ODD_EVEN 4
|
||||
#define CHNL_IMG_CTRL_DEINT_BLEND_EVEN_ODD 5
|
||||
#define CHNL_IMG_CTRL_DEINT_LDOUBLE_ODD_EVEN 6
|
||||
#define CHNL_IMG_CTRL_DEINT_LDOUBLE_EVEN_ODD 7
|
||||
#define CHNL_IMG_CTRL_DEC_X_OFFSET 10
|
||||
#define CHNL_IMG_CTRL_DEC_X_MASK 0xC00
|
||||
#define CHNL_IMG_CTRL_DEC_X_0 0
|
||||
#define CHNL_IMG_CTRL_DEC_X_2 1
|
||||
#define CHNL_IMG_CTRL_DEC_X_4 2
|
||||
#define CHNL_IMG_CTRL_DEC_X_8 3
|
||||
#define CHNL_IMG_CTRL_DEC_Y_OFFSET 8
|
||||
#define CHNL_IMG_CTRL_DEC_Y_MASK 0x300
|
||||
#define CHNL_IMG_CTRL_DEC_Y_0 0
|
||||
#define CHNL_IMG_CTRL_DEC_Y_2 1
|
||||
#define CHNL_IMG_CTRL_DEC_Y_4 2
|
||||
#define CHNL_IMG_CTRL_DEC_Y_8 3
|
||||
#define CHNL_IMG_CTRL_CROP_EN_OFFSET 7
|
||||
#define CHNL_IMG_CTRL_CROP_EN_MASK 0x80
|
||||
#define CHNL_IMG_CTRL_CROP_EN_ENABLE 1
|
||||
#define CHNL_IMG_CTRL_VFLIP_EN_OFFSET 6
|
||||
#define CHNL_IMG_CTRL_VFLIP_EN_MASK 0x40
|
||||
#define CHNL_IMG_CTRL_VFLIP_EN_ENABLE 1
|
||||
#define CHNL_IMG_CTRL_HFLIP_EN_OFFSET 5
|
||||
#define CHNL_IMG_CTRL_HFLIP_EN_MASK 0x20
|
||||
#define CHNL_IMG_CTRL_HFLIP_EN_ENABLE 1
|
||||
#define CHNL_IMG_CTRL_YCBCR_MODE_OFFSET 3
|
||||
#define CHNL_IMG_CTRL_YCBCR_MODE_MASK 0x8
|
||||
#define CHNL_IMG_CTRL_YCBCR_MODE_ENABLE 1
|
||||
#define CHNL_IMG_CTRL_CSC_MODE_OFFSET 1
|
||||
#define CHNL_IMG_CTRL_CSC_MODE_MASK 0x6
|
||||
#define CHNL_IMG_CTRL_CSC_MODE_YUV2RGB 0
|
||||
#define CHNL_IMG_CTRL_CSC_MODE_YCBCR2RGB 1
|
||||
#define CHNL_IMG_CTRL_CSC_MODE_RGB2YUV 2
|
||||
#define CHNL_IMG_CTRL_CSC_MODE_RGB2YCBCR 3
|
||||
#define CHNL_IMG_CTRL_CSC_BYPASS_OFFSET 0
|
||||
#define CHNL_IMG_CTRL_CSC_BYPASS_MASK 0x1
|
||||
#define CHNL_IMG_CTRL_CSC_BYPASS_ENABLE 0x1
|
||||
|
||||
/* Channel Output Buffer Control Register */
|
||||
#define CHNL_OUT_BUF_CTRL 0x8
|
||||
#define CHNL_OUT_BUF_CTRL_LOAD_BUF2_ADDR_OFFSET 15
|
||||
#define CHNL_OUT_BUF_CTRL_LOAD_BUF2_ADDR_MASK 0x8000
|
||||
#define CHNL_OUT_BUF_CTRL_LOAD_BUF1_ADDR_OFFSET 14
|
||||
#define CHNL_OUT_BUF_CTRL_LOAD_BUF1_ADDR_MASK 0x4000
|
||||
#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_V_OFFSET 6
|
||||
#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_V_MASK 0xC0
|
||||
#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_V_NO_PANIC 0
|
||||
#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_V_PANIC_25 1
|
||||
#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_V_PANIC_50 2
|
||||
#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_V_PANIC_75 3
|
||||
#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_U_OFFSET 3
|
||||
#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_U_MASK 0x18
|
||||
#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_U_NO_PANIC 0
|
||||
#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_U_PANIC_25 1
|
||||
#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_U_PANIC_50 2
|
||||
#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_U_PANIC_75 3
|
||||
#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_Y_OFFSET 0
|
||||
#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_Y_MASK 0x3
|
||||
#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_Y_NO_PANIC 0
|
||||
#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_Y_PANIC_25 1
|
||||
#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_Y_PANIC_50 2
|
||||
#define CHNL_OUT_BUF_CTRL_OFLW_PANIC_SET_THD_Y_PANIC_75 3
|
||||
|
||||
/* Channel Image Configuration */
|
||||
#define CHNL_IMG_CFG 0xC
|
||||
#define CHNL_IMG_CFG_HEIGHT_OFFSET 16
|
||||
#define CHNL_IMG_CFG_HEIGHT_MASK 0x1FFF0000
|
||||
#define CHNL_IMG_CFG_WIDTH_OFFSET 0
|
||||
#define CHNL_IMG_CFG_WIDTH_MASK 0x1FFF
|
||||
|
||||
/* Channel Interrupt Enable Register */
|
||||
#define CHNL_IER 0x10
|
||||
#define CHNL_IER_MEM_RD_DONE_EN_OFFSET 31
|
||||
#define CHNL_IER_MEM_RD_DONE_EN_MASK 0x80000000
|
||||
#define CHNL_IER_MEM_RD_DONE_EN_ENABLE 1
|
||||
#define CHNL_IER_LINE_RCVD_EN_OFFSET 30
|
||||
#define CHNL_IER_LINE_RCVD_EN_MASK 0x40000000
|
||||
#define CHNL_IER_LINE_RCVD_EN_ENABLE 1
|
||||
#define CHNL_IER_FRM_RCVD_EN_OFFSET 29
|
||||
#define CHNL_IER_FRM_RCVD_EN_MASK 0x20000000
|
||||
#define CHNL_IER_FRM_RCVD_EN_ENABLE 1
|
||||
#define CHNL_IER_AXI_WR_ERR_V_EN_OFFSET 28
|
||||
#define CHNL_IER_AXI_WR_ERR_V_EN_MASK 0x10000000
|
||||
#define CHNL_IER_AXI_WR_ERR_V_EN_ENABLE 1
|
||||
#define CHNL_IER_AXI_WR_ERR_U_EN_OFFSET 27
|
||||
#define CHNL_IER_AXI_WR_ERR_U_EN_MASK 0x8000000
|
||||
#define CHNL_IER_AXI_WR_ERR_U_EN_ENABLE 1
|
||||
#define CHNL_IER_AXI_WR_ERR_Y_EN_OFFSET 26
|
||||
#define CHNL_IER_AXI_WR_ERR_Y_EN_MASK 0x4000000
|
||||
#define CHNL_IER_AXI_WR_ERR_Y_EN_ENABLE 1
|
||||
#define CHNL_IER_AXI_RD_ERR_EN_OFFSET 25
|
||||
#define CHNL_IER_AXI_RD_ERR_EN_MASK 0x2000000
|
||||
#define CHNL_IER_AXI_RD_ERR_EN_ENABLE 1
|
||||
#define CHNL_IER_OFLW_PANIC_V_BUF_EN_OFFSET 24
|
||||
#define CHNL_IER_OFLW_PANIC_V_BUF_EN_MASK 0x1000000
|
||||
#define CHNL_IER_OFLW_PANIC_V_BUF_EN_ENABLE 1
|
||||
#define CHNL_IER_EXCS_OFLW_V_BUF_EN_OFFSET 23
|
||||
#define CHNL_IER_EXCS_OFLW_V_BUF_EN_MASK 0x800000
|
||||
#define CHNL_IER_EXCS_OFLW_V_BUF_EN_ENABLE 1
|
||||
#define CHNL_IER_OFLW_V_BUF_EN_OFFSET 22
|
||||
#define CHNL_IER_OFLW_V_BUF_EN_MASK 0x400000
|
||||
#define CHNL_IER_OFLW_V_BUF_EN_ENABLE 1
|
||||
#define CHNL_IER_OFLW_PANIC_U_BUF_EN_OFFSET 21
|
||||
#define CHNL_IER_OFLW_PANIC_U_BUF_EN_MASK 0x200000
|
||||
#define CHNL_IER_OFLW_PANIC_U_BUF_EN_ENABLE 1
|
||||
#define CHNL_IER_EXCS_OFLW_U_BUF_EN_OFFSET 20
|
||||
#define CHNL_IER_EXCS_OFLW_U_BUF_EN_MASK 0x100000
|
||||
#define CHNL_IER_EXCS_OFLW_U_BUF_EN_ENABLE 1
|
||||
#define CHNL_IER_OFLW_U_BUF_EN_OFFSET 19
|
||||
#define CHNL_IER_OFLW_U_BUF_EN_MASK 0x80000
|
||||
#define CHNL_IER_OFLW_U_BUF_EN_ENABLE 1
|
||||
#define CHNL_IER_OFLW_PANIC_Y_BUF_EN_OFFSET 18
|
||||
#define CHNL_IER_OFLW_PANIC_Y_BUF_EN_MASK 0x40000
|
||||
#define CHNL_IER_OFLW_PANIC_Y_BUF_EN_ENABLE 1
|
||||
#define CHNL_IER_EXCS_OFLW_Y_BUF_EN_OFFSET 17
|
||||
#define CHNL_IER_EXCS_OFLW_Y_BUF_EN_MASK 0x20000
|
||||
#define CHNL_IER_EXCS_OFLW_Y_BUF_EN_ENABLE 1
|
||||
#define CHNL_IER_OFLW_Y_BUF_EN_OFFSET 16
|
||||
#define CHNL_IER_OFLW_Y_BUF_EN_MASK 0x10000
|
||||
#define CHNL_IER_OFLW_Y_BUF_EN_ENABLE 1
|
||||
|
||||
/* Channel Status Register */
|
||||
#define CHNL_STS 0x14
|
||||
#define CHNL_STS_MEM_RD_DONE_OFFSET 31
|
||||
#define CHNL_STS_MEM_RD_DONE_MASK 0x80000000
|
||||
#define CHNL_STS_MEM_RD_DONE_ENABLE 1
|
||||
#define CHNL_STS_LINE_STRD_OFFSET 30
|
||||
#define CHNL_STS_LINE_STRD_MASK 0x40000000
|
||||
#define CHNL_STS_LINE_STRD_ENABLE 1
|
||||
#define CHNL_STS_FRM_STRD_OFFSET 29
|
||||
#define CHNL_STS_FRM_STRD_MASK 0x20000000
|
||||
#define CHNL_STS_FRM_STRD_ENABLE 1
|
||||
#define CHNL_STS_AXI_WR_ERR_V_OFFSET 28
|
||||
#define CHNL_STS_AXI_WR_ERR_V_MASK 0x10000000
|
||||
#define CHNL_STS_AXI_WR_ERR_V_ENABLE 1
|
||||
#define CHNL_STS_AXI_WR_ERR_U_OFFSET 27
|
||||
#define CHNL_STS_AXI_WR_ERR_U_MASK 0x8000000
|
||||
#define CHNL_STS_AXI_WR_ERR_U_ENABLE 1
|
||||
#define CHNL_STS_AXI_WR_ERR_Y_OFFSET 26
|
||||
#define CHNL_STS_AXI_WR_ERR_Y_MASK 0x4000000
|
||||
#define CHNL_STS_AXI_WR_ERR_Y_ENABLE 1
|
||||
#define CHNL_STS_AXI_RD_ERR_OFFSET 25
|
||||
#define CHNL_STS_AXI_RD_ERR_MASK 0x2000000
|
||||
#define CHNL_STS_AXI_RD_ERR_ENABLE 1
|
||||
#define CHNL_STS_OFLW_PANIC_V_BUF_OFFSET 24
|
||||
#define CHNL_STS_OFLW_PANIC_V_BUF_MASK 0x1000000
|
||||
#define CHNL_STS_OFLW_PANIC_V_BUF_ENABLE 1
|
||||
#define CHNL_STS_EXCS_OFLW_V_BUF_OFFSET 23
|
||||
#define CHNL_STS_EXCS_OFLW_V_BUF_MASK 0x800000
|
||||
#define CHNL_STS_EXCS_OFLW_V_BUF_ENABLE 1
|
||||
#define CHNL_STS_OFLW_V_BUF_OFFSET 22
|
||||
#define CHNL_STS_OFLW_V_BUF_MASK 0x400000
|
||||
#define CHNL_STS_OFLW_V_BUF_ENABLE 1
|
||||
#define CHNL_STS_OFLW_PANIC_U_BUF_OFFSET 21
|
||||
#define CHNL_STS_OFLW_PANIC_U_BUF_MASK 0x200000
|
||||
#define CHNL_STS_OFLW_PANIC_U_BUF_ENABLE 1
|
||||
#define CHNL_STS_EXCS_OFLW_U_BUF_OFFSET 20
|
||||
#define CHNL_STS_EXCS_OFLW_U_BUF_MASK 0x100000
|
||||
#define CHNL_STS_EXCS_OFLW_U_BUF_ENABLE 1
|
||||
#define CHNL_STS_OFLW_U_BUF_OFFSET 19
|
||||
#define CHNL_STS_OFLW_U_BUF_MASK 0x80000
|
||||
#define CHNL_STS_OFLW_U_BUF_ENABLE 1
|
||||
#define CHNL_STS_OFLW_PANIC_Y_BUF_OFFSET 18
|
||||
#define CHNL_STS_OFLW_PANIC_Y_BUF_MASK 0x40000
|
||||
#define CHNL_STS_OFLW_PANIC_Y_BUF_ENABLE 1
|
||||
#define CHNL_STS_EXCS_OFLW_Y_BUF_OFFSET 17
|
||||
#define CHNL_STS_EXCS_OFLW_Y_BUF_MASK 0x20000
|
||||
#define CHNL_STS_EXCS_OFLW_Y_BUF_ENABLE 1
|
||||
#define CHNL_STS_OFLW_Y_BUF_OFFSET 16
|
||||
#define CHNL_STS_OFLW_Y_BUF_MASK 0x10000
|
||||
#define CHNL_STS_OFLW_Y_BUF_ENABLE 1
|
||||
#define CHNL_STS_OFLW_BYTES_OFFSET 0
|
||||
#define CHNL_STS_OFLW_BYTES_MASK 0xFF
|
||||
|
||||
/* Channel Scale Factor Register */
|
||||
#define CHNL_SCALE_FACTOR 0x18
|
||||
#define CHNL_SCALE_FACTOR_Y_SCALE_OFFSET 16
|
||||
#define CHNL_SCALE_FACTOR_Y_SCALE_MASK 0x3FFF0000
|
||||
#define CHNL_SCALE_FACTOR_X_SCALE_OFFSET 0
|
||||
#define CHNL_SCALE_FACTOR_X_SCALE_MASK 0x3FFF
|
||||
|
||||
/* Channel Scale Offset Register */
|
||||
#define CHNL_SCALE_OFFSET 0x1C
|
||||
#define CHNL_SCALE_OFFSET_Y_SCALE_OFFSET 16
|
||||
#define CHNL_SCALE_OFFSET_Y_SCALE_MASK 0xFFF0000
|
||||
#define CHNL_SCALE_OFFSET_X_SCALE_OFFSET 0
|
||||
#define CHNL_SCALE_OFFSET_X_SCALE_MASK 0xFFF
|
||||
|
||||
/* Channel Crop Upper Left Corner Coordinate Register */
|
||||
#define CHNL_CROP_ULC 0x20
|
||||
#define CHNL_CROP_ULC_X_OFFSET 16
|
||||
#define CHNL_CROP_ULC_X_MASK 0xFFF0000
|
||||
#define CHNL_CROP_ULC_Y_OFFSET 0
|
||||
#define CHNL_CROP_ULC_Y_MASK 0xFFF
|
||||
|
||||
/* Channel Crop Lower Right Corner Coordinate Register */
|
||||
#define CHNL_CROP_LRC 0x24
|
||||
#define CHNL_CROP_LRC_X_OFFSET 16
|
||||
#define CHNL_CROP_LRC_X_MASK 0xFFF0000
|
||||
#define CHNL_CROP_LRC_Y_OFFSET 0
|
||||
#define CHNL_CROP_LRC_Y_MASK 0xFFF
|
||||
|
||||
/* Channel Color Space Conversion Coefficient Register 0 */
|
||||
#define CHNL_CSC_COEFF0 0x28
|
||||
#define CHNL_CSC_COEFF0_A2_OFFSET 16
|
||||
#define CHNL_CSC_COEFF0_A2_MASK 0x7FF0000
|
||||
#define CHNL_CSC_COEFF0_A1_OFFSET 0
|
||||
#define CHNL_CSC_COEFF0_A1_MASK 0x7FF
|
||||
|
||||
/* Channel Color Space Conversion Coefficient Register 1 */
|
||||
#define CHNL_CSC_COEFF1 0x2C
|
||||
#define CHNL_CSC_COEFF1_B1_OFFSET 16
|
||||
#define CHNL_CSC_COEFF1_B1_MASK 0x7FF0000
|
||||
#define CHNL_CSC_COEFF1_A3_OFFSET 0
|
||||
#define CHNL_CSC_COEFF1_A3_MASK 0x7FF
|
||||
|
||||
/* Channel Color Space Conversion Coefficient Register 2 */
|
||||
#define CHNL_CSC_COEFF2 0x30
|
||||
#define CHNL_CSC_COEFF2_B3_OFFSET 16
|
||||
#define CHNL_CSC_COEFF2_B3_MASK 0x7FF0000
|
||||
#define CHNL_CSC_COEFF2_B2_OFFSET 0
|
||||
#define CHNL_CSC_COEFF2_B2_MASK 0x7FF
|
||||
|
||||
/* Channel Color Space Conversion Coefficient Register 3 */
|
||||
#define CHNL_CSC_COEFF3 0x34
|
||||
#define CHNL_CSC_COEFF3_C2_OFFSET 16
|
||||
#define CHNL_CSC_COEFF3_C2_MASK 0x7FF0000
|
||||
#define CHNL_CSC_COEFF3_C1_OFFSET 0
|
||||
#define CHNL_CSC_COEFF3_C1_MASK 0x7FF
|
||||
|
||||
/* Channel Color Space Conversion Coefficient Register 4 */
|
||||
#define CHNL_CSC_COEFF4 0x38
|
||||
#define CHNL_CSC_COEFF4_D1_OFFSET 16
|
||||
#define CHNL_CSC_COEFF4_D1_MASK 0x1FF0000
|
||||
#define CHNL_CSC_COEFF4_C3_OFFSET 0
|
||||
#define CHNL_CSC_COEFF4_C3_MASK 0x7FF
|
||||
|
||||
/* Channel Color Space Conversion Coefficient Register 5 */
|
||||
#define CHNL_CSC_COEFF5 0x3C
|
||||
#define CHNL_CSC_COEFF5_D3_OFFSET 16
|
||||
#define CHNL_CSC_COEFF5_D3_MASK 0x1FF0000
|
||||
#define CHNL_CSC_COEFF5_D2_OFFSET 0
|
||||
#define CHNL_CSC_COEFF5_D2_MASK 0x1FF
|
||||
|
||||
/* Channel Alpha Value Register for ROI 0 */
|
||||
#define CHNL_ROI_0_ALPHA 0x40
|
||||
#define CHNL_ROI_0_ALPHA_OFFSET 24
|
||||
#define CHNL_ROI_0_ALPHA_MASK 0xFF000000
|
||||
#define CHNL_ROI_0_ALPHA_EN_OFFSET 16
|
||||
#define CHNL_ROI_0_ALPHA_EN_MASK 0x10000
|
||||
|
||||
/* Channel Upper Left Coordinate Register for ROI 0 */
|
||||
#define CHNL_ROI_0_ULC 0x44
|
||||
#define CHNL_ROI_0_ULC_X_OFFSET 16
|
||||
#define CHNL_ROI_0_ULC_X_MASK 0xFFF0000
|
||||
#define CHNL_ROI_0_ULC_Y_OFFSET 0
|
||||
#define CHNL_ROI_0_ULC_Y_MASK 0xFFF
|
||||
|
||||
/* Channel Lower Right Coordinate Register for ROI 0 */
|
||||
#define CHNL_ROI_0_LRC 0x48
|
||||
#define CHNL_ROI_0_LRC_X_OFFSET 16
|
||||
#define CHNL_ROI_0_LRC_X_MASK 0xFFF0000
|
||||
#define CHNL_ROI_0_LRC_Y_OFFSET 0
|
||||
#define CHNL_ROI_0_LRC_Y_MASK 0xFFF
|
||||
|
||||
/* Channel Alpha Value Register for ROI 1 */
|
||||
#define CHNL_ROI_1_ALPHA 0x4C
|
||||
#define CHNL_ROI_1_ALPHA_OFFSET 24
|
||||
#define CHNL_ROI_1_ALPHA_MASK 0xFF000000
|
||||
#define CHNL_ROI_1_ALPHA_EN_OFFSET 16
|
||||
#define CHNL_ROI_1_ALPHA_EN_MASK 0x10000
|
||||
|
||||
/* Channel Upper Left Coordinate Register for ROI 1 */
|
||||
#define CHNL_ROI_1_ULC 0x50
|
||||
#define CHNL_ROI_1_ULC_X_OFFSET 16
|
||||
#define CHNL_ROI_1_ULC_X_MASK 0xFFF0000
|
||||
#define CHNL_ROI_1_ULC_Y_OFFSET 0
|
||||
#define CHNL_ROI_1_ULC_Y_MASK 0xFFF
|
||||
|
||||
/* Channel Lower Right Coordinate Register for ROI 1 */
|
||||
#define CHNL_ROI_1_LRC 0x54
|
||||
#define CHNL_ROI_1_LRC_X_OFFSET 16
|
||||
#define CHNL_ROI_1_LRC_X_MASK 0xFFF0000
|
||||
#define CHNL_ROI_1_LRC_Y_OFFSET 0
|
||||
#define CHNL_ROI_1_LRC_Y_MASK 0xFFF
|
||||
|
||||
/* Channel Alpha Value Register for ROI 2 */
|
||||
#define CHNL_ROI_2_ALPHA 0x58
|
||||
#define CHNL_ROI_2_ALPHA_OFFSET 24
|
||||
#define CHNL_ROI_2_ALPHA_MASK 0xFF000000
|
||||
#define CHNL_ROI_2_ALPHA_EN_OFFSET 16
|
||||
#define CHNL_ROI_2_ALPHA_EN_MASK 0x10000
|
||||
|
||||
/* Channel Upper Left Coordinate Register for ROI 2 */
|
||||
#define CHNL_ROI_2_ULC 0x5C
|
||||
#define CHNL_ROI_2_ULC_X_OFFSET 16
|
||||
#define CHNL_ROI_2_ULC_X_MASK 0xFFF0000
|
||||
#define CHNL_ROI_2_ULC_Y_OFFSET 0
|
||||
#define CHNL_ROI_2_ULC_Y_MASK 0xFFF
|
||||
|
||||
/* Channel Lower Right Coordinate Register for ROI 2 */
|
||||
#define CHNL_ROI_2_LRC 0x60
|
||||
#define CHNL_ROI_2_LRC_X_OFFSET 16
|
||||
#define CHNL_ROI_2_LRC_X_MASK 0xFFF0000
|
||||
#define CHNL_ROI_2_LRC_Y_OFFSET 0
|
||||
#define CHNL_ROI_2_LRC_Y_MASK 0xFFF
|
||||
|
||||
/* Channel Alpha Value Register for ROI 3 */
|
||||
#define CHNL_ROI_3_ALPHA 0x64
|
||||
#define CHNL_ROI_3_ALPHA_OFFSET 24
|
||||
#define CHNL_ROI_3_ALPHA_MASK 0xFF000000
|
||||
#define CHNL_ROI_3_ALPHA_EN_OFFSET 16
|
||||
#define CHNL_ROI_3_ALPHA_EN_MASK 0x10000
|
||||
|
||||
/* Channel Upper Left Coordinate Register for ROI 3 */
|
||||
#define CHNL_ROI_3_ULC 0x68
|
||||
#define CHNL_ROI_3_ULC_X_OFFSET 16
|
||||
#define CHNL_ROI_3_ULC_X_MASK 0xFFF0000
|
||||
#define CHNL_ROI_3_ULC_Y_OFFSET 0
|
||||
#define CHNL_ROI_3_ULC_Y_MASK 0xFFF
|
||||
|
||||
/* Channel Lower Right Coordinate Register for ROI 3 */
|
||||
#define CHNL_ROI_3_LRC 0x6C
|
||||
#define CHNL_ROI_3_LRC_X_OFFSET 16
|
||||
#define CHNL_ROI_3_LRC_X_MASK 0xFFF0000
|
||||
#define CHNL_ROI_3_LRC_Y_OFFSET 0
|
||||
#define CHNL_ROI_3_LRC_Y_MASK 0xFFF
|
||||
|
||||
/* Channel RGB or Luma (Y) Output Buffer 1 Address */
|
||||
#define CHNL_OUT_BUF1_ADDR_Y 0x70
|
||||
|
||||
/* Channel Chroma (U/Cb/UV/CbCr) Output Buffer 1 Address */
|
||||
#define CHNL_OUT_BUF1_ADDR_U 0x74
|
||||
|
||||
/* Channel Chroma (V/Cr) Output Buffer 1 Address */
|
||||
#define CHNL_OUT_BUF1_ADDR_V 0x78
|
||||
|
||||
/* Channel Output Buffer Pitch */
|
||||
#define CHNL_OUT_BUF_PITCH 0x7C
|
||||
#define CHNL_OUT_BUF_PITCH_LINE_PITCH_OFFSET 0
|
||||
#define CHNL_OUT_BUF_PITCH_LINE_PITCH_MASK 0xFFFF
|
||||
|
||||
/* Channel Input Buffer Address */
|
||||
#define CHNL_IN_BUF_ADDR 0x80
|
||||
|
||||
/* Channel Input Buffer Pitch */
|
||||
#define CHNL_IN_BUF_PITCH 0x84
|
||||
#define CHNL_IN_BUF_PITCH_FRM_PITCH_OFFSET 16
|
||||
#define CHNL_IN_BUF_PITCH_FRM_PITCH_MASK 0xFFFF0000
|
||||
#define CHNL_IN_BUF_PITCH_LINE_PITCH_OFFSET 0
|
||||
#define CHNL_IN_BUF_PITCH_LINE_PITCH_MASK 0xFFFF
|
||||
|
||||
/* Channel Memory Read Control */
|
||||
#define CHNL_MEM_RD_CTRL 0x88
|
||||
#define CHNL_MEM_RD_CTRL_IMG_TYPE_OFFSET 28
|
||||
#define CHNL_MEM_RD_CTRL_IMG_TYPE_MASK 0xF0000000
|
||||
#define CHNL_MEM_RD_CTRL_READ_MEM_OFFSET 0
|
||||
#define CHNL_MEM_RD_CTRL_READ_MEM_MASK 1
|
||||
#define CHNL_MEM_RD_CTRL_READ_MEM_ENABLE 1
|
||||
|
||||
/* Channel RGB or Luma (Y) Output Buffer 2 Address */
|
||||
#define CHNL_OUT_BUF2_ADDR_Y 0x8C
|
||||
|
||||
/* Channel Chroma (U/Cb/UV/CbCr) Output Buffer 2 Address */
|
||||
#define CHNL_OUT_BUF2_ADDR_U 0x90
|
||||
|
||||
/* Channel Chroma (V/Cr) Output Buffer 2 Address */
|
||||
#define CHNL_OUT_BUF2_ADDR_V 0x94
|
||||
|
||||
/* Channel scale image config */
|
||||
#define CHNL_SCL_IMG_CFG 0x98
|
||||
#define CHNL_SCL_IMG_CFG_HEIGHT_OFFSET 16
|
||||
#define CHNL_SCL_IMG_CFG_HEIGHT_MASK 0x1FFF0000
|
||||
#define CHNL_SCL_IMG_CFG_WIDTH_OFFSET 0
|
||||
#define CHNL_SCL_IMG_CFG_WIDTH_MASK 0x1FFF
|
||||
|
||||
/* Channel Flow Control Register */
|
||||
#define CHNL_FLOW_CTRL 0x9C
|
||||
#define CHNL_FLOW_CTRL_FC_DENOM_MASK 0xFF
|
||||
#define CHNL_FLOW_CTRL_FC_DENOM_OFFSET 0
|
||||
#define CHNL_FLOW_CTRL_FC_NUMER_MASK 0xFF0000
|
||||
#define CHNL_FLOW_CTRL_FC_NUMER_OFFSET 0
|
||||
|
||||
enum isi_csi_coeff {
|
||||
YUV2RGB = 0,
|
||||
RGB2YUV,
|
||||
};
|
||||
|
||||
void mxc_isi_channel_init(struct mxc_isi_dev *mxc_isi);
|
||||
void mxc_isi_channel_deinit(struct mxc_isi_dev *mxc_isi);
|
||||
void mxc_isi_channel_enable(struct mxc_isi_dev *mxc_isi, bool m2m_enabled);
|
||||
void mxc_isi_channel_disable(struct mxc_isi_dev *mxc_isi);
|
||||
#if defined(CONFIG_IMX8_ISI_CAPTURE)
|
||||
void mxc_isi_cap_frame_write_done(struct mxc_isi_dev *mxc_isi);
|
||||
#else
|
||||
static inline void mxc_isi_cap_frame_write_done(struct mxc_isi_dev *mxc_isi) {}
|
||||
#endif
|
||||
void mxc_isi_channel_set_deinterlace(struct mxc_isi_dev *mxc_isi);
|
||||
void mxc_isi_channel_sw_reset(struct mxc_isi_dev *mxc_isi);
|
||||
void mxc_isi_channel_hw_reset(struct mxc_isi_dev *mxc_isi);
|
||||
void mxc_isi_channel_source_config(struct mxc_isi_dev *mxc_isi);
|
||||
void mxc_isi_channel_set_flip(struct mxc_isi_dev *mxc_isi);
|
||||
void mxc_isi_channel_set_alpha(struct mxc_isi_dev *mxc_isi);
|
||||
void mxc_isi_channel_set_chain_buf(struct mxc_isi_dev *mxc_isi);
|
||||
void mxc_isi_channel_set_deinterlace(struct mxc_isi_dev *mxc_isi);
|
||||
void mxc_isi_channel_set_crop(struct mxc_isi_dev *mxc_isi);
|
||||
void mxc_isi_channel_set_memory_image(struct mxc_isi_dev *mxc_isi);
|
||||
|
||||
void mxc_isi_channel_set_scaling(struct mxc_isi_dev *mxc_isi,
|
||||
struct mxc_isi_frame *src_f,
|
||||
struct mxc_isi_frame *dst_f);
|
||||
|
||||
void mxc_isi_channel_set_outbuf(struct mxc_isi_dev *mxc_isi,
|
||||
struct mxc_isi_buffer *buf);
|
||||
|
||||
void mxc_isi_channel_set_csc(struct mxc_isi_dev *mxc_isi,
|
||||
struct mxc_isi_frame *src_f,
|
||||
struct mxc_isi_frame *dst_f);
|
||||
|
||||
void mxc_isi_channel_config(struct mxc_isi_dev *mxc_isi,
|
||||
struct mxc_isi_frame *src_f,
|
||||
struct mxc_isi_frame *dst_f);
|
||||
|
||||
void mxc_isi_channel_set_alpha_roi0(struct mxc_isi_dev *mxc_isi,
|
||||
struct v4l2_rect *rect);
|
||||
void mxc_isi_channel_set_m2m_src_addr(struct mxc_isi_dev *mxc_isi,
|
||||
struct mxc_isi_buffer *buf);
|
||||
|
||||
void mxc_isi_m2m_config_src(struct mxc_isi_dev *mxc_isi,
|
||||
struct mxc_isi_frame *src_f);
|
||||
void mxc_isi_m2m_config_dst(struct mxc_isi_dev *mxc_isi,
|
||||
struct mxc_isi_frame *dst_f);
|
||||
|
||||
void mxc_isi_m2m_start_read(struct mxc_isi_dev *mxc_isi);
|
||||
#if defined(CONFIG_IMX8_ISI_M2M)
|
||||
void mxc_isi_m2m_frame_write_done(struct mxc_isi_dev *mxc_isi);
|
||||
#else
|
||||
static inline void mxc_isi_m2m_frame_write_done(struct mxc_isi_dev *mxc_isi) {}
|
||||
#endif
|
||||
void mxc_isi_clean_irq_status(struct mxc_isi_dev *mxc_isi, u32 val);
|
||||
void mxc_isi_clean_registers(struct mxc_isi_dev *mxc_isi);
|
||||
void mxc_isi_enable_irq(struct mxc_isi_dev *mxc_isi);
|
||||
void mxc_isi_disable_irq(struct mxc_isi_dev *mxc_isi);
|
||||
void dump_isi_regs(struct mxc_isi_dev *mxc_isi);
|
||||
|
||||
u32 mxc_isi_get_irq_status(struct mxc_isi_dev *mxc_isi);
|
||||
|
||||
#endif /* __MXC_ISI_HW_H__ */
|
1202
drivers/staging/media/imx/imx8-isi-m2m.c
Normal file
1202
drivers/staging/media/imx/imx8-isi-m2m.c
Normal file
File diff suppressed because it is too large
Load Diff
1073
drivers/staging/media/imx/imx8-media-dev.c
Normal file
1073
drivers/staging/media/imx/imx8-media-dev.c
Normal file
File diff suppressed because it is too large
Load Diff
1515
drivers/staging/media/imx/imx8-mipi-csi2-sam.c
Normal file
1515
drivers/staging/media/imx/imx8-mipi-csi2-sam.c
Normal file
File diff suppressed because it is too large
Load Diff
1155
drivers/staging/media/imx/imx8-mipi-csi2.c
Normal file
1155
drivers/staging/media/imx/imx8-mipi-csi2.c
Normal file
File diff suppressed because it is too large
Load Diff
826
drivers/staging/media/imx/imx8-parallel-csi.c
Normal file
826
drivers/staging/media/imx/imx8-parallel-csi.c
Normal file
|
@ -0,0 +1,826 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* V4L2 Capture CSI Subdev for Freescale i.MX8QM/QXP SOC
|
||||
*
|
||||
* Copyright (c) 2019 NXP Semiconductor
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/memory.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/videodev2.h>
|
||||
#include <media/v4l2-fwnode.h>
|
||||
#include <media/v4l2-subdev.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <linux/firmware/imx/sci.h>
|
||||
#include <dt-bindings/pinctrl/pads-imx8qxp.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/pm_domain.h>
|
||||
|
||||
#include "imx8-common.h"
|
||||
|
||||
#define MXC_PARALLEL_CSI_DRIVER_NAME "mxc-parallel-csi"
|
||||
#define MXC_PARALLEL_CSI_SUBDEV_NAME MXC_PARALLEL_CSI_DRIVER_NAME
|
||||
|
||||
#define BIT_U(nr) (1U << (nr))
|
||||
#define CI_PI_BASE_OFFSET 0x0U
|
||||
|
||||
/* CI_PI INTERFACE Control */
|
||||
#define IF_CTRL_REG (CI_PI_BASE_OFFSET + 0x00)
|
||||
#define IF_CTRL_REG_PL_ENABLE BIT_U(0)
|
||||
#define IF_CTRL_REG_PL_VALID BIT_U(1)
|
||||
#define IF_CTRL_REG_PL_ADDR(x) (((x) & 0x7U) << 2)
|
||||
#define IF_CTRL_REG_IF_FORCE(x) (((x) & 0x7U) << 5)
|
||||
#define IF_CTRL_REG_DATA_TYPE_SEL BIT_U(8)
|
||||
#define IF_CTRL_REG_DATA_TYPE(x) (((x) & 0x1FU) << 9)
|
||||
|
||||
#define DATA_TYPE_OUT_NULL (0x00)
|
||||
#define DATA_TYPE_OUT_RGB (0x04)
|
||||
#define DATA_TYPE_OUT_YUV444 (0x08)
|
||||
#define DATA_TYPE_OUT_YYU420_ODD (0x10)
|
||||
#define DATA_TYPE_OUT_YYU420_EVEN (0x12)
|
||||
#define DATA_TYPE_OUT_YYY_ODD (0x18)
|
||||
#define DATA_TYPE_OUT_UYVY_EVEN (0x1A)
|
||||
#define DATA_TYPE_OUT_RAW (0x1C)
|
||||
|
||||
#define IF_CTRL_REG_IF_FORCE_HSYNV_OVERRIDE 0x4
|
||||
#define IF_CTRL_REG_IF_FORCE_VSYNV_OVERRIDE 0x2
|
||||
#define IF_CTRL_REG_IF_FORCE_DATA_ENABLE_OVERRIDE 0x1
|
||||
|
||||
#define IF_CTRL_REG_SET (CI_PI_BASE_OFFSET + 0x04)
|
||||
#define IF_CTRL_REG_CLR (CI_PI_BASE_OFFSET + 0x08)
|
||||
#define IF_CTRL_REG_TOG (CI_PI_BASE_OFFSET + 0x0C)
|
||||
|
||||
/* CSI INTERFACE CONTROL REG */
|
||||
#define CSI_CTRL_REG (CI_PI_BASE_OFFSET + 0x10)
|
||||
#define CSI_CTRL_REG_CSI_EN BIT_U(0)
|
||||
#define CSI_CTRL_REG_PIXEL_CLK_POL BIT_U(1)
|
||||
#define CSI_CTRL_REG_HSYNC_POL BIT_U(2)
|
||||
#define CSI_CTRL_REG_VSYNC_POL BIT_U(3)
|
||||
#define CSI_CTRL_REG_DE_POL BIT_U(4)
|
||||
#define CSI_CTRL_REG_PIXEL_DATA_POL BIT_U(5)
|
||||
#define CSI_CTRL_REG_CCIR_EXT_VSYNC_EN BIT_U(6)
|
||||
#define CSI_CTRL_REG_CCIR_EN BIT_U(7)
|
||||
#define CSI_CTRL_REG_CCIR_VIDEO_MODE BIT_U(8)
|
||||
#define CSI_CTRL_REG_CCIR_NTSC_EN BIT_U(9)
|
||||
#define CSI_CTRL_REG_CCIR_VSYNC_RESET_EN BIT_U(10)
|
||||
#define CSI_CTRL_REG_CCIR_ECC_ERR_CORRECT_EN BIT_U(11)
|
||||
#define CSI_CTRL_REG_HSYNC_FORCE_EN BIT_U(12)
|
||||
#define CSI_CTRL_REG_VSYNC_FORCE_EN BIT_U(13)
|
||||
#define CSI_CTRL_REG_GCLK_MODE_EN BIT_U(14)
|
||||
#define CSI_CTRL_REG_VALID_SEL BIT_U(15)
|
||||
#define CSI_CTRL_REG_RAW_OUT_SEL BIT_U(16)
|
||||
#define CSI_CTRL_REG_HSYNC_OUT_SEL BIT_U(17)
|
||||
#define CSI_CTRL_REG_HSYNC_PULSE(x) (((x) & 0x7U) << 19)
|
||||
#define CSI_CTRL_REG_UV_SWAP_EN BIT_U(22)
|
||||
#define CSI_CTRL_REG_DATA_TYPE_IN(x) (((x) & 0xFU) << 23)
|
||||
#define CSI_CTRL_REG_MASK_VSYNC_COUNTER(x) (((x) & 0x3U) << 27)
|
||||
#define CSI_CTRL_REG_SOFTRST BIT_U(31)
|
||||
|
||||
#define DATA_TYPE_IN_UYVY_BT656_8BITS 0x0
|
||||
#define DATA_TYPE_IN_UYVY_BT656_10BITS 0x1
|
||||
#define DATA_TYPE_IN_RGB_8BITS 0x2
|
||||
#define DATA_TYPE_IN_BGR_8BITS 0x3
|
||||
#define DATA_TYPE_IN_RGB_24BITS 0x4
|
||||
#define DATA_TYPE_IN_YVYU_8BITS 0x5
|
||||
#define DATA_TYPE_IN_YUV_8BITS 0x6
|
||||
#define DATA_TYPE_IN_YVYU_16BITS 0x7
|
||||
#define DATA_TYPE_IN_YUV_24BITS 0x8
|
||||
#define DATA_TYPE_IN_BAYER_8BITS 0x9
|
||||
#define DATA_TYPE_IN_BAYER_10BITS 0xA
|
||||
#define DATA_TYPE_IN_BAYER_12BITS 0xB
|
||||
#define DATA_TYPE_IN_BAYER_16BITS 0xC
|
||||
|
||||
#define CSI_CTRL_REG_SET (CI_PI_BASE_OFFSET + 0x14)
|
||||
#define CSI_CTRL_REG_CLR (CI_PI_BASE_OFFSET + 0x18)
|
||||
#define CSI_CTRL_REG_TOG (CI_PI_BASE_OFFSET + 0x1C)
|
||||
|
||||
/* CSI interface Status */
|
||||
#define CSI_STATUS (CI_PI_BASE_OFFSET + 0x20)
|
||||
#define CSI_STATUS_FIELD_TOGGLE BIT_U(0)
|
||||
#define CSI_STATUS_ECC_ERROR BIT_U(1)
|
||||
|
||||
#define CSI_STATUS_SET (CI_PI_BASE_OFFSET + 0x24)
|
||||
#define CSI_STATUS_CLR (CI_PI_BASE_OFFSET + 0x28)
|
||||
#define CSI_STATUS_TOG (CI_PI_BASE_OFFSET + 0x2C)
|
||||
|
||||
/* CSI INTERFACE CONTROL REG1 */
|
||||
#define CSI_CTRL_REG1 (CI_PI_BASE_OFFSET + 0x30)
|
||||
#define CSI_CTRL_REG1_PIXEL_WIDTH(v) (((v) & 0xFFFFU) << 0)
|
||||
#define CSI_CTRL_REG1_VSYNC_PULSE(v) (((v) & 0xFFFFU) << 16)
|
||||
|
||||
#define CSI_CTRL_REG1_SET (CI_PI_BASE_OFFSET + 0x34)
|
||||
#define CSI_CTRL_REG1_CLR (CI_PI_BASE_OFFSET + 0x38)
|
||||
#define CSI_CTRL_REG1_TOG (CI_PI_BASE_OFFSET + 0x3C)
|
||||
|
||||
enum {
|
||||
PI_MODE_INIT,
|
||||
PI_GATE_CLOCK_MODE,
|
||||
PI_CCIR_MODE,
|
||||
};
|
||||
struct mxc_parallel_csi_dev {
|
||||
struct v4l2_subdev sd;
|
||||
struct v4l2_device v4l2_dev;
|
||||
struct v4l2_subdev *sensor_sd;
|
||||
|
||||
struct media_pad pads[MXC_PARALLEL_CSI_PADS_NUM];
|
||||
|
||||
void __iomem *csr_regs;
|
||||
void __iomem *lpcg_regs;
|
||||
struct platform_device *pdev;
|
||||
u32 flags;
|
||||
int irq;
|
||||
|
||||
struct clk *clk_ipg;
|
||||
struct clk *clk_pixel;
|
||||
bool clk_enable;
|
||||
|
||||
struct v4l2_async_subdev asd;
|
||||
struct v4l2_async_notifier subdev_notifier;
|
||||
struct v4l2_async_subdev *async_subdevs[2];
|
||||
struct v4l2_mbus_framefmt format;
|
||||
|
||||
struct device *pd_pi;
|
||||
struct device *pd_isi;
|
||||
|
||||
struct mutex lock;
|
||||
|
||||
u8 running;
|
||||
u8 mode;
|
||||
u8 uv_swap;
|
||||
u8 tvdec;
|
||||
};
|
||||
|
||||
static int debug;
|
||||
module_param(debug, int, 0644);
|
||||
MODULE_PARM_DESC(debug, "Debug level (0-2)");
|
||||
|
||||
static int format;
|
||||
module_param(format, int, 0644);
|
||||
MODULE_PARM_DESC(format, "Format level (0-2)");
|
||||
|
||||
#ifdef DEBUG
|
||||
static void mxc_pcsi_regs_dump(struct mxc_parallel_csi_dev *pcsidev)
|
||||
{
|
||||
struct device *dev = &pcsidev->pdev->dev;
|
||||
struct {
|
||||
u32 offset;
|
||||
const char *const name[32];
|
||||
} registers[] = {
|
||||
{ 0x00, "HW_IF_CTRL_REG" },
|
||||
{ 0x10, "HW_CSI_CTRL_REG" },
|
||||
{ 0x20, "HW_CSI_STATUS" },
|
||||
{ 0x30, "HW_CSI_CTRL_REG1" },
|
||||
};
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(registers); i++) {
|
||||
u32 reg = readl(pcsidev->csr_regs + registers[i].offset);
|
||||
dev_dbg(dev, "%20s[0x%.2x]: 0x%.8x\n",
|
||||
registers[i].name, registers[i].offset, reg);
|
||||
}
|
||||
}
|
||||
#else
|
||||
static void mxc_pcsi_regs_dump(struct mxc_parallel_csi_dev *pcsidev) { }
|
||||
#endif
|
||||
|
||||
static struct mxc_parallel_csi_dev *sd_to_mxc_pcsi_dev(struct v4l2_subdev *sdev)
|
||||
{
|
||||
return container_of(sdev, struct mxc_parallel_csi_dev, sd);
|
||||
}
|
||||
|
||||
static int mxc_pcsi_clk_get(struct mxc_parallel_csi_dev *pcsidev)
|
||||
{
|
||||
struct device *dev = &pcsidev->pdev->dev;
|
||||
|
||||
pcsidev->clk_pixel = devm_clk_get(dev, "pixel");
|
||||
if (IS_ERR(pcsidev->clk_pixel)) {
|
||||
dev_info(dev, "failed to get parallel csi pixel clk\n");
|
||||
return PTR_ERR(pcsidev->clk_pixel);
|
||||
}
|
||||
|
||||
pcsidev->clk_ipg = devm_clk_get(dev, "ipg");
|
||||
if (IS_ERR(pcsidev->clk_ipg)) {
|
||||
dev_info(dev, "failed to get parallel ipg pixel clk\n");
|
||||
return PTR_ERR(pcsidev->clk_ipg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxc_pcsi_attach_pd(struct mxc_parallel_csi_dev *pcsidev)
|
||||
{
|
||||
struct device *dev = &pcsidev->pdev->dev;
|
||||
struct device_link *link;
|
||||
|
||||
pcsidev->pd_pi = dev_pm_domain_attach_by_name(dev, "pd_pi");
|
||||
if (IS_ERR(pcsidev->pd_pi)) {
|
||||
if (PTR_ERR(pcsidev->pd_pi) != -EPROBE_DEFER) {
|
||||
dev_err(dev, "attach pd_pi domain for pi fail\n");
|
||||
return PTR_ERR(pcsidev->pd_pi);
|
||||
} else {
|
||||
return PTR_ERR(pcsidev->pd_pi);
|
||||
}
|
||||
}
|
||||
link = device_link_add(dev, pcsidev->pd_pi,
|
||||
DL_FLAG_STATELESS |
|
||||
DL_FLAG_PM_RUNTIME |
|
||||
DL_FLAG_RPM_ACTIVE);
|
||||
if (IS_ERR(link))
|
||||
return PTR_ERR(link);
|
||||
|
||||
pcsidev->pd_isi = dev_pm_domain_attach_by_name(dev, "pd_isi_ch0");
|
||||
if (IS_ERR(pcsidev->pd_isi)) {
|
||||
if (PTR_ERR(pcsidev->pd_isi) != -EPROBE_DEFER) {
|
||||
dev_err(dev, "attach pd_isi_ch0 domain for pi fail\n");
|
||||
return PTR_ERR(pcsidev->pd_isi);
|
||||
} else {
|
||||
return PTR_ERR(pcsidev->pd_isi);
|
||||
}
|
||||
}
|
||||
link = device_link_add(dev, pcsidev->pd_isi,
|
||||
DL_FLAG_STATELESS |
|
||||
DL_FLAG_PM_RUNTIME |
|
||||
DL_FLAG_RPM_ACTIVE);
|
||||
if (IS_ERR(link))
|
||||
return PTR_ERR(link);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxc_pcsi_clk_enable(struct mxc_parallel_csi_dev *pcsidev)
|
||||
{
|
||||
struct device *dev = &pcsidev->pdev->dev;
|
||||
int ret;
|
||||
|
||||
if (pcsidev->clk_enable)
|
||||
return 0;
|
||||
|
||||
ret = clk_prepare_enable(pcsidev->clk_pixel);
|
||||
if (ret < 0) {
|
||||
dev_info(dev, "enable pixel clk error (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(pcsidev->clk_ipg);
|
||||
if (ret < 0) {
|
||||
dev_info(dev, "enable ipg clk error (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
pcsidev->clk_enable = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mxc_pcsi_clk_disable(struct mxc_parallel_csi_dev *pcsidev)
|
||||
{
|
||||
if (!pcsidev->clk_enable)
|
||||
return;
|
||||
|
||||
clk_disable_unprepare(pcsidev->clk_pixel);
|
||||
clk_disable_unprepare(pcsidev->clk_ipg);
|
||||
|
||||
pcsidev->clk_enable = false;
|
||||
}
|
||||
|
||||
static void mxc_pcsi_sw_reset(struct mxc_parallel_csi_dev *pcsidev)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/* Softwaret Reset */
|
||||
val = CSI_CTRL_REG_SOFTRST;
|
||||
writel(val, pcsidev->csr_regs + CSI_CTRL_REG_SET);
|
||||
|
||||
msleep(1);
|
||||
writel(val, pcsidev->csr_regs + CSI_CTRL_REG_CLR);
|
||||
}
|
||||
|
||||
static void mxc_pcsi_csr_config(struct mxc_parallel_csi_dev *pcsidev)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/* Software Reset */
|
||||
mxc_pcsi_sw_reset(pcsidev);
|
||||
|
||||
/* Config PL Data Type */
|
||||
val = IF_CTRL_REG_DATA_TYPE(DATA_TYPE_OUT_YUV444);
|
||||
writel(val, pcsidev->csr_regs + IF_CTRL_REG_SET);
|
||||
|
||||
/* Enable sync Force */
|
||||
val = (CSI_CTRL_REG_HSYNC_FORCE_EN | CSI_CTRL_REG_VSYNC_FORCE_EN);
|
||||
writel(val, pcsidev->csr_regs + CSI_CTRL_REG_SET);
|
||||
|
||||
/* Enable Pixel Link */
|
||||
val = IF_CTRL_REG_PL_ENABLE;
|
||||
writel(val, pcsidev->csr_regs + IF_CTRL_REG_SET);
|
||||
|
||||
/* Enable Pixel Link */
|
||||
val = IF_CTRL_REG_PL_VALID;
|
||||
writel(val, pcsidev->csr_regs + IF_CTRL_REG_SET);
|
||||
|
||||
/* Config CTRL REG */
|
||||
val = readl(pcsidev->csr_regs + CSI_CTRL_REG);
|
||||
val |= (CSI_CTRL_REG_DATA_TYPE_IN(DATA_TYPE_IN_UYVY_BT656_8BITS) |
|
||||
CSI_CTRL_REG_HSYNC_POL |
|
||||
CSI_CTRL_REG_MASK_VSYNC_COUNTER(3) |
|
||||
CSI_CTRL_REG_HSYNC_PULSE(2));
|
||||
|
||||
if (pcsidev->uv_swap)
|
||||
val |= CSI_CTRL_REG_UV_SWAP_EN;
|
||||
|
||||
if (pcsidev->mode & PI_GATE_CLOCK_MODE) {
|
||||
val |= CSI_CTRL_REG_GCLK_MODE_EN;
|
||||
} else if (pcsidev->mode & PI_CCIR_MODE) {
|
||||
val |= (CSI_CTRL_REG_CCIR_EN |
|
||||
CSI_CTRL_REG_CCIR_VSYNC_RESET_EN |
|
||||
CSI_CTRL_REG_CCIR_EXT_VSYNC_EN |
|
||||
CSI_CTRL_REG_CCIR_ECC_ERR_CORRECT_EN);
|
||||
}
|
||||
|
||||
writel(val, pcsidev->csr_regs + CSI_CTRL_REG);
|
||||
}
|
||||
|
||||
static void mxc_pcsi_config_ctrl_reg1(struct mxc_parallel_csi_dev *pcsidev)
|
||||
{
|
||||
struct device *dev = &pcsidev->pdev->dev;
|
||||
u32 val;
|
||||
|
||||
if (pcsidev->format.width <= 0 || pcsidev->format.height <= 0) {
|
||||
dev_dbg(dev, "%s width/height invalid\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Config Pixel Width */
|
||||
val = (CSI_CTRL_REG1_PIXEL_WIDTH(pcsidev->format.width - 1) |
|
||||
CSI_CTRL_REG1_VSYNC_PULSE(pcsidev->format.width << 1));
|
||||
writel(val, pcsidev->csr_regs + CSI_CTRL_REG1);
|
||||
}
|
||||
|
||||
static void mxc_pcsi_enable_csi(struct mxc_parallel_csi_dev *pcsidev)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/* Enable CSI */
|
||||
val = CSI_CTRL_REG_CSI_EN;
|
||||
writel(val, pcsidev->csr_regs + CSI_CTRL_REG_SET);
|
||||
|
||||
/* Disable SYNC Force */
|
||||
val = (CSI_CTRL_REG_HSYNC_FORCE_EN | CSI_CTRL_REG_VSYNC_FORCE_EN);
|
||||
writel(val, pcsidev->csr_regs + CSI_CTRL_REG_CLR);
|
||||
}
|
||||
|
||||
static void mxc_pcsi_disable_csi(struct mxc_parallel_csi_dev *pcsidev)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/* Enable Sync Force */
|
||||
val = (CSI_CTRL_REG_HSYNC_FORCE_EN | CSI_CTRL_REG_VSYNC_FORCE_EN);
|
||||
writel(val, pcsidev->csr_regs + CSI_CTRL_REG_SET);
|
||||
|
||||
/* Disable CSI */
|
||||
val = CSI_CTRL_REG_CSI_EN;
|
||||
writel(val, pcsidev->csr_regs + CSI_CTRL_REG_CLR);
|
||||
|
||||
/* Disable Pixel Link */
|
||||
val = IF_CTRL_REG_PL_VALID | IF_CTRL_REG_PL_ENABLE;
|
||||
writel(val, pcsidev->csr_regs + IF_CTRL_REG_CLR);
|
||||
}
|
||||
|
||||
static struct media_pad *
|
||||
mxc_pcsi_get_remote_sensor_pad(struct mxc_parallel_csi_dev *pcsidev)
|
||||
{
|
||||
struct v4l2_subdev *subdev = &pcsidev->sd;
|
||||
struct media_pad *sink_pad, *source_pad;
|
||||
int i;
|
||||
|
||||
while (1) {
|
||||
source_pad = NULL;
|
||||
for (i = 0; i < subdev->entity.num_pads; i++) {
|
||||
sink_pad = &subdev->entity.pads[i];
|
||||
|
||||
if (sink_pad->flags & MEDIA_PAD_FL_SINK) {
|
||||
source_pad = media_entity_remote_pad(sink_pad);
|
||||
if (source_pad)
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* return first pad point in the loop */
|
||||
return source_pad;
|
||||
}
|
||||
|
||||
if (i == subdev->entity.num_pads)
|
||||
v4l2_err(&pcsidev->v4l2_dev,
|
||||
"%s, No remote pad found!\n", __func__);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct v4l2_subdev *mxc_get_remote_subdev(struct mxc_parallel_csi_dev *pcsidev,
|
||||
const char * const label)
|
||||
{
|
||||
struct media_pad *source_pad;
|
||||
struct v4l2_subdev *sen_sd;
|
||||
|
||||
/* Get remote source pad */
|
||||
source_pad = mxc_pcsi_get_remote_sensor_pad(pcsidev);
|
||||
if (!source_pad) {
|
||||
v4l2_err(&pcsidev->sd, "%s, No remote pad found!\n", label);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get remote source pad subdev */
|
||||
sen_sd = media_entity_to_v4l2_subdev(source_pad->entity);
|
||||
if (!sen_sd) {
|
||||
v4l2_err(&pcsidev->sd, "%s, No remote subdev found!\n", label);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return sen_sd;
|
||||
}
|
||||
|
||||
static int mxc_pcsi_get_sensor_fmt(struct mxc_parallel_csi_dev *pcsidev)
|
||||
{
|
||||
struct v4l2_mbus_framefmt *mf = &pcsidev->format;
|
||||
struct v4l2_subdev *sen_sd;
|
||||
struct media_pad *source_pad;
|
||||
struct v4l2_subdev_format src_fmt;
|
||||
int ret;
|
||||
|
||||
/* Get remote source pad */
|
||||
source_pad = mxc_pcsi_get_remote_sensor_pad(pcsidev);
|
||||
if (!source_pad) {
|
||||
v4l2_err(&pcsidev->v4l2_dev, "%s, No remote pad found!\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Get remote source pad subdev */
|
||||
sen_sd = media_entity_to_v4l2_subdev(source_pad->entity);
|
||||
if (!sen_sd) {
|
||||
v4l2_err(&pcsidev->v4l2_dev, "%s, No remote subdev found!\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
src_fmt.pad = source_pad->index;
|
||||
src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
|
||||
ret = v4l2_subdev_call(sen_sd, pad, get_fmt, NULL, &src_fmt);
|
||||
if (ret < 0 && ret != -ENOIOCTLCMD)
|
||||
return -EINVAL;
|
||||
|
||||
/* Update input frame size and formate */
|
||||
memcpy(mf, &src_fmt.format, sizeof(struct v4l2_mbus_framefmt));
|
||||
|
||||
if (mf->code == MEDIA_BUS_FMT_YUYV8_2X8 ||
|
||||
mf->code == MEDIA_BUS_FMT_UYVY8_2X8)
|
||||
pcsidev->uv_swap = 1;
|
||||
|
||||
dev_dbg(&pcsidev->pdev->dev,
|
||||
"width=%d, height=%d, fmt.code=0x%x\n",
|
||||
mf->width, mf->height, mf->code);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxc_pcsi_enum_framesizes(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_pad_config *cfg,
|
||||
struct v4l2_subdev_frame_size_enum *fse)
|
||||
{
|
||||
struct mxc_parallel_csi_dev *pcsidev = sd_to_mxc_pcsi_dev(sd);
|
||||
struct v4l2_subdev *sen_sd;
|
||||
|
||||
sen_sd = mxc_get_remote_subdev(pcsidev, __func__);
|
||||
if (!sen_sd)
|
||||
return -EINVAL;
|
||||
|
||||
return v4l2_subdev_call(sen_sd, pad, enum_frame_size, NULL, fse);
|
||||
}
|
||||
|
||||
static int mxc_pcsi_enum_frame_interval(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_pad_config *cfg,
|
||||
struct v4l2_subdev_frame_interval_enum *fie)
|
||||
{
|
||||
struct mxc_parallel_csi_dev *pcsidev = sd_to_mxc_pcsi_dev(sd);
|
||||
struct v4l2_subdev *sen_sd;
|
||||
|
||||
sen_sd = mxc_get_remote_subdev(pcsidev, __func__);
|
||||
if (!sen_sd)
|
||||
return -EINVAL;
|
||||
|
||||
return v4l2_subdev_call(sen_sd, pad, enum_frame_interval, NULL, fie);
|
||||
}
|
||||
|
||||
static int mxc_pcsi_get_fmt(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_pad_config *cfg,
|
||||
struct v4l2_subdev_format *fmt)
|
||||
{
|
||||
struct mxc_parallel_csi_dev *pcsidev = sd_to_mxc_pcsi_dev(sd);
|
||||
struct v4l2_mbus_framefmt *mf = &fmt->format;
|
||||
|
||||
mxc_pcsi_get_sensor_fmt(pcsidev);
|
||||
|
||||
memcpy(mf, &pcsidev->format, sizeof(struct v4l2_mbus_framefmt));
|
||||
/* Source/Sink pads crop rectangle size */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxc_pcsi_set_fmt(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_pad_config *cfg,
|
||||
struct v4l2_subdev_format *fmt)
|
||||
{
|
||||
struct mxc_parallel_csi_dev *pcsidev = sd_to_mxc_pcsi_dev(sd);
|
||||
struct v4l2_subdev *sen_sd;
|
||||
struct media_pad *source_pad;
|
||||
int ret;
|
||||
|
||||
/* Get remote source pad */
|
||||
source_pad = mxc_pcsi_get_remote_sensor_pad(pcsidev);
|
||||
if (!source_pad) {
|
||||
v4l2_err(&pcsidev->v4l2_dev, "%s, No remote pad found!\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Get remote source pad subdev */
|
||||
sen_sd = media_entity_to_v4l2_subdev(source_pad->entity);
|
||||
if (!sen_sd) {
|
||||
v4l2_err(&pcsidev->v4l2_dev, "%s, No remote subdev found!\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fmt->pad = source_pad->index;
|
||||
ret = v4l2_subdev_call(sen_sd, pad, set_fmt, NULL, fmt);
|
||||
if (ret < 0 && ret != -ENOIOCTLCMD)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxc_pcsi_s_power(struct v4l2_subdev *sd, int on)
|
||||
{
|
||||
struct mxc_parallel_csi_dev *pcsidev = sd_to_mxc_pcsi_dev(sd);
|
||||
struct v4l2_subdev *sen_sd;
|
||||
|
||||
sen_sd = mxc_get_remote_subdev(pcsidev, __func__);
|
||||
if (!sen_sd)
|
||||
return -EINVAL;
|
||||
|
||||
return v4l2_subdev_call(sen_sd, core, s_power, on);
|
||||
}
|
||||
|
||||
static int mxc_pcsi_g_frame_interval(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_frame_interval *interval)
|
||||
{
|
||||
struct mxc_parallel_csi_dev *pcsidev = sd_to_mxc_pcsi_dev(sd);
|
||||
struct v4l2_subdev *sen_sd;
|
||||
|
||||
sen_sd = mxc_get_remote_subdev(pcsidev, __func__);
|
||||
if (!sen_sd)
|
||||
return -EINVAL;
|
||||
|
||||
return v4l2_subdev_call(sen_sd, video, g_frame_interval, interval);
|
||||
}
|
||||
|
||||
static int mxc_pcsi_s_frame_interval(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_frame_interval *interval)
|
||||
{
|
||||
struct mxc_parallel_csi_dev *pcsidev = sd_to_mxc_pcsi_dev(sd);
|
||||
struct v4l2_subdev *sen_sd;
|
||||
|
||||
sen_sd = mxc_get_remote_subdev(pcsidev, __func__);
|
||||
if (!sen_sd)
|
||||
return -EINVAL;
|
||||
|
||||
return v4l2_subdev_call(sen_sd, video, s_frame_interval, interval);
|
||||
}
|
||||
|
||||
static int mxc_pcsi_s_stream(struct v4l2_subdev *sd, int enable)
|
||||
{
|
||||
struct mxc_parallel_csi_dev *pcsidev = sd_to_mxc_pcsi_dev(sd);
|
||||
struct device *dev = &pcsidev->pdev->dev;
|
||||
|
||||
dev_dbg(dev, "%s: enable = %d\n", __func__, enable);
|
||||
|
||||
if (enable) {
|
||||
pm_runtime_get_sync(dev);
|
||||
if (!pcsidev->running) {
|
||||
mxc_pcsi_get_sensor_fmt(pcsidev);
|
||||
mxc_pcsi_csr_config(pcsidev);
|
||||
mxc_pcsi_config_ctrl_reg1(pcsidev);
|
||||
mxc_pcsi_enable_csi(pcsidev);
|
||||
mxc_pcsi_regs_dump(pcsidev);
|
||||
}
|
||||
pcsidev->running++;
|
||||
} else {
|
||||
if (pcsidev->running)
|
||||
mxc_pcsi_disable_csi(pcsidev);
|
||||
pcsidev->running--;
|
||||
pm_runtime_put(dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxc_pcsi_link_setup(struct media_entity *entity,
|
||||
const struct media_pad *local,
|
||||
const struct media_pad *remote, u32 flags)
|
||||
{
|
||||
struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
|
||||
struct platform_device *pdev = v4l2_get_subdevdata(sd);
|
||||
|
||||
if (local->flags & MEDIA_PAD_FL_SOURCE) {
|
||||
switch (local->index) {
|
||||
case MXC_PARALLEL_CSI_PAD_SOURCE:
|
||||
break;
|
||||
default:
|
||||
dev_err(&pdev->dev, "%s invalid source pad\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (local->flags & MEDIA_PAD_FL_SINK) {
|
||||
switch (local->index) {
|
||||
case MXC_PARALLEL_CSI_PAD_SINK:
|
||||
break;
|
||||
default:
|
||||
dev_err(&pdev->dev, "%s invalid sink pad\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct v4l2_subdev_pad_ops pcsi_pad_ops = {
|
||||
.enum_frame_size = mxc_pcsi_enum_framesizes,
|
||||
.enum_frame_interval = mxc_pcsi_enum_frame_interval,
|
||||
.get_fmt = mxc_pcsi_get_fmt,
|
||||
.set_fmt = mxc_pcsi_set_fmt,
|
||||
};
|
||||
|
||||
static struct v4l2_subdev_core_ops pcsi_core_ops = {
|
||||
.s_power = mxc_pcsi_s_power,
|
||||
};
|
||||
|
||||
static struct v4l2_subdev_video_ops pcsi_video_ops = {
|
||||
.g_frame_interval = mxc_pcsi_g_frame_interval,
|
||||
.s_frame_interval = mxc_pcsi_s_frame_interval,
|
||||
.s_stream = mxc_pcsi_s_stream,
|
||||
};
|
||||
|
||||
static struct v4l2_subdev_ops pcsi_subdev_ops = {
|
||||
.core = &pcsi_core_ops,
|
||||
.video = &pcsi_video_ops,
|
||||
.pad = &pcsi_pad_ops,
|
||||
};
|
||||
|
||||
static const struct media_entity_operations mxc_pcsi_sd_media_ops = {
|
||||
.link_setup = mxc_pcsi_link_setup,
|
||||
};
|
||||
|
||||
static int mxc_parallel_csi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *mem_res;
|
||||
struct mxc_parallel_csi_dev *pcsidev;
|
||||
int ret;
|
||||
|
||||
pcsidev = devm_kzalloc(dev, sizeof(*pcsidev), GFP_KERNEL);
|
||||
if (!pcsidev)
|
||||
return -ENOMEM;
|
||||
|
||||
pcsidev->pdev = pdev;
|
||||
|
||||
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
pcsidev->csr_regs = devm_ioremap_resource(dev, mem_res);
|
||||
if (IS_ERR(pcsidev->csr_regs)) {
|
||||
dev_dbg(dev, "Failed to get parallel CSI CSR register\n");
|
||||
return PTR_ERR(pcsidev->csr_regs);
|
||||
}
|
||||
|
||||
ret = mxc_pcsi_clk_get(pcsidev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = mxc_pcsi_attach_pd(pcsidev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
v4l2_subdev_init(&pcsidev->sd, &pcsi_subdev_ops);
|
||||
|
||||
pcsidev->mode = PI_GATE_CLOCK_MODE;
|
||||
|
||||
pcsidev->sd.owner = THIS_MODULE;
|
||||
sprintf(pcsidev->sd.name, "%s", MXC_PARALLEL_CSI_SUBDEV_NAME);
|
||||
|
||||
pcsidev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
|
||||
pcsidev->sd.entity.function = MEDIA_ENT_F_IO_V4L;
|
||||
|
||||
pcsidev->sd.dev = dev;
|
||||
|
||||
pcsidev->pads[MXC_PARALLEL_CSI_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
|
||||
pcsidev->pads[MXC_PARALLEL_CSI_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
|
||||
|
||||
ret = media_entity_pads_init(&pcsidev->sd.entity,
|
||||
MXC_PARALLEL_CSI_PADS_NUM,
|
||||
pcsidev->pads);
|
||||
if (ret < 0)
|
||||
goto e_clkdis;
|
||||
|
||||
pcsidev->sd.entity.ops = &mxc_pcsi_sd_media_ops;
|
||||
|
||||
v4l2_set_subdevdata(&pcsidev->sd, pdev);
|
||||
platform_set_drvdata(pdev, pcsidev);
|
||||
|
||||
pcsidev->running = 0;
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
dev_info(dev, "%s probe successfully\n", __func__);
|
||||
return 0;
|
||||
|
||||
e_clkdis:
|
||||
media_entity_cleanup(&pcsidev->sd.entity);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mxc_parallel_csi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mxc_parallel_csi_dev *pcsidev =
|
||||
(struct mxc_parallel_csi_dev *)platform_get_drvdata(pdev);
|
||||
|
||||
media_entity_cleanup(&pcsidev->sd.entity);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parallel_csi_pm_suspend(struct device *dev)
|
||||
{
|
||||
return pm_runtime_force_suspend(dev);
|
||||
}
|
||||
|
||||
static int parallel_csi_pm_resume(struct device *dev)
|
||||
{
|
||||
return pm_runtime_force_resume(dev);
|
||||
}
|
||||
|
||||
static int parallel_csi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct mxc_parallel_csi_dev *pcsidev = dev_get_drvdata(dev);
|
||||
|
||||
mxc_pcsi_clk_disable(pcsidev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parallel_csi_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct mxc_parallel_csi_dev *pcsidev = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = mxc_pcsi_clk_enable(pcsidev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops parallel_csi_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(parallel_csi_pm_suspend, parallel_csi_pm_resume)
|
||||
SET_RUNTIME_PM_OPS(parallel_csi_runtime_suspend,
|
||||
parallel_csi_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
|
||||
static const struct of_device_id parallel_csi_of_match[] = {
|
||||
{ .compatible = "fsl,mxc-parallel-csi",},
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, parallel_csi_of_match);
|
||||
|
||||
static struct platform_driver parallel_csi_driver = {
|
||||
.driver = {
|
||||
.name = MXC_PARALLEL_CSI_DRIVER_NAME,
|
||||
.of_match_table = parallel_csi_of_match,
|
||||
.pm = ¶llel_csi_pm_ops,
|
||||
},
|
||||
.probe = mxc_parallel_csi_probe,
|
||||
.remove = mxc_parallel_csi_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(parallel_csi_driver);
|
||||
|
||||
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
|
||||
MODULE_DESCRIPTION("MXC PARALLEL CSI driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:" MXC_PARALLEL_CSI_DRIVER_NAME);
|
81
include/linux/mipi_csi2.h
Normal file
81
include/linux/mipi_csi2.h
Normal file
|
@ -0,0 +1,81 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
* Copyright 2019 NXP
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_MIPI_CSI2_H
|
||||
#define __INCLUDE_MIPI_CSI2_H
|
||||
|
||||
/* MIPI CSI2 registers */
|
||||
#define MIPI_CSI2_REG(offset) (offset)
|
||||
|
||||
#define MIPI_CSI2_VERSION MIPI_CSI2_REG(0x000)
|
||||
#define MIPI_CSI2_N_LANES MIPI_CSI2_REG(0x004)
|
||||
#define MIPI_CSI2_PHY_SHUTDOWNZ MIPI_CSI2_REG(0x008)
|
||||
#define MIPI_CSI2_DPHY_RSTZ MIPI_CSI2_REG(0x00c)
|
||||
#define MIPI_CSI2_CSI2_RESETN MIPI_CSI2_REG(0x010)
|
||||
#define MIPI_CSI2_PHY_STATE MIPI_CSI2_REG(0x014)
|
||||
#define MIPI_CSI2_DATA_IDS_1 MIPI_CSI2_REG(0x018)
|
||||
#define MIPI_CSI2_DATA_IDS_2 MIPI_CSI2_REG(0x01c)
|
||||
#define MIPI_CSI2_ERR1 MIPI_CSI2_REG(0x020)
|
||||
#define MIPI_CSI2_ERR2 MIPI_CSI2_REG(0x024)
|
||||
#define MIPI_CSI2_MASK1 MIPI_CSI2_REG(0x028)
|
||||
#define MIPI_CSI2_MASK2 MIPI_CSI2_REG(0x02c)
|
||||
#define MIPI_CSI2_PHY_TST_CTRL0 MIPI_CSI2_REG(0x030)
|
||||
#define MIPI_CSI2_PHY_TST_CTRL1 MIPI_CSI2_REG(0x034)
|
||||
#define MIPI_CSI2_SFT_RESET MIPI_CSI2_REG(0xf00)
|
||||
|
||||
/* mipi data type */
|
||||
#define MIPI_DT_YUV420 0x18 /* YYY.../UYVY.... */
|
||||
#define MIPI_DT_YUV420_LEGACY 0x1a /* UYY.../VYY... */
|
||||
#define MIPI_DT_YUV422 0x1e /* UYVY... */
|
||||
#define MIPI_DT_RGB444 0x20
|
||||
#define MIPI_DT_RGB555 0x21
|
||||
#define MIPI_DT_RGB565 0x22
|
||||
#define MIPI_DT_RGB666 0x23
|
||||
#define MIPI_DT_RGB888 0x24
|
||||
#define MIPI_DT_RAW6 0x28
|
||||
#define MIPI_DT_RAW7 0x29
|
||||
#define MIPI_DT_RAW8 0x2a
|
||||
#define MIPI_DT_RAW10 0x2b
|
||||
#define MIPI_DT_RAW12 0x2c
|
||||
#define MIPI_DT_RAW14 0x2d
|
||||
|
||||
|
||||
struct mipi_csi2_info;
|
||||
/* mipi csi2 API */
|
||||
struct mipi_csi2_info *mipi_csi2_get_info(void);
|
||||
|
||||
bool mipi_csi2_enable(struct mipi_csi2_info *info);
|
||||
|
||||
bool mipi_csi2_disable(struct mipi_csi2_info *info);
|
||||
|
||||
bool mipi_csi2_get_status(struct mipi_csi2_info *info);
|
||||
|
||||
int mipi_csi2_get_bind_ipu(struct mipi_csi2_info *info);
|
||||
|
||||
unsigned int mipi_csi2_get_bind_csi(struct mipi_csi2_info *info);
|
||||
|
||||
unsigned int mipi_csi2_get_virtual_channel(struct mipi_csi2_info *info);
|
||||
|
||||
unsigned int mipi_csi2_set_lanes(struct mipi_csi2_info *info);
|
||||
|
||||
unsigned int mipi_csi2_set_datatype(struct mipi_csi2_info *info,
|
||||
unsigned int datatype);
|
||||
|
||||
unsigned int mipi_csi2_get_datatype(struct mipi_csi2_info *info);
|
||||
|
||||
unsigned int mipi_csi2_dphy_status(struct mipi_csi2_info *info);
|
||||
|
||||
unsigned int mipi_csi2_get_error1(struct mipi_csi2_info *info);
|
||||
|
||||
unsigned int mipi_csi2_get_error2(struct mipi_csi2_info *info);
|
||||
|
||||
int mipi_csi2_pixelclk_enable(struct mipi_csi2_info *info);
|
||||
|
||||
void mipi_csi2_pixelclk_disable(struct mipi_csi2_info *info);
|
||||
|
||||
int mipi_csi2_reset(struct mipi_csi2_info *info);
|
||||
|
||||
#endif
|
355
include/media/v4l2-chip-ident.h
Normal file
355
include/media/v4l2-chip-ident.h
Normal file
|
@ -0,0 +1,355 @@
|
|||
/*
|
||||
v4l2 chip identifiers header
|
||||
|
||||
This header provides a list of chip identifiers that can be returned
|
||||
through the VIDIOC_DBG_G_CHIP_IDENT ioctl.
|
||||
|
||||
Copyright (C) 2011-2014 Freescale Semiconductor, Inc.
|
||||
Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef V4L2_CHIP_IDENT_H_
|
||||
#define V4L2_CHIP_IDENT_H_
|
||||
|
||||
/* VIDIOC_DBG_G_CHIP_IDENT: identifies the actual chip installed on the board */
|
||||
|
||||
/* KEEP THIS LIST ORDERED BY ID!
|
||||
Otherwise it will be hard to see which ranges are already in use when
|
||||
adding support to a new chip family. */
|
||||
enum {
|
||||
/* general idents: reserved range 0-49 */
|
||||
V4L2_IDENT_NONE = 0, /* No chip matched */
|
||||
V4L2_IDENT_AMBIGUOUS = 1, /* Match too general, multiple chips matched */
|
||||
V4L2_IDENT_UNKNOWN = 2, /* Chip found, but cannot identify */
|
||||
|
||||
/* module tvaudio: reserved range 50-99 */
|
||||
V4L2_IDENT_TVAUDIO = 50, /* A tvaudio chip, unknown which it is exactly */
|
||||
|
||||
/* Sony IMX074 */
|
||||
V4L2_IDENT_IMX074 = 74,
|
||||
|
||||
/* module saa7110: just ident 100 */
|
||||
V4L2_IDENT_SAA7110 = 100,
|
||||
|
||||
/* module saa7115: reserved range 101-149 */
|
||||
V4L2_IDENT_SAA7111 = 101,
|
||||
V4L2_IDENT_SAA7111A = 102,
|
||||
V4L2_IDENT_SAA7113 = 103,
|
||||
V4L2_IDENT_SAA7114 = 104,
|
||||
V4L2_IDENT_SAA7115 = 105,
|
||||
V4L2_IDENT_SAA7118 = 108,
|
||||
|
||||
V4L2_IDENT_GM7113C = 140,
|
||||
|
||||
/* module saa7127: reserved range 150-199 */
|
||||
V4L2_IDENT_SAA7127 = 157,
|
||||
V4L2_IDENT_SAA7129 = 159,
|
||||
|
||||
/* module cx25840: reserved range 200-249 */
|
||||
V4L2_IDENT_CX25836 = 236,
|
||||
V4L2_IDENT_CX25837 = 237,
|
||||
V4L2_IDENT_CX25840 = 240,
|
||||
V4L2_IDENT_CX25841 = 241,
|
||||
V4L2_IDENT_CX25842 = 242,
|
||||
V4L2_IDENT_CX25843 = 243,
|
||||
|
||||
/* OmniVision sensors: reserved range 250-299 */
|
||||
V4L2_IDENT_OV7670 = 250,
|
||||
V4L2_IDENT_OV7720 = 251,
|
||||
V4L2_IDENT_OV7725 = 252,
|
||||
V4L2_IDENT_OV7660 = 253,
|
||||
V4L2_IDENT_OV9650 = 254,
|
||||
V4L2_IDENT_OV9655 = 255,
|
||||
V4L2_IDENT_SOI968 = 256,
|
||||
V4L2_IDENT_OV9640 = 257,
|
||||
V4L2_IDENT_OV6650 = 258,
|
||||
V4L2_IDENT_OV2640 = 259,
|
||||
V4L2_IDENT_OV9740 = 260,
|
||||
V4L2_IDENT_OV5642 = 261,
|
||||
|
||||
/* module saa7146: reserved range 300-309 */
|
||||
V4L2_IDENT_SAA7146 = 300,
|
||||
|
||||
/* Conexant MPEG encoder/decoders: reserved range 400-420 */
|
||||
V4L2_IDENT_CX23418_843 = 403, /* Integrated A/V Decoder on the '418 */
|
||||
V4L2_IDENT_CX23415 = 415,
|
||||
V4L2_IDENT_CX23416 = 416,
|
||||
V4L2_IDENT_CX23417 = 417,
|
||||
V4L2_IDENT_CX23418 = 418,
|
||||
|
||||
/* module bt819: reserved range 810-819 */
|
||||
V4L2_IDENT_BT815A = 815,
|
||||
V4L2_IDENT_BT817A = 817,
|
||||
V4L2_IDENT_BT819A = 819,
|
||||
|
||||
/* module au0828 */
|
||||
V4L2_IDENT_AU0828 = 828,
|
||||
|
||||
/* module bttv: ident 848 + 849 */
|
||||
V4L2_IDENT_BT848 = 848,
|
||||
V4L2_IDENT_BT849 = 849,
|
||||
|
||||
/* module bt856: just ident 856 */
|
||||
V4L2_IDENT_BT856 = 856,
|
||||
|
||||
/* module bt866: just ident 866 */
|
||||
V4L2_IDENT_BT866 = 866,
|
||||
|
||||
/* module bttv: ident 878 + 879 */
|
||||
V4L2_IDENT_BT878 = 878,
|
||||
V4L2_IDENT_BT879 = 879,
|
||||
|
||||
/* module ks0127: reserved range 1120-1129 */
|
||||
V4L2_IDENT_KS0122S = 1122,
|
||||
V4L2_IDENT_KS0127 = 1127,
|
||||
V4L2_IDENT_KS0127B = 1128,
|
||||
|
||||
/* module indycam: just ident 2000 */
|
||||
V4L2_IDENT_INDYCAM = 2000,
|
||||
|
||||
/* module vp27smpx: just ident 2700 */
|
||||
V4L2_IDENT_VP27SMPX = 2700,
|
||||
|
||||
/* module vpx3220: reserved range: 3210-3229 */
|
||||
V4L2_IDENT_VPX3214C = 3214,
|
||||
V4L2_IDENT_VPX3216B = 3216,
|
||||
V4L2_IDENT_VPX3220A = 3220,
|
||||
|
||||
/* VX855 just ident 3409 */
|
||||
/* Other via devs could use 3314, 3324, 3327, 3336, 3364, 3353 */
|
||||
V4L2_IDENT_VIA_VX855 = 3409,
|
||||
|
||||
/* module tvp5150 */
|
||||
V4L2_IDENT_TVP5150 = 5150,
|
||||
|
||||
/* module saa5246a: just ident 5246 */
|
||||
V4L2_IDENT_SAA5246A = 5246,
|
||||
|
||||
/* module saa5249: just ident 5249 */
|
||||
V4L2_IDENT_SAA5249 = 5249,
|
||||
|
||||
/* module cs5345: just ident 5345 */
|
||||
V4L2_IDENT_CS5345 = 5345,
|
||||
|
||||
/* module tea6415c: just ident 6415 */
|
||||
V4L2_IDENT_TEA6415C = 6415,
|
||||
|
||||
/* module tea6420: just ident 6420 */
|
||||
V4L2_IDENT_TEA6420 = 6420,
|
||||
|
||||
/* module saa6588: just ident 6588 */
|
||||
V4L2_IDENT_SAA6588 = 6588,
|
||||
|
||||
/* module vs6624: just ident 6624 */
|
||||
V4L2_IDENT_VS6624 = 6624,
|
||||
|
||||
/* module saa6752hs: reserved range 6750-6759 */
|
||||
V4L2_IDENT_SAA6752HS = 6752,
|
||||
V4L2_IDENT_SAA6752HS_AC3 = 6753,
|
||||
|
||||
/* modules tef6862: just ident 6862 */
|
||||
V4L2_IDENT_TEF6862 = 6862,
|
||||
|
||||
/* module tvp7002: just ident 7002 */
|
||||
V4L2_IDENT_TVP7002 = 7002,
|
||||
|
||||
/* module adv7170: just ident 7170 */
|
||||
V4L2_IDENT_ADV7170 = 7170,
|
||||
|
||||
/* module adv7175: just ident 7175 */
|
||||
V4L2_IDENT_ADV7175 = 7175,
|
||||
|
||||
/* module adv7180: just ident 7180 */
|
||||
V4L2_IDENT_ADV7180 = 7180,
|
||||
|
||||
/* module adv7183: just ident 7183 */
|
||||
V4L2_IDENT_ADV7183 = 7183,
|
||||
|
||||
/* module saa7185: just ident 7185 */
|
||||
V4L2_IDENT_SAA7185 = 7185,
|
||||
|
||||
/* module saa7191: just ident 7191 */
|
||||
V4L2_IDENT_SAA7191 = 7191,
|
||||
|
||||
/* module ths7303: just ident 7303 */
|
||||
V4L2_IDENT_THS7303 = 7303,
|
||||
|
||||
/* module adv7343: just ident 7343 */
|
||||
V4L2_IDENT_ADV7343 = 7343,
|
||||
|
||||
/* module ths7353: just ident 7353 */
|
||||
V4L2_IDENT_THS7353 = 7353,
|
||||
|
||||
/* module adv7393: just ident 7393 */
|
||||
V4L2_IDENT_ADV7393 = 7393,
|
||||
|
||||
/* module adv7604: just ident 7604 */
|
||||
V4L2_IDENT_ADV7604 = 7604,
|
||||
|
||||
/* module saa7706h: just ident 7706 */
|
||||
V4L2_IDENT_SAA7706H = 7706,
|
||||
|
||||
/* module mt9v011, just ident 8243 */
|
||||
V4L2_IDENT_MT9V011 = 8243,
|
||||
|
||||
/* module wm8739: just ident 8739 */
|
||||
V4L2_IDENT_WM8739 = 8739,
|
||||
|
||||
/* module wm8775: just ident 8775 */
|
||||
V4L2_IDENT_WM8775 = 8775,
|
||||
|
||||
/* Marvell controllers starting at 8801 */
|
||||
V4L2_IDENT_CAFE = 8801,
|
||||
V4L2_IDENT_ARMADA610 = 8802,
|
||||
|
||||
/* AKM AK8813/AK8814 */
|
||||
V4L2_IDENT_AK8813 = 8813,
|
||||
V4L2_IDENT_AK8814 = 8814,
|
||||
|
||||
/* module cx23885 and cx25840 */
|
||||
V4L2_IDENT_CX23885 = 8850,
|
||||
V4L2_IDENT_CX23885_AV = 8851, /* Integrated A/V decoder */
|
||||
V4L2_IDENT_CX23887 = 8870,
|
||||
V4L2_IDENT_CX23887_AV = 8871, /* Integrated A/V decoder */
|
||||
V4L2_IDENT_CX23888 = 8880,
|
||||
V4L2_IDENT_CX23888_AV = 8881, /* Integrated A/V decoder */
|
||||
V4L2_IDENT_CX23888_IR = 8882, /* Integrated infrared controller */
|
||||
|
||||
/* module ad9389b: just ident 9389 */
|
||||
V4L2_IDENT_AD9389B = 9389,
|
||||
|
||||
/* module tda9840: just ident 9840 */
|
||||
V4L2_IDENT_TDA9840 = 9840,
|
||||
|
||||
/* module tw9910: just ident 9910 */
|
||||
V4L2_IDENT_TW9910 = 9910,
|
||||
|
||||
/* module sn9c20x: just ident 10000 */
|
||||
V4L2_IDENT_SN9C20X = 10000,
|
||||
|
||||
/* module cx231xx and cx25840 */
|
||||
V4L2_IDENT_CX2310X_AV = 23099, /* Integrated A/V decoder; not in '100 */
|
||||
V4L2_IDENT_CX23100 = 23100,
|
||||
V4L2_IDENT_CX23101 = 23101,
|
||||
V4L2_IDENT_CX23102 = 23102,
|
||||
|
||||
/* module msp3400: reserved range 34000-34999 for msp34xx */
|
||||
V4L2_IDENT_MSPX4XX = 34000, /* generic MSPX4XX identifier, only
|
||||
use internally (tveeprom.c). */
|
||||
|
||||
V4L2_IDENT_MSP3400B = 34002,
|
||||
V4L2_IDENT_MSP3400C = 34003,
|
||||
V4L2_IDENT_MSP3400D = 34004,
|
||||
V4L2_IDENT_MSP3400G = 34007,
|
||||
V4L2_IDENT_MSP3401G = 34017,
|
||||
V4L2_IDENT_MSP3402G = 34027,
|
||||
V4L2_IDENT_MSP3405D = 34054,
|
||||
V4L2_IDENT_MSP3405G = 34057,
|
||||
V4L2_IDENT_MSP3407D = 34074,
|
||||
V4L2_IDENT_MSP3407G = 34077,
|
||||
|
||||
V4L2_IDENT_MSP3410B = 34102,
|
||||
V4L2_IDENT_MSP3410C = 34103,
|
||||
V4L2_IDENT_MSP3410D = 34104,
|
||||
V4L2_IDENT_MSP3410G = 34107,
|
||||
V4L2_IDENT_MSP3411G = 34117,
|
||||
V4L2_IDENT_MSP3412G = 34127,
|
||||
V4L2_IDENT_MSP3415D = 34154,
|
||||
V4L2_IDENT_MSP3415G = 34157,
|
||||
V4L2_IDENT_MSP3417D = 34174,
|
||||
V4L2_IDENT_MSP3417G = 34177,
|
||||
|
||||
V4L2_IDENT_MSP3420G = 34207,
|
||||
V4L2_IDENT_MSP3421G = 34217,
|
||||
V4L2_IDENT_MSP3422G = 34227,
|
||||
V4L2_IDENT_MSP3425G = 34257,
|
||||
V4L2_IDENT_MSP3427G = 34277,
|
||||
|
||||
V4L2_IDENT_MSP3430G = 34307,
|
||||
V4L2_IDENT_MSP3431G = 34317,
|
||||
V4L2_IDENT_MSP3435G = 34357,
|
||||
V4L2_IDENT_MSP3437G = 34377,
|
||||
|
||||
V4L2_IDENT_MSP3440G = 34407,
|
||||
V4L2_IDENT_MSP3441G = 34417,
|
||||
V4L2_IDENT_MSP3442G = 34427,
|
||||
V4L2_IDENT_MSP3445G = 34457,
|
||||
V4L2_IDENT_MSP3447G = 34477,
|
||||
|
||||
V4L2_IDENT_MSP3450G = 34507,
|
||||
V4L2_IDENT_MSP3451G = 34517,
|
||||
V4L2_IDENT_MSP3452G = 34527,
|
||||
V4L2_IDENT_MSP3455G = 34557,
|
||||
V4L2_IDENT_MSP3457G = 34577,
|
||||
|
||||
V4L2_IDENT_MSP3460G = 34607,
|
||||
V4L2_IDENT_MSP3461G = 34617,
|
||||
V4L2_IDENT_MSP3465G = 34657,
|
||||
V4L2_IDENT_MSP3467G = 34677,
|
||||
|
||||
/* module msp3400: reserved range 44000-44999 for msp44xx */
|
||||
V4L2_IDENT_MSP4400G = 44007,
|
||||
V4L2_IDENT_MSP4408G = 44087,
|
||||
V4L2_IDENT_MSP4410G = 44107,
|
||||
V4L2_IDENT_MSP4418G = 44187,
|
||||
V4L2_IDENT_MSP4420G = 44207,
|
||||
V4L2_IDENT_MSP4428G = 44287,
|
||||
V4L2_IDENT_MSP4440G = 44407,
|
||||
V4L2_IDENT_MSP4448G = 44487,
|
||||
V4L2_IDENT_MSP4450G = 44507,
|
||||
V4L2_IDENT_MSP4458G = 44587,
|
||||
|
||||
/* Micron CMOS sensor chips: 45000-45099 */
|
||||
V4L2_IDENT_MT9M001C12ST = 45000,
|
||||
V4L2_IDENT_MT9M001C12STM = 45005,
|
||||
V4L2_IDENT_MT9M111 = 45007,
|
||||
V4L2_IDENT_MT9M112 = 45008,
|
||||
V4L2_IDENT_MT9V022IX7ATC = 45010, /* No way to detect "normal" I77ATx */
|
||||
V4L2_IDENT_MT9V022IX7ATM = 45015, /* and "lead free" IA7ATx chips */
|
||||
V4L2_IDENT_MT9T031 = 45020,
|
||||
V4L2_IDENT_MT9T111 = 45021,
|
||||
V4L2_IDENT_MT9T112 = 45022,
|
||||
V4L2_IDENT_MT9V111 = 45031,
|
||||
V4L2_IDENT_MT9V112 = 45032,
|
||||
|
||||
/* HV7131R CMOS sensor: just ident 46000 */
|
||||
V4L2_IDENT_HV7131R = 46000,
|
||||
|
||||
/* Sharp RJ54N1CB0C, 0xCB0C = 51980 */
|
||||
V4L2_IDENT_RJ54N1CB0C = 51980,
|
||||
|
||||
/* module m52790: just ident 52790 */
|
||||
V4L2_IDENT_M52790 = 52790,
|
||||
|
||||
/* module cs53132a: just ident 53132 */
|
||||
V4L2_IDENT_CS53l32A = 53132,
|
||||
|
||||
/* modules upd61151 MPEG2 encoder: just ident 54000 */
|
||||
V4L2_IDENT_UPD61161 = 54000,
|
||||
/* modules upd61152 MPEG2 encoder with AC3: just ident 54001 */
|
||||
V4L2_IDENT_UPD61162 = 54001,
|
||||
|
||||
/* module upd64031a: just ident 64031 */
|
||||
V4L2_IDENT_UPD64031A = 64031,
|
||||
|
||||
/* module upd64083: just ident 64083 */
|
||||
V4L2_IDENT_UPD64083 = 64083,
|
||||
|
||||
/* Don't just add new IDs at the end: KEEP THIS LIST ORDERED BY ID! */
|
||||
};
|
||||
|
||||
#endif
|
|
@ -554,6 +554,8 @@ struct v4l2_ioctl_ops {
|
|||
int (*vidioc_g_chip_info)(struct file *file, void *fh,
|
||||
struct v4l2_dbg_chip_info *chip);
|
||||
#endif
|
||||
int (*vidioc_g_chip_ident) (struct file *file, void *fh,
|
||||
struct v4l2_dbg_chip_ident *chip);
|
||||
|
||||
int (*vidioc_enum_framesizes)(struct file *file, void *fh,
|
||||
struct v4l2_frmsizeenum *fsize);
|
||||
|
|
|
@ -88,6 +88,7 @@ struct v4l2_decode_vbi_line {
|
|||
/*
|
||||
* Core ops: it is highly recommended to implement at least these ops:
|
||||
*
|
||||
* g_chip_ident
|
||||
* log_status
|
||||
* g_register
|
||||
* s_register
|
||||
|
@ -186,6 +187,7 @@ struct v4l2_subdev_io_pin_config {
|
|||
* @unsubscribe_event: remove event subscription from the control framework.
|
||||
*/
|
||||
struct v4l2_subdev_core_ops {
|
||||
int (*g_chip_ident)(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip);
|
||||
int (*log_status)(struct v4l2_subdev *sd);
|
||||
int (*s_io_pin_config)(struct v4l2_subdev *sd, size_t n,
|
||||
struct v4l2_subdev_io_pin_config *pincfg);
|
||||
|
@ -388,6 +390,10 @@ struct v4l2_mbus_frame_desc {
|
|||
*
|
||||
* @g_pixelaspect: callback to return the pixelaspect ratio.
|
||||
*
|
||||
* @g_parm: callback for VIDIOC_G_PARM() ioctl handler code.
|
||||
*
|
||||
* @s_parm: callback for VIDIOC_S_PARM() ioctl handler code.
|
||||
*
|
||||
* @g_frame_interval: callback for VIDIOC_SUBDEV_G_FRAME_INTERVAL()
|
||||
* ioctl handler code.
|
||||
*
|
||||
|
@ -425,6 +431,8 @@ struct v4l2_subdev_video_ops {
|
|||
int (*g_input_status)(struct v4l2_subdev *sd, u32 *status);
|
||||
int (*s_stream)(struct v4l2_subdev *sd, int enable);
|
||||
int (*g_pixelaspect)(struct v4l2_subdev *sd, struct v4l2_fract *aspect);
|
||||
int (*g_parm)(struct v4l2_subdev *sd, struct v4l2_streamparm *param);
|
||||
int (*s_parm)(struct v4l2_subdev *sd, struct v4l2_streamparm *param);
|
||||
int (*g_frame_interval)(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_frame_interval *interval);
|
||||
int (*s_frame_interval)(struct v4l2_subdev *sd,
|
||||
|
|
73
include/uapi/linux/mxc_v4l2.h
Normal file
73
include/uapi/linux/mxc_v4l2.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2015 Freescale Semiconductor, Inc. All Rights Reserved
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* @file uapi/linux/mxc_v4l2.h
|
||||
*
|
||||
* @brief MXC V4L2 private header file
|
||||
*
|
||||
* @ingroup MXC V4L2
|
||||
*/
|
||||
|
||||
#ifndef __ASM_ARCH_MXC_V4L2_H__
|
||||
#define __ASM_ARCH_MXC_V4L2_H__
|
||||
|
||||
/*
|
||||
* For IPUv1 and IPUv3, V4L2_CID_MXC_ROT means encoder ioctl ID.
|
||||
* And V4L2_CID_MXC_VF_ROT is viewfinder ioctl ID only for IPUv1 and IPUv3.
|
||||
*/
|
||||
#define V4L2_CID_MXC_ROT (V4L2_CID_PRIVATE_BASE + 0)
|
||||
#define V4L2_CID_MXC_FLASH (V4L2_CID_PRIVATE_BASE + 1)
|
||||
#define V4L2_CID_MXC_VF_ROT (V4L2_CID_PRIVATE_BASE + 2)
|
||||
#define V4L2_CID_MXC_MOTION (V4L2_CID_PRIVATE_BASE + 3)
|
||||
#define V4L2_CID_MXC_SWITCH_CAM (V4L2_CID_PRIVATE_BASE + 6)
|
||||
|
||||
#define V4L2_MXC_ROTATE_NONE 0
|
||||
#define V4L2_MXC_ROTATE_VERT_FLIP 1
|
||||
#define V4L2_MXC_ROTATE_HORIZ_FLIP 2
|
||||
#define V4L2_MXC_ROTATE_180 3
|
||||
#define V4L2_MXC_ROTATE_90_RIGHT 4
|
||||
#define V4L2_MXC_ROTATE_90_RIGHT_VFLIP 5
|
||||
#define V4L2_MXC_ROTATE_90_RIGHT_HFLIP 6
|
||||
#define V4L2_MXC_ROTATE_90_LEFT 7
|
||||
|
||||
struct v4l2_mxc_offset {
|
||||
uint32_t u_offset;
|
||||
uint32_t v_offset;
|
||||
};
|
||||
|
||||
struct v4l2_mxc_dest_crop {
|
||||
__u32 type; /* enum v4l2_buf_type */
|
||||
struct v4l2_mxc_offset offset;
|
||||
};
|
||||
|
||||
/*
|
||||
* Private IOCTLs
|
||||
*
|
||||
* VIDIOC_S_INOUT_CROP: Set input stream crop size
|
||||
* VIDIOC_G_INOUT_CROP: Get input stream crop size
|
||||
*/
|
||||
#define VIDIOC_S_INPUT_CROP \
|
||||
_IOWR('V', BASE_VIDIOC_PRIVATE + 1, struct v4l2_crop)
|
||||
#define VIDIOC_G_INPUT_CROP \
|
||||
_IOWR('V', BASE_VIDIOC_PRIVATE + 2, struct v4l2_crop)
|
||||
#define VIDIOC_S_DEST_CROP \
|
||||
_IOWR('V', BASE_VIDIOC_PRIVATE + 3, struct v4l2_mxc_dest_crop)
|
||||
#endif
|
|
@ -554,6 +554,8 @@ struct v4l2_pix_format {
|
|||
#define V4L2_PIX_FMT_ARGB32 v4l2_fourcc('B', 'A', '2', '4') /* 32 ARGB-8-8-8-8 */
|
||||
#define V4L2_PIX_FMT_XRGB32 v4l2_fourcc('B', 'X', '2', '4') /* 32 XRGB-8-8-8-8 */
|
||||
|
||||
#define V4L2_PIX_FMT_RGBA v4l2_fourcc('R', 'G', 'B', 'A') /* 32 RGBA-8-8-8-8 */
|
||||
|
||||
/* Grey formats */
|
||||
#define V4L2_PIX_FMT_GREY v4l2_fourcc('G', 'R', 'E', 'Y') /* 8 Greyscale */
|
||||
#define V4L2_PIX_FMT_Y4 v4l2_fourcc('Y', '0', '4', ' ') /* 4 Greyscale */
|
||||
|
@ -583,6 +585,7 @@ struct v4l2_pix_format {
|
|||
#define V4L2_PIX_FMT_YUV444 v4l2_fourcc('Y', '4', '4', '4') /* 16 xxxxyyyy uuuuvvvv */
|
||||
#define V4L2_PIX_FMT_YUV555 v4l2_fourcc('Y', 'U', 'V', 'O') /* 16 YUV-5-5-5 */
|
||||
#define V4L2_PIX_FMT_YUV565 v4l2_fourcc('Y', 'U', 'V', 'P') /* 16 YUV-5-6-5 */
|
||||
#define V4L2_PIX_FMT_YUV24 v4l2_fourcc('Y', 'U', 'V', '3') /* 24 YUV-8-8-8 */
|
||||
#define V4L2_PIX_FMT_YUV32 v4l2_fourcc('Y', 'U', 'V', '4') /* 32 YUV-8-8-8-8 */
|
||||
#define V4L2_PIX_FMT_AYUV32 v4l2_fourcc('A', 'Y', 'U', 'V') /* 32 AYUV-8-8-8-8 */
|
||||
#define V4L2_PIX_FMT_XYUV32 v4l2_fourcc('X', 'Y', 'U', 'V') /* 32 XYUV-8-8-8-8 */
|
||||
|
@ -2352,13 +2355,11 @@ struct v4l2_event_subscription {
|
|||
/* VIDIOC_DBG_G_REGISTER and VIDIOC_DBG_S_REGISTER */
|
||||
|
||||
#define V4L2_CHIP_MATCH_BRIDGE 0 /* Match against chip ID on the bridge (0 for the bridge) */
|
||||
#define V4L2_CHIP_MATCH_SUBDEV 4 /* Match against subdev index */
|
||||
|
||||
/* The following four defines are no longer in use */
|
||||
#define V4L2_CHIP_MATCH_HOST V4L2_CHIP_MATCH_BRIDGE
|
||||
#define V4L2_CHIP_MATCH_I2C_DRIVER 1 /* Match against I2C driver name */
|
||||
#define V4L2_CHIP_MATCH_I2C_ADDR 2 /* Match against I2C 7-bit address */
|
||||
#define V4L2_CHIP_MATCH_AC97 3 /* Match against ancillary AC97 chip */
|
||||
#define V4L2_CHIP_MATCH_SUBDEV 4 /* Match against subdev index */
|
||||
|
||||
struct v4l2_dbg_match {
|
||||
__u32 type; /* Match type */
|
||||
|
@ -2375,6 +2376,13 @@ struct v4l2_dbg_register {
|
|||
__u64 val;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* VIDIOC_DBG_G_CHIP_IDENT */
|
||||
struct v4l2_dbg_chip_ident {
|
||||
struct v4l2_dbg_match match;
|
||||
__u32 ident; /* chip identifier as specified in <media/v4l2-chip-ident.h> */
|
||||
__u32 revision; /* chip revision, chip specific */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define V4L2_CHIP_FL_READABLE (1 << 0)
|
||||
#define V4L2_CHIP_FL_WRITABLE (1 << 1)
|
||||
|
||||
|
@ -2480,6 +2488,12 @@ struct v4l2_create_buffers {
|
|||
#define VIDIOC_DBG_S_REGISTER _IOW('V', 79, struct v4l2_dbg_register)
|
||||
#define VIDIOC_DBG_G_REGISTER _IOWR('V', 80, struct v4l2_dbg_register)
|
||||
|
||||
/* Experimental, meant for debugging, testing and internal use.
|
||||
Never use this ioctl in applications!
|
||||
Note: this ioctl is deprecated in favor of VIDIOC_DBG_G_CHIP_INFO and
|
||||
will go away in the future. */
|
||||
#define VIDIOC_DBG_G_CHIP_IDENT _IOWR('V', 81, struct v4l2_dbg_chip_ident)
|
||||
|
||||
#define VIDIOC_S_HW_FREQ_SEEK _IOW('V', 82, struct v4l2_hw_freq_seek)
|
||||
#define VIDIOC_S_DV_TIMINGS _IOWR('V', 87, struct v4l2_dv_timings)
|
||||
#define VIDIOC_G_DV_TIMINGS _IOWR('V', 88, struct v4l2_dv_timings)
|
||||
|
|
Loading…
Reference in New Issue
Block a user