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:
Dong Aisheng 2019-12-02 18:02:07 +08:00
commit 1e188020fb
71 changed files with 41603 additions and 43 deletions

View 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>;
};

View 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";
};

View 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";
...
};
...
};

View 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>;
};
};
};

View File

@ -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>;
};
};
};

View File

@ -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``

View File

@ -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

View File

@ -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)

View File

@ -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"

View File

@ -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/

View 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

View 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

View 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);
}

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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

View 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);

View 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

View 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

File diff suppressed because it is too large Load Diff

View 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(&params, 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, &params);
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");

View 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(&params, 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, &params);
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");

View 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(&params, 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, &params);
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");

View 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");

View 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

View 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");

View 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");

View 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(&params, 0, sizeof(params));
err = ipu_init_channel(cam->ipu, CSI_MEM, &params);
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");

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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__ */

View 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");

View 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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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");

View 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

View File

@ -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);

View File

@ -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
View File

@ -0,0 +1 @@
source "drivers/mxc/mipi/Kconfig"

1
drivers/mxc/Makefile Normal file
View File

@ -0,0 +1 @@
obj-$(CONFIG_MXC_MIPI_CSI2) += mipi/

14
drivers/mxc/mipi/Kconfig Normal file
View 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

View File

@ -0,0 +1,4 @@
#
# Makefile for the mipi interface driver
#
obj-$(CONFIG_MXC_MIPI_CSI2) += mxc_mipi_csi2.o

View 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");

View 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

View File

@ -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/

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

View 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_ */

File diff suppressed because it is too large Load Diff

View 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");

View 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__ */

View 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);
}

View 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__ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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 = &parallel_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
View 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

View 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

View File

@ -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);

View File

@ -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,

View 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

View File

@ -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)