diff options
214 files changed, 7487 insertions, 5073 deletions
diff --git a/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-csi.yaml b/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-csi.yaml index f1ccca35a790..b3d6db922693 100644 --- a/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-csi.yaml +++ b/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-csi.yaml @@ -73,6 +73,10 @@ properties: $ref: /schemas/graph.yaml#/properties/port description: MIPI CSI-2 bridge input port + port@2: + $ref: /schemas/graph.yaml#/properties/port + description: Internal output port to the ISP + anyOf: - required: - port@0 diff --git a/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-isp.yaml b/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-isp.yaml new file mode 100644 index 000000000000..6bda4f2b94c2 --- /dev/null +++ b/Documentation/devicetree/bindings/media/allwinner,sun6i-a31-isp.yaml @@ -0,0 +1,101 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/allwinner,sun6i-a31-isp.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Allwinner A31 Image Signal Processor Driver (ISP) Device Tree Bindings + +maintainers: + - Paul Kocialkowski <[email protected]> + +properties: + compatible: + enum: + - allwinner,sun6i-a31-isp + - allwinner,sun8i-v3s-isp + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: Bus Clock + - description: Module Clock + - description: DRAM Clock + + clock-names: + items: + - const: bus + - const: mod + - const: ram + + resets: + maxItems: 1 + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/properties/port + description: CSI0 input port + + port@1: + $ref: /schemas/graph.yaml#/properties/port + description: CSI1 input port + + if: + properties: + compatible: + contains: + enum: + - allwinner,sun8i-v3s-isp + then: + required: + - port@0 + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - resets + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/clock/sun8i-v3s-ccu.h> + #include <dt-bindings/reset/sun8i-v3s-ccu.h> + + isp: isp@1cb8000 { + compatible = "allwinner,sun8i-v3s-isp"; + reg = <0x01cb8000 0x1000>; + interrupts = <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu CLK_BUS_CSI>, + <&ccu CLK_CSI1_SCLK>, + <&ccu CLK_DRAM_CSI>; + clock-names = "bus", "mod", "ram"; + resets = <&ccu RST_BUS_CSI>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + isp_in_csi0: endpoint { + remote-endpoint = <&csi0_out_isp>; + }; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/media/i2c/ov5645.txt b/Documentation/devicetree/bindings/media/i2c/ov5645.txt deleted file mode 100644 index 72ad992f77be..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/ov5645.txt +++ /dev/null @@ -1,54 +0,0 @@ -* Omnivision 1/4-Inch 5Mp CMOS Digital Image Sensor - -The Omnivision OV5645 is a 1/4-Inch CMOS active pixel digital image sensor with -an active array size of 2592H x 1944V. It is programmable through a serial I2C -interface. - -Required Properties: -- compatible: Value should be "ovti,ov5645". -- clocks: Reference to the xclk clock. -- clock-names: Should be "xclk". -- clock-frequency: Frequency of the xclk clock. -- enable-gpios: Chip enable GPIO. Polarity is GPIO_ACTIVE_HIGH. This corresponds - to the hardware pin PWDNB which is physically active low. -- reset-gpios: Chip reset GPIO. Polarity is GPIO_ACTIVE_LOW. This corresponds to - the hardware pin RESETB. -- vdddo-supply: Chip digital IO regulator. -- vdda-supply: Chip analog regulator. -- vddd-supply: Chip digital core regulator. - -The device node must contain one 'port' child node for its digital output -video port, in accordance with the video interface bindings defined in -Documentation/devicetree/bindings/media/video-interfaces.txt. - -Example: - - &i2c1 { - ... - - ov5645: ov5645@3c { - compatible = "ovti,ov5645"; - reg = <0x3c>; - - enable-gpios = <&gpio1 6 GPIO_ACTIVE_HIGH>; - reset-gpios = <&gpio5 20 GPIO_ACTIVE_LOW>; - pinctrl-names = "default"; - pinctrl-0 = <&camera_rear_default>; - - clocks = <&clks 200>; - clock-names = "xclk"; - clock-frequency = <24000000>; - - vdddo-supply = <&camera_dovdd_1v8>; - vdda-supply = <&camera_avdd_2v8>; - vddd-supply = <&camera_dvdd_1v2>; - - port { - ov5645_ep: endpoint { - clock-lanes = <1>; - data-lanes = <0 2>; - remote-endpoint = <&csi0_ep>; - }; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov5645.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov5645.yaml new file mode 100644 index 000000000000..52c6281a6684 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov5645.yaml @@ -0,0 +1,104 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/ovti,ov5645.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: OmniVision OV5645 Image Sensor Device Tree Bindings + +maintainers: + - Lad Prabhakar <[email protected]> + +properties: + compatible: + const: ovti,ov5645 + + reg: + maxItems: 1 + + clocks: + description: XCLK Input Clock + + clock-frequency: + description: Frequency of the xclk clock in Hz. + + vdda-supply: + description: Analog voltage supply, 2.8 volts + + vddd-supply: + description: Digital core voltage supply, 1.5 volts + + vdddo-supply: + description: Digital I/O voltage supply, 1.8 volts + + enable-gpios: + maxItems: 1 + description: + Reference to the GPIO connected to the PWDNB pin, if any. + + reset-gpios: + maxItems: 1 + description: + Reference to the GPIO connected to the RESETB pin, if any. + + port: + description: Digital Output Port + $ref: /schemas/graph.yaml#/$defs/port-base + additionalProperties: false + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + minItems: 1 + maxItems: 2 + items: + enum: [1, 2] + + required: + - data-lanes + +required: + - compatible + - reg + - clocks + - vdddo-supply + - vdda-supply + - vddd-supply + - port + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + camera@3c { + compatible = "ovti,ov5645"; + reg = <0x3c>; + clocks = <&clks 1>; + clock-frequency = <24000000>; + vdddo-supply = <&ov5645_vdddo_1v8>; + vdda-supply = <&ov5645_vdda_2v8>; + vddd-supply = <&ov5645_vddd_1v5>; + enable-gpios = <&gpio1 19 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio1 20 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ov5645>; + + port { + ov5645_ep: endpoint { + remote-endpoint = <&csi0_ep>; + data-lanes = <1 2>; + }; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx412.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx412.yaml index 26d1807d0bb6..60dc25ff2b9e 100644 --- a/Documentation/devicetree/bindings/media/i2c/sony,imx412.yaml +++ b/Documentation/devicetree/bindings/media/i2c/sony,imx412.yaml @@ -19,7 +19,9 @@ description: properties: compatible: - const: sony,imx412 + enum: + - sony,imx412 + - sony,imx577 reg: description: I2C address maxItems: 1 diff --git a/MAINTAINERS b/MAINTAINERS index 333c821717d7..6f1b811eed91 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -775,6 +775,24 @@ T: git git://linuxtv.org/media_tree.git F: Documentation/devicetree/bindings/media/allwinner,sun4i-a10-csi.yaml F: drivers/media/platform/sunxi/sun4i-csi/ +ALLWINNER A31 CSI DRIVER +M: Yong Deng <[email protected]> +M: Paul Kocialkowski <[email protected]> +S: Maintained +T: git git://linuxtv.org/media_tree.git +F: Documentation/devicetree/bindings/media/allwinner,sun6i-a31-csi.yaml +F: drivers/media/platform/sunxi/sun6i-csi/ + +ALLWINNER A31 ISP DRIVER +M: Paul Kocialkowski <[email protected]> +S: Maintained +T: git git://linuxtv.org/media_tree.git +F: Documentation/devicetree/bindings/media/allwinner,sun6i-a31-isp.yaml +F: drivers/staging/media/sunxi/sun6i-isp/ +F: drivers/staging/media/sunxi/sun6i-isp/uapi/sun6i-isp-config.h + ALLWINNER A31 MIPI CSI-2 BRIDGE DRIVER M: Paul Kocialkowski <[email protected]> @@ -5502,14 +5520,6 @@ M: Jaya Kumar <[email protected]> S: Maintained F: sound/pci/cs5535audio/ -CSI DRIVERS FOR ALLWINNER V3s -M: Yong Deng <[email protected]> -S: Maintained -T: git git://linuxtv.org/media_tree.git -F: Documentation/devicetree/bindings/media/allwinner,sun6i-a31-csi.yaml -F: drivers/media/platform/sunxi/sun6i-csi/ - CTU CAN FD DRIVER M: Pavel Pisa <[email protected]> M: Ondrej Ille <[email protected]> diff --git a/arch/arm/boot/dts/imx6qdl-pico.dtsi b/arch/arm/boot/dts/imx6qdl-pico.dtsi index f7a56d6b160c..c39a9ebdaba1 100644 --- a/arch/arm/boot/dts/imx6qdl-pico.dtsi +++ b/arch/arm/boot/dts/imx6qdl-pico.dtsi @@ -233,7 +233,6 @@ pinctrl-0 = <&pinctrl_ov5645>; reg = <0x3c>; clocks = <&clks IMX6QDL_CLK_CKO2>; - clock-names = "xclk"; clock-frequency = <24000000>; vdddo-supply = <®_1p8v>; vdda-supply = <®_2p8v>; diff --git a/arch/arm/boot/dts/imx6qdl-wandboard.dtsi b/arch/arm/boot/dts/imx6qdl-wandboard.dtsi index ec6fba5ee8fd..e4f63423d8ee 100644 --- a/arch/arm/boot/dts/imx6qdl-wandboard.dtsi +++ b/arch/arm/boot/dts/imx6qdl-wandboard.dtsi @@ -131,7 +131,6 @@ pinctrl-0 = <&pinctrl_ov5645>; reg = <0x3c>; clocks = <&clks IMX6QDL_CLK_CKO2>; - clock-names = "xclk"; clock-frequency = <24000000>; vdddo-supply = <®_1p8v>; vdda-supply = <®_2p8v>; diff --git a/arch/arm64/boot/dts/renesas/aistarvision-mipi-adapter-2.1.dtsi b/arch/arm64/boot/dts/renesas/aistarvision-mipi-adapter-2.1.dtsi index 7ce986f0a06f..7cb5c958aece 100644 --- a/arch/arm64/boot/dts/renesas/aistarvision-mipi-adapter-2.1.dtsi +++ b/arch/arm64/boot/dts/renesas/aistarvision-mipi-adapter-2.1.dtsi @@ -65,7 +65,6 @@ ov5645: ov5645@3c { compatible = "ovti,ov5645"; reg = <0x3c>; - clock-names = "xclk"; clocks = <&osc25250_clk>; clock-frequency = <24000000>; vdddo-supply = <&ov5645_vdddo_1v8>; diff --git a/drivers/media/dvb-frontends/a8293.c b/drivers/media/dvb-frontends/a8293.c index ba38783b2b4f..3d9f87359c71 100644 --- a/drivers/media/dvb-frontends/a8293.c +++ b/drivers/media/dvb-frontends/a8293.c @@ -62,8 +62,7 @@ err: return ret; } -static int a8293_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int a8293_probe(struct i2c_client *client) { struct a8293_dev *dev; struct a8293_platform_data *pdata = client->dev.platform_data; @@ -118,7 +117,7 @@ static struct i2c_driver a8293_driver = { .name = "a8293", .suppress_bind_attrs = true, }, - .probe = a8293_probe, + .probe_new = a8293_probe, .remove = a8293_remove, .id_table = a8293_id_table, }; diff --git a/drivers/media/dvb-frontends/af9013.c b/drivers/media/dvb-frontends/af9013.c index d85929582c3f..206758a73ae2 100644 --- a/drivers/media/dvb-frontends/af9013.c +++ b/drivers/media/dvb-frontends/af9013.c @@ -1430,8 +1430,7 @@ err: return ret; } -static int af9013_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int af9013_probe(struct i2c_client *client) { struct af9013_state *state; struct af9013_platform_data *pdata = client->dev.platform_data; @@ -1564,7 +1563,7 @@ static struct i2c_driver af9013_driver = { .name = "af9013", .suppress_bind_attrs = true, }, - .probe = af9013_probe, + .probe_new = af9013_probe, .remove = af9013_remove, .id_table = af9013_id_table, }; diff --git a/drivers/media/dvb-frontends/af9033.c b/drivers/media/dvb-frontends/af9033.c index 808da7a9ffe7..a30773f62006 100644 --- a/drivers/media/dvb-frontends/af9033.c +++ b/drivers/media/dvb-frontends/af9033.c @@ -1049,8 +1049,7 @@ static const struct dvb_frontend_ops af9033_ops = { .i2c_gate_ctrl = af9033_i2c_gate_ctrl, }; -static int af9033_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int af9033_probe(struct i2c_client *client) { struct af9033_config *cfg = client->dev.platform_data; struct af9033_dev *dev; @@ -1184,7 +1183,7 @@ static struct i2c_driver af9033_driver = { .name = "af9033", .suppress_bind_attrs = true, }, - .probe = af9033_probe, + .probe_new = af9033_probe, .remove = af9033_remove, .id_table = af9033_id_table, }; diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c index e4f99bd468cb..0f748cf46089 100644 --- a/drivers/media/dvb-frontends/au8522_decoder.c +++ b/drivers/media/dvb-frontends/au8522_decoder.c @@ -669,8 +669,7 @@ static const struct v4l2_ctrl_ops au8522_ctrl_ops = { /* ----------------------------------------------------------------------- */ -static int au8522_probe(struct i2c_client *client, - const struct i2c_device_id *did) +static int au8522_probe(struct i2c_client *client) { struct au8522_state *state; struct v4l2_ctrl_handler *hdl; @@ -777,7 +776,7 @@ static struct i2c_driver au8522_driver = { .driver = { .name = "au8522", }, - .probe = au8522_probe, + .probe_new = au8522_probe, .remove = au8522_remove, .id_table = au8522_id, }; diff --git a/drivers/media/dvb-frontends/cxd2099.c b/drivers/media/dvb-frontends/cxd2099.c index fbc666fa04ec..c0967ad95220 100644 --- a/drivers/media/dvb-frontends/cxd2099.c +++ b/drivers/media/dvb-frontends/cxd2099.c @@ -598,8 +598,7 @@ static const struct dvb_ca_en50221 en_templ = { .write_data = write_data, }; -static int cxd2099_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int cxd2099_probe(struct i2c_client *client) { struct cxd *ci; struct cxd2099_cfg *cfg = client->dev.platform_data; @@ -682,7 +681,7 @@ static struct i2c_driver cxd2099_driver = { .driver = { .name = "cxd2099", }, - .probe = cxd2099_probe, + .probe_new = cxd2099_probe, .remove = cxd2099_remove, .id_table = cxd2099_id, }; diff --git a/drivers/media/dvb-frontends/cxd2820r_core.c b/drivers/media/dvb-frontends/cxd2820r_core.c index 5d98222f9df0..b8d5cb3a269d 100644 --- a/drivers/media/dvb-frontends/cxd2820r_core.c +++ b/drivers/media/dvb-frontends/cxd2820r_core.c @@ -547,8 +547,7 @@ static struct dvb_frontend *cxd2820r_get_dvb_frontend(struct i2c_client *client) return &priv->fe; } -static int cxd2820r_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int cxd2820r_probe(struct i2c_client *client) { struct cxd2820r_platform_data *pdata = client->dev.platform_data; struct cxd2820r_priv *priv; @@ -734,7 +733,7 @@ static struct i2c_driver cxd2820r_driver = { .name = "cxd2820r", .suppress_bind_attrs = true, }, - .probe = cxd2820r_probe, + .probe_new = cxd2820r_probe, .remove = cxd2820r_remove, .id_table = cxd2820r_id_table, }; diff --git a/drivers/media/dvb-frontends/cxd2820r_priv.h b/drivers/media/dvb-frontends/cxd2820r_priv.h index 09c42bcef971..9b4d9cf8563d 100644 --- a/drivers/media/dvb-frontends/cxd2820r_priv.h +++ b/drivers/media/dvb-frontends/cxd2820r_priv.h @@ -52,8 +52,6 @@ struct cxd2820r_priv { /* cxd2820r_core.c */ -extern int cxd2820r_debug; - int cxd2820r_gpio(struct dvb_frontend *fe, u8 *gpio); int cxd2820r_wr_reg_val_mask_tab(struct cxd2820r_priv *priv, diff --git a/drivers/media/dvb-frontends/drx39xyj/drx_dap_fasi.h b/drivers/media/dvb-frontends/drx39xyj/drx_dap_fasi.h index 739dc5590fa4..9df34c10d22b 100644 --- a/drivers/media/dvb-frontends/drx39xyj/drx_dap_fasi.h +++ b/drivers/media/dvb-frontends/drx39xyj/drx_dap_fasi.h @@ -234,8 +234,6 @@ /*-------- Public API functions ----------------------------------------------*/ -extern struct drx_access_func drx_dap_fasi_funct_g; - #define DRXDAP_FASI_RMW 0x10000000 #define DRXDAP_FASI_BROADCAST 0x20000000 #define DRXDAP_FASI_CLEARCRC 0x80000000 diff --git a/drivers/media/dvb-frontends/helene.c b/drivers/media/dvb-frontends/helene.c index 8c1310c6b0bc..e4bbf6a51a2b 100644 --- a/drivers/media/dvb-frontends/helene.c +++ b/drivers/media/dvb-frontends/helene.c @@ -1063,8 +1063,7 @@ struct dvb_frontend *helene_attach(struct dvb_frontend *fe, } EXPORT_SYMBOL(helene_attach); -static int helene_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int helene_probe(struct i2c_client *client) { struct helene_config *config = client->dev.platform_data; struct dvb_frontend *fe = config->fe; @@ -1111,7 +1110,7 @@ static struct i2c_driver helene_driver = { .driver = { .name = "helene", }, - .probe = helene_probe, + .probe_new = helene_probe, .id_table = helene_id, }; module_i2c_driver(helene_driver); diff --git a/drivers/media/dvb-frontends/lgdt3306a.c b/drivers/media/dvb-frontends/lgdt3306a.c index 424311afb2bf..6bf723b5ffad 100644 --- a/drivers/media/dvb-frontends/lgdt3306a.c +++ b/drivers/media/dvb-frontends/lgdt3306a.c @@ -2169,8 +2169,7 @@ static int lgdt3306a_deselect(struct i2c_mux_core *muxc, u32 chan) return lgdt3306a_i2c_gate_ctrl(&state->frontend, 0); } -static int lgdt3306a_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int lgdt3306a_probe(struct i2c_client *client) { struct lgdt3306a_config *config; struct lgdt3306a_state *state; @@ -2250,7 +2249,7 @@ static struct i2c_driver lgdt3306a_driver = { .name = "lgdt3306a", .suppress_bind_attrs = true, }, - .probe = lgdt3306a_probe, + .probe_new = lgdt3306a_probe, .remove = lgdt3306a_remove, .id_table = lgdt3306a_id_table, }; diff --git a/drivers/media/dvb-frontends/lgdt330x.c b/drivers/media/dvb-frontends/lgdt330x.c index ea9ae22fd201..1d6932d8e497 100644 --- a/drivers/media/dvb-frontends/lgdt330x.c +++ b/drivers/media/dvb-frontends/lgdt330x.c @@ -857,8 +857,7 @@ static struct dvb_frontend *lgdt330x_get_dvb_frontend(struct i2c_client *client) static const struct dvb_frontend_ops lgdt3302_ops; static const struct dvb_frontend_ops lgdt3303_ops; -static int lgdt330x_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int lgdt330x_probe(struct i2c_client *client) { struct lgdt330x_state *state = NULL; u8 buf[1]; @@ -994,7 +993,7 @@ static struct i2c_driver lgdt330x_driver = { .name = "lgdt330x", .suppress_bind_attrs = true, }, - .probe = lgdt330x_probe, + .probe_new = lgdt330x_probe, .remove = lgdt330x_remove, .id_table = lgdt330x_id_table, }; diff --git a/drivers/media/dvb-frontends/mn88472.c b/drivers/media/dvb-frontends/mn88472.c index 2b01cc678f7e..4a71f1c6371a 100644 --- a/drivers/media/dvb-frontends/mn88472.c +++ b/drivers/media/dvb-frontends/mn88472.c @@ -572,8 +572,7 @@ static struct dvb_frontend *mn88472_get_dvb_frontend(struct i2c_client *client) return &dev->fe; } -static int mn88472_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int mn88472_probe(struct i2c_client *client) { struct mn88472_config *pdata = client->dev.platform_data; struct mn88472_dev *dev; @@ -719,7 +718,7 @@ static struct i2c_driver mn88472_driver = { .name = "mn88472", .suppress_bind_attrs = true, }, - .probe = mn88472_probe, + .probe_new = mn88472_probe, .remove = mn88472_remove, .id_table = mn88472_id_table, }; diff --git a/drivers/media/dvb-frontends/mn88473.c b/drivers/media/dvb-frontends/mn88473.c index f0ecf5910c02..205b14ae584e 100644 --- a/drivers/media/dvb-frontends/mn88473.c +++ b/drivers/media/dvb-frontends/mn88473.c @@ -606,8 +606,7 @@ static const struct dvb_frontend_ops mn88473_ops = { .read_status = mn88473_read_status, }; -static int mn88473_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int mn88473_probe(struct i2c_client *client) { struct mn88473_config *config = client->dev.platform_data; struct mn88473_dev *dev; @@ -754,7 +753,7 @@ static struct i2c_driver mn88473_driver = { .name = "mn88473", .suppress_bind_attrs = true, }, - .probe = mn88473_probe, + .probe_new = mn88473_probe, .remove = mn88473_remove, .id_table = mn88473_id_table, }; diff --git a/drivers/media/dvb-frontends/mxl692.c b/drivers/media/dvb-frontends/mxl692.c index 129630cbffff..9858e11943a0 100644 --- a/drivers/media/dvb-frontends/mxl692.c +++ b/drivers/media/dvb-frontends/mxl692.c @@ -1308,8 +1308,7 @@ static const struct dvb_frontend_ops mxl692_ops = { .read_snr = mxl692_read_snr, }; -static int mxl692_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int mxl692_probe(struct i2c_client *client) { struct mxl692_config *config = client->dev.platform_data; struct mxl692_dev *dev; @@ -1356,7 +1355,7 @@ static struct i2c_driver mxl692_driver = { .driver = { .name = "mxl692", }, - .probe = mxl692_probe, + .probe_new = mxl692_probe, .remove = mxl692_remove, .id_table = mxl692_id_table, }; diff --git a/drivers/media/dvb-frontends/rtl2830.c b/drivers/media/dvb-frontends/rtl2830.c index e0fbf41316ae..db3254950147 100644 --- a/drivers/media/dvb-frontends/rtl2830.c +++ b/drivers/media/dvb-frontends/rtl2830.c @@ -768,8 +768,7 @@ static int rtl2830_regmap_gather_write(void *context, const void *reg, return 0; } -static int rtl2830_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int rtl2830_probe(struct i2c_client *client) { struct rtl2830_platform_data *pdata = client->dev.platform_data; struct rtl2830_dev *dev; @@ -887,7 +886,7 @@ static struct i2c_driver rtl2830_driver = { .name = "rtl2830", .suppress_bind_attrs = true, }, - .probe = rtl2830_probe, + .probe_new = rtl2830_probe, .remove = rtl2830_remove, .id_table = rtl2830_id_table, }; diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index 4fa884eda5d5..900d4db8b922 100644 --- a/drivers/media/dvb-frontends/rtl2832.c +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -1021,8 +1021,7 @@ err: return ret; } -static int rtl2832_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int rtl2832_probe(struct i2c_client *client) { struct rtl2832_platform_data *pdata = client->dev.platform_data; struct i2c_adapter *i2c = client->adapter; @@ -1136,7 +1135,7 @@ static struct i2c_driver rtl2832_driver = { .name = "rtl2832", .suppress_bind_attrs = true, }, - .probe = rtl2832_probe, + .probe_new = rtl2832_probe, .remove = rtl2832_remove, .id_table = rtl2832_id_table, }; diff --git a/drivers/media/dvb-frontends/si2165.c b/drivers/media/dvb-frontends/si2165.c index 86b0d59169dd..cc07e965c34c 100644 --- a/drivers/media/dvb-frontends/si2165.c +++ b/drivers/media/dvb-frontends/si2165.c @@ -1144,8 +1144,7 @@ static const struct dvb_frontend_ops si2165_ops = { .read_ber = si2165_read_ber, }; -static int si2165_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int si2165_probe(struct i2c_client *client) { struct si2165_state *state = NULL; struct si2165_platform_data *pdata = client->dev.platform_data; @@ -1293,7 +1292,7 @@ static struct i2c_driver si2165_driver = { .driver = { .name = "si2165", }, - .probe = si2165_probe, + .probe_new = si2165_probe, .remove = si2165_remove, .id_table = si2165_id_table, }; diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index 8157df4570d1..2a0e108c5eb0 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -672,8 +672,7 @@ static const struct dvb_frontend_ops si2168_ops = { .read_status = si2168_read_status, }; -static int si2168_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int si2168_probe(struct i2c_client *client) { struct si2168_config *config = client->dev.platform_data; struct si2168_dev *dev; @@ -799,7 +798,7 @@ static struct i2c_driver si2168_driver = { .name = "si2168", .suppress_bind_attrs = true, }, - .probe = si2168_probe, + .probe_new = si2168_probe, .remove = si2168_remove, .id_table = si2168_id_table, }; diff --git a/drivers/media/dvb-frontends/sp2.c b/drivers/media/dvb-frontends/sp2.c index 27e7037e130e..3395f6b5b948 100644 --- a/drivers/media/dvb-frontends/sp2.c +++ b/drivers/media/dvb-frontends/sp2.c @@ -363,8 +363,7 @@ static int sp2_exit(struct i2c_client *client) return 0; } -static int sp2_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int sp2_probe(struct i2c_client *client) { struct sp2_config *cfg = client->dev.platform_data; struct sp2 *s; @@ -417,7 +416,7 @@ static struct i2c_driver sp2_driver = { .driver = { .name = "sp2", }, - .probe = sp2_probe, + .probe_new = sp2_probe, .remove = sp2_remove, .id_table = sp2_id, }; diff --git a/drivers/media/dvb-frontends/stv090x.c b/drivers/media/dvb-frontends/stv090x.c index 0a600c1d7d1b..9bde0ad6f26e 100644 --- a/drivers/media/dvb-frontends/stv090x.c +++ b/drivers/media/dvb-frontends/stv090x.c @@ -4990,8 +4990,7 @@ static struct dvb_frontend *stv090x_get_dvb_frontend(struct i2c_client *client) return &state->frontend; } -static int stv090x_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int stv090x_probe(struct i2c_client *client) { int ret = 0; struct stv090x_config *config = client->dev.platform_data; @@ -5085,7 +5084,7 @@ static struct i2c_driver stv090x_driver = { .name = "stv090x", .suppress_bind_attrs = true, }, - .probe = stv090x_probe, + .probe_new = stv090x_probe, .remove = stv090x_remove, .id_table = stv090x_id_table, }; diff --git a/drivers/media/dvb-frontends/stv6110x.c b/drivers/media/dvb-frontends/stv6110x.c index fbc4dbd62151..b2f456116c60 100644 --- a/drivers/media/dvb-frontends/stv6110x.c +++ b/drivers/media/dvb-frontends/stv6110x.c @@ -406,8 +406,7 @@ static struct stv6110x_devctl *stv6110x_get_devctl(struct i2c_client *client) return stv6110x->devctl; } -static int stv6110x_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int stv6110x_probe(struct i2c_client *client) { struct stv6110x_config *config = client->dev.platform_data; @@ -481,7 +480,7 @@ static struct i2c_driver stv6110x_driver = { .name = "stv6110x", .suppress_bind_attrs = true, }, - .probe = stv6110x_probe, + .probe_new = stv6110x_probe, .remove = stv6110x_remove, .id_table = stv6110x_id_table, }; diff --git a/drivers/media/dvb-frontends/tda10071.c b/drivers/media/dvb-frontends/tda10071.c index d1098ef20a8b..c8e5617d08c0 100644 --- a/drivers/media/dvb-frontends/tda10071.c +++ b/drivers/media/dvb-frontends/tda10071.c @@ -1145,8 +1145,7 @@ static struct dvb_frontend *tda10071_get_dvb_frontend(struct i2c_client *client) return &dev->fe; } -static int tda10071_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tda10071_probe(struct i2c_client *client) { struct tda10071_dev *dev; struct tda10071_platform_data *pdata = client->dev.platform_data; @@ -1241,7 +1240,7 @@ static struct i2c_driver tda10071_driver = { .name = "tda10071", .suppress_bind_attrs = true, }, - .probe = tda10071_probe, + .probe_new = tda10071_probe, .remove = tda10071_remove, .id_table = tda10071_id_table, }; diff --git a/drivers/media/dvb-frontends/ts2020.c b/drivers/media/dvb-frontends/ts2020.c index 02338256b974..c28fee7509cd 100644 --- a/drivers/media/dvb-frontends/ts2020.c +++ b/drivers/media/dvb-frontends/ts2020.c @@ -550,8 +550,7 @@ static void ts2020_regmap_unlock(void *__dev) mutex_unlock(&dev->regmap_mutex); } -static int ts2020_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ts2020_probe(struct i2c_client *client) { struct ts2020_config *pdata = client->dev.platform_data; struct dvb_frontend *fe = pdata->fe; @@ -721,7 +720,7 @@ static struct i2c_driver ts2020_driver = { .driver = { .name = "ts2020", }, - .probe = ts2020_probe, + .probe_new = ts2020_probe, .remove = ts2020_remove, .id_table = ts2020_id_table, }; diff --git a/drivers/media/i2c/ad5820.c b/drivers/media/i2c/ad5820.c index a12fedcc3a1c..9945d17fadd6 100644 --- a/drivers/media/i2c/ad5820.c +++ b/drivers/media/i2c/ad5820.c @@ -290,8 +290,7 @@ static int __maybe_unused ad5820_resume(struct device *dev) return ad5820_power_on(coil, true); } -static int ad5820_probe(struct i2c_client *client, - const struct i2c_device_id *devid) +static int ad5820_probe(struct i2c_client *client) { struct ad5820_device *coil; int ret; @@ -377,7 +376,7 @@ static struct i2c_driver ad5820_i2c_driver = { .pm = &ad5820_pm, .of_match_table = ad5820_of_table, }, - .probe = ad5820_probe, + .probe_new = ad5820_probe, .remove = ad5820_remove, .id_table = ad5820_id_table, }; diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index 4a255a492918..ad17097a2d25 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -1080,7 +1080,7 @@ static void ad9389b_init_setup(struct v4l2_subdev *sd) ad9389b_set_isr(sd, false); } -static int ad9389b_probe(struct i2c_client *client, const struct i2c_device_id *id) +static int ad9389b_probe(struct i2c_client *client) { const struct v4l2_dv_timings dv1080p60 = V4L2_DV_BT_CEA_1920X1080P60; struct ad9389b_state *state; @@ -1207,7 +1207,7 @@ static struct i2c_driver ad9389b_driver = { .driver = { .name = "ad9389b", }, - .probe = ad9389b_probe, + .probe_new = ad9389b_probe, .remove = ad9389b_remove, .id_table = ad9389b_id, }; diff --git a/drivers/media/i2c/adp1653.c b/drivers/media/i2c/adp1653.c index 1f353157df07..a61a77de6eee 100644 --- a/drivers/media/i2c/adp1653.c +++ b/drivers/media/i2c/adp1653.c @@ -463,8 +463,7 @@ err: } -static int adp1653_probe(struct i2c_client *client, - const struct i2c_device_id *devid) +static int adp1653_probe(struct i2c_client *client) { struct adp1653_flash *flash; int ret; @@ -536,7 +535,7 @@ static struct i2c_driver adp1653_i2c_driver = { .name = ADP1653_NAME, .pm = &adp1653_pm_ops, }, - .probe = adp1653_probe, + .probe_new = adp1653_probe, .remove = adp1653_remove, .id_table = adp1653_id_table, }; diff --git a/drivers/media/i2c/adv7170.c b/drivers/media/i2c/adv7170.c index 61a2f87d3c62..aa0f80e299b3 100644 --- a/drivers/media/i2c/adv7170.c +++ b/drivers/media/i2c/adv7170.c @@ -334,8 +334,7 @@ static const struct v4l2_subdev_ops adv7170_ops = { /* ----------------------------------------------------------------------- */ -static int adv7170_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adv7170_probe(struct i2c_client *client) { struct adv7170 *encoder; struct v4l2_subdev *sd; @@ -388,7 +387,7 @@ static struct i2c_driver adv7170_driver = { .driver = { .name = "adv7170", }, - .probe = adv7170_probe, + .probe_new = adv7170_probe, .remove = adv7170_remove, .id_table = adv7170_id, }; diff --git a/drivers/media/i2c/adv7175.c b/drivers/media/i2c/adv7175.c index b58689728243..d9bea2b9ec33 100644 --- a/drivers/media/i2c/adv7175.c +++ b/drivers/media/i2c/adv7175.c @@ -389,8 +389,7 @@ static const struct v4l2_subdev_ops adv7175_ops = { /* ----------------------------------------------------------------------- */ -static int adv7175_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adv7175_probe(struct i2c_client *client) { int i; struct adv7175 *encoder; @@ -443,7 +442,7 @@ static struct i2c_driver adv7175_driver = { .driver = { .name = "adv7175", }, - .probe = adv7175_probe, + .probe_new = adv7175_probe, .remove = adv7175_remove, .id_table = adv7175_id, }; diff --git a/drivers/media/i2c/adv7183.c b/drivers/media/i2c/adv7183.c index 313c706e8335..98b63d79d33d 100644 --- a/drivers/media/i2c/adv7183.c +++ b/drivers/media/i2c/adv7183.c @@ -521,8 +521,7 @@ static const struct v4l2_subdev_ops adv7183_ops = { .pad = &adv7183_pad_ops, }; -static int adv7183_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adv7183_probe(struct i2c_client *client) { struct adv7183 *decoder; struct v4l2_subdev *sd; @@ -632,7 +631,7 @@ static struct i2c_driver adv7183_driver = { .driver = { .name = "adv7183", }, - .probe = adv7183_probe, + .probe_new = adv7183_probe, .remove = adv7183_remove, .id_table = adv7183_id, }; diff --git a/drivers/media/i2c/adv7393.c b/drivers/media/i2c/adv7393.c index fb5fefa83b18..61e916cbe651 100644 --- a/drivers/media/i2c/adv7393.c +++ b/drivers/media/i2c/adv7393.c @@ -381,8 +381,7 @@ static int adv7393_initialize(struct v4l2_subdev *sd) return err; } -static int adv7393_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adv7393_probe(struct i2c_client *client) { struct adv7393_state *state; int err; @@ -456,7 +455,7 @@ static struct i2c_driver adv7393_driver = { .driver = { .name = "adv7393", }, - .probe = adv7393_probe, + .probe_new = adv7393_probe, .remove = adv7393_remove, .id_table = adv7393_id, }; diff --git a/drivers/media/i2c/adv748x/adv748x-afe.c b/drivers/media/i2c/adv748x/adv748x-afe.c index 02eabe10ab97..00095c7762c2 100644 --- a/drivers/media/i2c/adv748x/adv748x-afe.c +++ b/drivers/media/i2c/adv748x/adv748x-afe.c @@ -521,6 +521,10 @@ int adv748x_afe_init(struct adv748x_afe *afe) } } + adv748x_afe_s_input(afe, afe->input); + + adv_dbg(state, "AFE Default input set to %d\n", afe->input); + /* Entity pads and sinks are 0-indexed to match the pads */ for (i = ADV748X_AFE_SINK_AIN0; i <= ADV748X_AFE_SINK_AIN7; i++) afe->pads[i].flags = MEDIA_PAD_FL_SINK; diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h index d75eb3d8be5a..6f90f78f58cf 100644 --- a/drivers/media/i2c/adv748x/adv748x.h +++ b/drivers/media/i2c/adv748x/adv748x.h @@ -428,9 +428,6 @@ void adv748x_subdev_init(struct v4l2_subdev *sd, struct adv748x_state *state, const struct v4l2_subdev_ops *ops, u32 function, const char *ident); -int adv748x_register_subdevs(struct adv748x_state *state, - struct v4l2_device *v4l2_dev); - int adv748x_tx_power(struct adv748x_csi2 *tx, bool on); int adv748x_afe_init(struct adv748x_afe *afe); diff --git a/drivers/media/i2c/adv7511-v4l2.c b/drivers/media/i2c/adv7511-v4l2.c index 0d5ce69f12e7..3999fa524cab 100644 --- a/drivers/media/i2c/adv7511-v4l2.c +++ b/drivers/media/i2c/adv7511-v4l2.c @@ -1763,7 +1763,7 @@ static void adv7511_init_setup(struct v4l2_subdev *sd) adv7511_cec_write(sd, 0x4e, ratio << 2); } -static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *id) +static int adv7511_probe(struct i2c_client *client) { struct adv7511_state *state; struct adv7511_platform_data *pdata = client->dev.platform_data; @@ -1957,7 +1957,7 @@ static struct i2c_driver adv7511_driver = { .driver = { .name = "adv7511-v4l2", }, - .probe = adv7511_probe, + .probe_new = adv7511_probe, .remove = adv7511_remove, .id_table = adv7511_id, }; diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 7731cc1887e6..cb8655574119 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -3441,8 +3441,7 @@ static int adv7842_register_clients(struct v4l2_subdev *sd) return 0; } -static int adv7842_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adv7842_probe(struct i2c_client *client) { struct adv7842_state *state; static const struct v4l2_dv_timings cea640x480 = @@ -3620,7 +3619,7 @@ static struct i2c_driver adv7842_driver = { .driver = { .name = "adv7842", }, - .probe = adv7842_probe, + .probe_new = adv7842_probe, .remove = adv7842_remove, .id_table = adv7842_id, }; diff --git a/drivers/media/i2c/ak881x.c b/drivers/media/i2c/ak881x.c index 0370ad6b6811..7c9ab76e2448 100644 --- a/drivers/media/i2c/ak881x.c +++ b/drivers/media/i2c/ak881x.c @@ -226,8 +226,7 @@ static const struct v4l2_subdev_ops ak881x_subdev_ops = { .pad = &ak881x_subdev_pad_ops, }; -static int ak881x_probe(struct i2c_client *client, - const struct i2c_device_id *did) +static int ak881x_probe(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; struct ak881x *ak881x; @@ -315,7 +314,7 @@ static struct i2c_driver ak881x_i2c_driver = { .driver = { .name = "ak881x", }, - .probe = ak881x_probe, + .probe_new = ak881x_probe, .remove = ak881x_remove, .id_table = ak881x_id, }; diff --git a/drivers/media/i2c/bt819.c b/drivers/media/i2c/bt819.c index 4d9bb6eb7d65..39f8a5361166 100644 --- a/drivers/media/i2c/bt819.c +++ b/drivers/media/i2c/bt819.c @@ -380,8 +380,7 @@ static const struct v4l2_subdev_ops bt819_ops = { /* ----------------------------------------------------------------------- */ -static int bt819_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int bt819_probe(struct i2c_client *client) { int i, ver; struct bt819 *decoder; @@ -469,7 +468,7 @@ static struct i2c_driver bt819_driver = { .driver = { .name = "bt819", }, - .probe = bt819_probe, + .probe_new = bt819_probe, .remove = bt819_remove, .id_table = bt819_id, }; diff --git a/drivers/media/i2c/bt856.c b/drivers/media/i2c/bt856.c index 70443ef1ac46..d1d397b15b85 100644 --- a/drivers/media/i2c/bt856.c +++ b/drivers/media/i2c/bt856.c @@ -181,8 +181,7 @@ static const struct v4l2_subdev_ops bt856_ops = { /* ----------------------------------------------------------------------- */ -static int bt856_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int bt856_probe(struct i2c_client *client) { struct bt856 *encoder; struct v4l2_subdev *sd; @@ -240,7 +239,7 @@ static struct i2c_driver bt856_driver = { .driver = { .name = "bt856", }, - .probe = bt856_probe, + .probe_new = bt856_probe, .remove = bt856_remove, .id_table = bt856_id, }; diff --git a/drivers/media/i2c/bt866.c b/drivers/media/i2c/bt866.c index c2508cbafd02..d632d9a07f04 100644 --- a/drivers/media/i2c/bt866.c +++ b/drivers/media/i2c/bt866.c @@ -173,8 +173,7 @@ static const struct v4l2_subdev_ops bt866_ops = { .video = &bt866_video_ops, }; -static int bt866_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int bt866_probe(struct i2c_client *client) { struct bt866 *encoder; struct v4l2_subdev *sd; @@ -207,7 +206,7 @@ static struct i2c_driver bt866_driver = { .driver = { .name = "bt866", }, - .probe = bt866_probe, + .probe_new = bt866_probe, .remove = bt866_remove, .id_table = bt866_id, }; diff --git a/drivers/media/i2c/cs3308.c b/drivers/media/i2c/cs3308.c index d901a59883a9..a0b66c04fe25 100644 --- a/drivers/media/i2c/cs3308.c +++ b/drivers/media/i2c/cs3308.c @@ -64,8 +64,7 @@ static const struct v4l2_subdev_ops cs3308_ops = { /* ----------------------------------------------------------------------- */ -static int cs3308_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int cs3308_probe(struct i2c_client *client) { struct v4l2_subdev *sd; unsigned i; @@ -119,7 +118,7 @@ static struct i2c_driver cs3308_driver = { .driver = { .name = "cs3308", }, - .probe = cs3308_probe, + .probe_new = cs3308_probe, .remove = cs3308_remove, .id_table = cs3308_id, }; diff --git a/drivers/media/i2c/cs5345.c b/drivers/media/i2c/cs5345.c index 591b1e7b24ee..ac4b5632fc46 100644 --- a/drivers/media/i2c/cs5345.c +++ b/drivers/media/i2c/cs5345.c @@ -136,8 +136,7 @@ static const struct v4l2_subdev_ops cs5345_ops = { /* ----------------------------------------------------------------------- */ -static int cs5345_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int cs5345_probe(struct i2c_client *client) { struct cs5345_state *state; struct v4l2_subdev *sd; @@ -199,7 +198,7 @@ static struct i2c_driver cs5345_driver = { .driver = { .name = "cs5345", }, - .probe = cs5345_probe, + .probe_new = cs5345_probe, .remove = cs5345_remove, .id_table = cs5345_id, }; diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c index f1a978af82ef..46cf422270b2 100644 --- a/drivers/media/i2c/cx25840/cx25840-core.c +++ b/drivers/media/i2c/cx25840/cx25840-core.c @@ -5825,8 +5825,7 @@ static u32 get_cx2388x_ident(struct i2c_client *client) return ret; } -static int cx25840_probe(struct i2c_client *client, - const struct i2c_device_id *did) +static int cx25840_probe(struct i2c_client *client) { struct cx25840_state *state; struct v4l2_subdev *sd; @@ -6046,7 +6045,7 @@ static struct i2c_driver cx25840_driver = { .driver = { .name = "cx25840", }, - .probe = cx25840_probe, + .probe_new = cx25840_probe, .remove = cx25840_remove, .id_table = cx25840_id, }; diff --git a/drivers/media/i2c/imx412.c b/drivers/media/i2c/imx412.c index 7f6d29e0e7c4..e1e986dc8856 100644 --- a/drivers/media/i2c/imx412.c +++ b/drivers/media/i2c/imx412.c @@ -1172,6 +1172,7 @@ static int imx412_init_controls(struct imx412 *imx412) static int imx412_probe(struct i2c_client *client) { struct imx412 *imx412; + const char *name; int ret; imx412 = devm_kzalloc(&client->dev, sizeof(*imx412), GFP_KERNEL); @@ -1179,6 +1180,9 @@ static int imx412_probe(struct i2c_client *client) return -ENOMEM; imx412->dev = &client->dev; + name = device_get_match_data(&client->dev); + if (!name) + return -ENODEV; /* Initialize subdev */ v4l2_i2c_subdev_init(&imx412->sd, client, &imx412_subdev_ops); @@ -1218,6 +1222,8 @@ static int imx412_probe(struct i2c_client *client) imx412->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; imx412->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + v4l2_i2c_subdev_set_name(&imx412->sd, client, name, NULL); + /* Initialize source pad */ imx412->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&imx412->sd.entity, 1, &imx412->pad); @@ -1279,7 +1285,8 @@ static const struct dev_pm_ops imx412_pm_ops = { }; static const struct of_device_id imx412_of_match[] = { - { .compatible = "sony,imx412" }, + { .compatible = "sony,imx412", .data = "imx412" }, + { .compatible = "sony,imx577", .data = "imx577" }, { } }; diff --git a/drivers/media/i2c/ks0127.c b/drivers/media/i2c/ks0127.c index 215d9a43b0b9..0d86f2db7ad2 100644 --- a/drivers/media/i2c/ks0127.c +++ b/drivers/media/i2c/ks0127.c @@ -650,7 +650,7 @@ static const struct v4l2_subdev_ops ks0127_ops = { /* ----------------------------------------------------------------------- */ -static int ks0127_probe(struct i2c_client *client, const struct i2c_device_id *id) +static int ks0127_probe(struct i2c_client *client) { struct ks0127 *ks; struct v4l2_subdev *sd; @@ -696,7 +696,7 @@ static struct i2c_driver ks0127_driver = { .driver = { .name = "ks0127", }, - .probe = ks0127_probe, + .probe_new = ks0127_probe, .remove = ks0127_remove, .id_table = ks0127_id, }; diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c index edad3138cb07..5ef613604be7 100644 --- a/drivers/media/i2c/lm3560.c +++ b/drivers/media/i2c/lm3560.c @@ -391,8 +391,7 @@ static int lm3560_init_device(struct lm3560_flash *flash) return rval; } -static int lm3560_probe(struct i2c_client *client, - const struct i2c_device_id *devid) +static int lm3560_probe(struct i2c_client *client) { struct lm3560_flash *flash; struct lm3560_platform_data *pdata = dev_get_platdata(&client->dev); @@ -468,7 +467,7 @@ static struct i2c_driver lm3560_i2c_driver = { .name = LM3560_NAME, .pm = NULL, }, - .probe = lm3560_probe, + .probe_new = lm3560_probe, .remove = lm3560_remove, .id_table = lm3560_id_table, }; diff --git a/drivers/media/i2c/lm3646.c b/drivers/media/i2c/lm3646.c index 0aaa963917d8..2a0cf74d2bed 100644 --- a/drivers/media/i2c/lm3646.c +++ b/drivers/media/i2c/lm3646.c @@ -334,8 +334,7 @@ static int lm3646_init_device(struct lm3646_flash *flash) return regmap_read(flash->regmap, REG_FLAG, ®_val); } -static int lm3646_probe(struct i2c_client *client, - const struct i2c_device_id *devid) +static int lm3646_probe(struct i2c_client *client) { struct lm3646_flash *flash; struct lm3646_platform_data *pdata = dev_get_platdata(&client->dev); @@ -397,7 +396,7 @@ static struct i2c_driver lm3646_i2c_driver = { .driver = { .name = LM3646_NAME, }, - .probe = lm3646_probe, + .probe_new = lm3646_probe, .remove = lm3646_remove, .id_table = lm3646_id_table, }; diff --git a/drivers/media/i2c/m52790.c b/drivers/media/i2c/m52790.c index 2ab91b993c33..0e6507ab7e08 100644 --- a/drivers/media/i2c/m52790.c +++ b/drivers/media/i2c/m52790.c @@ -129,8 +129,7 @@ static const struct v4l2_subdev_ops m52790_ops = { /* i2c implementation */ -static int m52790_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int m52790_probe(struct i2c_client *client) { struct m52790_state *state; struct v4l2_subdev *sd; @@ -173,7 +172,7 @@ static struct i2c_driver m52790_driver = { .driver = { .name = "m52790", }, - .probe = m52790_probe, + .probe_new = m52790_probe, .remove = m52790_remove, .id_table = m52790_id, }; diff --git a/drivers/media/i2c/m5mols/m5mols_core.c b/drivers/media/i2c/m5mols/m5mols_core.c index 2201d2a26353..2b01873ba0db 100644 --- a/drivers/media/i2c/m5mols/m5mols_core.c +++ b/drivers/media/i2c/m5mols/m5mols_core.c @@ -939,8 +939,7 @@ static irqreturn_t m5mols_irq_handler(int irq, void *data) return IRQ_HANDLED; } -static int m5mols_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int m5mols_probe(struct i2c_client *client) { const struct m5mols_platform_data *pdata = client->dev.platform_data; struct m5mols_info *info; @@ -1039,7 +1038,7 @@ static struct i2c_driver m5mols_i2c_driver = { .driver = { .name = MODULE_NAME, }, - .probe = m5mols_probe, + .probe_new = m5mols_probe, .remove = m5mols_remove, .id_table = m5mols_id, }; diff --git a/drivers/media/i2c/ml86v7667.c b/drivers/media/i2c/ml86v7667.c index 49ec59b0ca43..dbd2f0bd3651 100644 --- a/drivers/media/i2c/ml86v7667.c +++ b/drivers/media/i2c/ml86v7667.c @@ -359,8 +359,7 @@ static int ml86v7667_init(struct ml86v7667_priv *priv) return ret; } -static int ml86v7667_probe(struct i2c_client *client, - const struct i2c_device_id *did) +static int ml86v7667_probe(struct i2c_client *client) { struct ml86v7667_priv *priv; int ret; @@ -434,7 +433,7 @@ static struct i2c_driver ml86v7667_i2c_driver = { .driver = { .name = DRV_NAME, }, - .probe = ml86v7667_probe, + .probe_new = ml86v7667_probe, .remove = ml86v7667_remove, .id_table = ml86v7667_id, }; diff --git a/drivers/media/i2c/mt9m032.c b/drivers/media/i2c/mt9m032.c index 76b8c9c08c82..958cfdd73d57 100644 --- a/drivers/media/i2c/mt9m032.c +++ b/drivers/media/i2c/mt9m032.c @@ -701,8 +701,7 @@ static const struct v4l2_subdev_ops mt9m032_ops = { * Driver initialization and probing */ -static int mt9m032_probe(struct i2c_client *client, - const struct i2c_device_id *devid) +static int mt9m032_probe(struct i2c_client *client) { struct mt9m032_platform_data *pdata = client->dev.platform_data; struct i2c_adapter *adapter = client->adapter; @@ -880,7 +879,7 @@ static struct i2c_driver mt9m032_i2c_driver = { .driver = { .name = MT9M032_NAME, }, - .probe = mt9m032_probe, + .probe_new = mt9m032_probe, .remove = mt9m032_remove, .id_table = mt9m032_id_table, }; diff --git a/drivers/media/i2c/mt9t001.c b/drivers/media/i2c/mt9t001.c index d5abe4a7ef07..c635ed11388a 100644 --- a/drivers/media/i2c/mt9t001.c +++ b/drivers/media/i2c/mt9t001.c @@ -856,8 +856,7 @@ static const struct v4l2_subdev_internal_ops mt9t001_subdev_internal_ops = { .close = mt9t001_close, }; -static int mt9t001_probe(struct i2c_client *client, - const struct i2c_device_id *did) +static int mt9t001_probe(struct i2c_client *client) { struct mt9t001_platform_data *pdata = client->dev.platform_data; struct mt9t001 *mt9t001; @@ -981,7 +980,7 @@ static struct i2c_driver mt9t001_driver = { .driver = { .name = "mt9t001", }, - .probe = mt9t001_probe, + .probe_new = mt9t001_probe, .remove = mt9t001_remove, .id_table = mt9t001_id, }; diff --git a/drivers/media/i2c/mt9t112.c b/drivers/media/i2c/mt9t112.c index ad564095d0cf..a82f056787b8 100644 --- a/drivers/media/i2c/mt9t112.c +++ b/drivers/media/i2c/mt9t112.c @@ -1060,8 +1060,7 @@ done: return ret; } -static int mt9t112_probe(struct i2c_client *client, - const struct i2c_device_id *did) +static int mt9t112_probe(struct i2c_client *client) { struct mt9t112_priv *priv; int ret; @@ -1120,7 +1119,7 @@ static struct i2c_driver mt9t112_i2c_driver = { .driver = { .name = "mt9t112", }, - .probe = mt9t112_probe, + .probe_new = mt9t112_probe, .remove = mt9t112_remove, .id_table = mt9t112_id, }; diff --git a/drivers/media/i2c/mt9v011.c b/drivers/media/i2c/mt9v011.c index 9952ce06ebb2..c54c7fbf0963 100644 --- a/drivers/media/i2c/mt9v011.c +++ b/drivers/media/i2c/mt9v011.c @@ -478,8 +478,7 @@ static const struct v4l2_subdev_ops mt9v011_ops = { I2C Client & Driver ****************************************************************************/ -static int mt9v011_probe(struct i2c_client *c, - const struct i2c_device_id *id) +static int mt9v011_probe(struct i2c_client *c) { u16 version; struct mt9v011 *core; @@ -586,7 +585,7 @@ static struct i2c_driver mt9v011_driver = { .driver = { .name = "mt9v011", }, - .probe = mt9v011_probe, + .probe_new = mt9v011_probe, .remove = mt9v011_remove, .id_table = mt9v011_id, }; diff --git a/drivers/media/i2c/noon010pc30.c b/drivers/media/i2c/noon010pc30.c index ecaf5e9057f1..144bef2835f7 100644 --- a/drivers/media/i2c/noon010pc30.c +++ b/drivers/media/i2c/noon010pc30.c @@ -702,8 +702,7 @@ static int noon010_detect(struct i2c_client *client, struct noon010_info *info) return ret == NOON010PC30_ID ? 0 : -ENODEV; } -static int noon010_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int noon010_probe(struct i2c_client *client) { struct noon010_info *info; struct v4l2_subdev *sd; @@ -810,7 +809,7 @@ static struct i2c_driver noon010_i2c_driver = { .driver = { .name = MODULE_NAME }, - .probe = noon010_probe, + .probe_new = noon010_probe, .remove = noon010_remove, .id_table = noon010_id, }; diff --git a/drivers/media/i2c/ov13858.c b/drivers/media/i2c/ov13858.c index e618b613e078..69a7a2c590db 100644 --- a/drivers/media/i2c/ov13858.c +++ b/drivers/media/i2c/ov13858.c @@ -1698,8 +1698,7 @@ static void ov13858_free_controls(struct ov13858 *ov13858) mutex_destroy(&ov13858->mutex); } -static int ov13858_probe(struct i2c_client *client, - const struct i2c_device_id *devid) +static int ov13858_probe(struct i2c_client *client) { struct ov13858 *ov13858; int ret; @@ -1807,7 +1806,7 @@ static struct i2c_driver ov13858_i2c_driver = { .pm = &ov13858_pm_ops, .acpi_match_table = ACPI_PTR(ov13858_acpi_ids), }, - .probe = ov13858_probe, + .probe_new = ov13858_probe, .remove = ov13858_remove, .id_table = ov13858_id_table, }; diff --git a/drivers/media/i2c/ov5645.c b/drivers/media/i2c/ov5645.c index 47451238ca05..c8999fc4f26f 100644 --- a/drivers/media/i2c/ov5645.c +++ b/drivers/media/i2c/ov5645.c @@ -14,9 +14,6 @@ * https://www.mail-archive.com/linux-media%40vger.kernel.org/msg92671.html */ -/* - */ - #include <linux/bitops.h> #include <linux/clk.h> #include <linux/delay.h> @@ -27,6 +24,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/of_graph.h> +#include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/types.h> @@ -108,7 +106,6 @@ struct ov5645 { u8 timing_tc_reg21; struct mutex power_lock; /* lock to protect power state */ - int power_count; struct gpio_desc *enable_gpio; struct gpio_desc *rst_gpio; @@ -635,8 +632,24 @@ static int ov5645_set_register_array(struct ov5645 *ov5645, return 0; } -static int ov5645_set_power_on(struct ov5645 *ov5645) +static int ov5645_set_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov5645 *ov5645 = to_ov5645(sd); + + ov5645_write_reg(ov5645, OV5645_IO_MIPI_CTRL00, 0x58); + gpiod_set_value_cansleep(ov5645->rst_gpio, 1); + gpiod_set_value_cansleep(ov5645->enable_gpio, 0); + clk_disable_unprepare(ov5645->xclk); + regulator_bulk_disable(OV5645_NUM_SUPPLIES, ov5645->supplies); + + return 0; +} + +static int ov5645_set_power_on(struct device *dev) { + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov5645 *ov5645 = to_ov5645(sd); int ret; ret = regulator_bulk_enable(OV5645_NUM_SUPPLIES, ov5645->supplies); @@ -658,57 +671,19 @@ static int ov5645_set_power_on(struct ov5645 *ov5645) msleep(20); - return 0; -} - -static void ov5645_set_power_off(struct ov5645 *ov5645) -{ - gpiod_set_value_cansleep(ov5645->rst_gpio, 1); - gpiod_set_value_cansleep(ov5645->enable_gpio, 0); - clk_disable_unprepare(ov5645->xclk); - regulator_bulk_disable(OV5645_NUM_SUPPLIES, ov5645->supplies); -} - -static int ov5645_s_power(struct v4l2_subdev *sd, int on) -{ - struct ov5645 *ov5645 = to_ov5645(sd); - int ret = 0; - - mutex_lock(&ov5645->power_lock); - - /* If the power count is modified from 0 to != 0 or from != 0 to 0, - * update the power state. - */ - if (ov5645->power_count == !on) { - if (on) { - ret = ov5645_set_power_on(ov5645); - if (ret < 0) - goto exit; - - ret = ov5645_set_register_array(ov5645, - ov5645_global_init_setting, + ret = ov5645_set_register_array(ov5645, ov5645_global_init_setting, ARRAY_SIZE(ov5645_global_init_setting)); - if (ret < 0) { - dev_err(ov5645->dev, - "could not set init registers\n"); - ov5645_set_power_off(ov5645); - goto exit; - } - - usleep_range(500, 1000); - } else { - ov5645_write_reg(ov5645, OV5645_IO_MIPI_CTRL00, 0x58); - ov5645_set_power_off(ov5645); - } + if (ret < 0) { + dev_err(ov5645->dev, "could not set init registers\n"); + goto exit; } - /* Update the power count. */ - ov5645->power_count += on ? 1 : -1; - WARN_ON(ov5645->power_count < 0); + usleep_range(500, 1000); -exit: - mutex_unlock(&ov5645->power_lock); + return 0; +exit: + ov5645_set_power_off(dev); return ret; } @@ -795,7 +770,7 @@ static int ov5645_s_ctrl(struct v4l2_ctrl *ctrl) int ret; mutex_lock(&ov5645->power_lock); - if (!ov5645->power_count) { + if (!pm_runtime_get_if_in_use(ov5645->dev)) { mutex_unlock(&ov5645->power_lock); return 0; } @@ -827,6 +802,8 @@ static int ov5645_s_ctrl(struct v4l2_ctrl *ctrl) break; } + pm_runtime_mark_last_busy(ov5645->dev); + pm_runtime_put_autosuspend(ov5645->dev); mutex_unlock(&ov5645->power_lock); return ret; @@ -991,6 +968,10 @@ static int ov5645_s_stream(struct v4l2_subdev *subdev, int enable) int ret; if (enable) { + ret = pm_runtime_resume_and_get(ov5645->dev); + if (ret < 0) + return ret; + ret = ov5645_set_register_array(ov5645, ov5645->current_mode->data, ov5645->current_mode->data_size); @@ -998,39 +979,44 @@ static int ov5645_s_stream(struct v4l2_subdev *subdev, int enable) dev_err(ov5645->dev, "could not set mode %dx%d\n", ov5645->current_mode->width, ov5645->current_mode->height); - return ret; + goto err_rpm_put; } ret = v4l2_ctrl_handler_setup(&ov5645->ctrls); if (ret < 0) { dev_err(ov5645->dev, "could not sync v4l2 controls\n"); - return ret; + goto err_rpm_put; } ret = ov5645_write_reg(ov5645, OV5645_IO_MIPI_CTRL00, 0x45); if (ret < 0) - return ret; + goto err_rpm_put; ret = ov5645_write_reg(ov5645, OV5645_SYSTEM_CTRL0, OV5645_SYSTEM_CTRL0_START); if (ret < 0) - return ret; + goto err_rpm_put; } else { ret = ov5645_write_reg(ov5645, OV5645_IO_MIPI_CTRL00, 0x40); if (ret < 0) - return ret; + goto stream_off_rpm_put; ret = ov5645_write_reg(ov5645, OV5645_SYSTEM_CTRL0, OV5645_SYSTEM_CTRL0_STOP); - if (ret < 0) - return ret; + + goto stream_off_rpm_put; } return 0; -} -static const struct v4l2_subdev_core_ops ov5645_core_ops = { - .s_power = ov5645_s_power, -}; +err_rpm_put: + pm_runtime_put_sync(ov5645->dev); + return ret; + +stream_off_rpm_put: + pm_runtime_mark_last_busy(ov5645->dev); + pm_runtime_put_autosuspend(ov5645->dev); + return ret; +} static const struct v4l2_subdev_video_ops ov5645_video_ops = { .s_stream = ov5645_s_stream, @@ -1046,7 +1032,6 @@ static const struct v4l2_subdev_pad_ops ov5645_subdev_pad_ops = { }; static const struct v4l2_subdev_ops ov5645_subdev_ops = { - .core = &ov5645_core_ops, .video = &ov5645_video_ops, .pad = &ov5645_subdev_pad_ops, }; @@ -1188,11 +1173,9 @@ static int ov5645_probe(struct i2c_client *client) goto free_ctrl; } - ret = ov5645_s_power(&ov5645->sd, true); - if (ret < 0) { - dev_err(dev, "could not power up OV5645\n"); + ret = ov5645_set_power_on(dev); + if (ret) goto free_entity; - } ret = ov5645_read_reg(ov5645, OV5645_CHIP_ID_HIGH, &chip_id_high); if (ret < 0 || chip_id_high != OV5645_CHIP_ID_HIGH_BYTE) { @@ -1233,20 +1216,30 @@ static int ov5645_probe(struct i2c_client *client) goto power_down; } - ov5645_s_power(&ov5645->sd, false); + pm_runtime_set_active(dev); + pm_runtime_get_noresume(dev); + pm_runtime_enable(dev); + + ov5645_entity_init_cfg(&ov5645->sd, NULL); ret = v4l2_async_register_subdev(&ov5645->sd); if (ret < 0) { dev_err(dev, "could not register v4l2 device\n"); - goto free_entity; + goto err_pm_runtime; } - ov5645_entity_init_cfg(&ov5645->sd, NULL); + pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); return 0; +err_pm_runtime: + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); power_down: - ov5645_s_power(&ov5645->sd, false); + ov5645_set_power_off(dev); free_entity: media_entity_cleanup(&ov5645->sd.entity); free_ctrl: @@ -1264,6 +1257,10 @@ static void ov5645_remove(struct i2c_client *client) v4l2_async_unregister_subdev(&ov5645->sd); media_entity_cleanup(&ov5645->sd.entity); v4l2_ctrl_handler_free(&ov5645->ctrls); + pm_runtime_disable(ov5645->dev); + if (!pm_runtime_status_suspended(ov5645->dev)) + ov5645_set_power_off(ov5645->dev); + pm_runtime_set_suspended(ov5645->dev); mutex_destroy(&ov5645->power_lock); } @@ -1279,10 +1276,15 @@ static const struct of_device_id ov5645_of_match[] = { }; MODULE_DEVICE_TABLE(of, ov5645_of_match); +static const struct dev_pm_ops ov5645_pm_ops = { + SET_RUNTIME_PM_OPS(ov5645_set_power_off, ov5645_set_power_on, NULL) +}; + static struct i2c_driver ov5645_i2c_driver = { .driver = { .of_match_table = ov5645_of_match, .name = "ov5645", + .pm = &ov5645_pm_ops, }, .probe_new = ov5645_probe, .remove = ov5645_remove, diff --git a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c index 18f041e985b7..4c0ea2ae671b 100644 --- a/drivers/media/i2c/ov6650.c +++ b/drivers/media/i2c/ov6650.c @@ -1025,8 +1025,7 @@ static const struct v4l2_subdev_internal_ops ov6650_internal_ops = { /* * i2c_driver function */ -static int ov6650_probe(struct i2c_client *client, - const struct i2c_device_id *did) +static int ov6650_probe(struct i2c_client *client) { struct ov6650 *priv; int ret; @@ -1114,7 +1113,7 @@ static struct i2c_driver ov6650_i2c_driver = { .driver = { .name = "ov6650", }, - .probe = ov6650_probe, + .probe_new = ov6650_probe, .remove = ov6650_remove, .id_table = ov6650_id, }; diff --git a/drivers/media/i2c/ov7640.c b/drivers/media/i2c/ov7640.c index 5e2d67f0f9f2..e6751d5cc64b 100644 --- a/drivers/media/i2c/ov7640.c +++ b/drivers/media/i2c/ov7640.c @@ -42,8 +42,7 @@ static int write_regs(struct i2c_client *client, static const struct v4l2_subdev_ops ov7640_ops; -static int ov7640_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ov7640_probe(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; struct v4l2_subdev *sd; @@ -87,7 +86,7 @@ static struct i2c_driver ov7640_driver = { .driver = { .name = "ov7640", }, - .probe = ov7640_probe, + .probe_new = ov7640_probe, .remove = ov7640_remove, .id_table = ov7640_id, }; diff --git a/drivers/media/i2c/ov9282.c b/drivers/media/i2c/ov9282.c index df144a2f6eda..f2ec92deb5ec 100644 --- a/drivers/media/i2c/ov9282.c +++ b/drivers/media/i2c/ov9282.c @@ -13,6 +13,7 @@ #include <linux/pm_runtime.h> #include <media/v4l2-ctrls.h> +#include <media/v4l2-event.h> #include <media/v4l2-fwnode.h> #include <media/v4l2-subdev.h> @@ -21,6 +22,13 @@ #define OV9282_MODE_STANDBY 0x00 #define OV9282_MODE_STREAMING 0x01 +#define OV9282_REG_PLL_CTRL_0D 0x030d +#define OV9282_PLL_CTRL_0D_RAW8 0x60 +#define OV9282_PLL_CTRL_0D_RAW10 0x50 + +#define OV9282_REG_TIMING_HTS 0x380c +#define OV9282_TIMING_HTS_MAX 0x7fff + /* Lines per frame */ #define OV9282_REG_LPFR 0x380e @@ -45,6 +53,17 @@ /* Group hold register */ #define OV9282_REG_HOLD 0x3308 +#define OV9282_REG_ANA_CORE_2 0x3662 +#define OV9282_ANA_CORE2_RAW8 0x07 +#define OV9282_ANA_CORE2_RAW10 0x05 + +#define OV9282_REG_TIMING_FORMAT_1 0x3820 +#define OV9282_REG_TIMING_FORMAT_2 0x3821 +#define OV9282_FLIP_BIT BIT(2) + +#define OV9282_REG_MIPI_CTRL00 0x4800 +#define OV9282_GATED_CLOCK BIT(5) + /* Input clock rate */ #define OV9282_INCLK_RATE 24000000 @@ -52,6 +71,23 @@ #define OV9282_LINK_FREQ 400000000 #define OV9282_NUM_DATA_LANES 2 +/* Pixel rate */ +#define OV9282_PIXEL_RATE_10BIT (OV9282_LINK_FREQ * 2 * \ + OV9282_NUM_DATA_LANES / 10) +#define OV9282_PIXEL_RATE_8BIT (OV9282_LINK_FREQ * 2 * \ + OV9282_NUM_DATA_LANES / 8) + +/* + * OV9282 native and active pixel array size. + * 8 dummy rows/columns on each edge of a 1280x800 active array + */ +#define OV9282_NATIVE_WIDTH 1296U +#define OV9282_NATIVE_HEIGHT 816U +#define OV9282_PIXEL_ARRAY_LEFT 8U +#define OV9282_PIXEL_ARRAY_TOP 8U +#define OV9282_PIXEL_ARRAY_WIDTH 1280U +#define OV9282_PIXEL_ARRAY_HEIGHT 800U + #define OV9282_REG_MIN 0x00 #define OV9282_REG_MAX 0xfffff @@ -79,25 +115,23 @@ struct ov9282_reg_list { * struct ov9282_mode - ov9282 sensor mode structure * @width: Frame width * @height: Frame height - * @code: Format code - * @hblank: Horizontal blanking in lines + * @hblank_min: Minimum horizontal blanking in lines for non-continuous[0] and + * continuous[1] clock modes * @vblank: Vertical blanking in lines * @vblank_min: Minimum vertical blanking in lines * @vblank_max: Maximum vertical blanking in lines - * @pclk: Sensor pixel clock * @link_freq_idx: Link frequency index * @reg_list: Register list for sensor mode */ struct ov9282_mode { u32 width; u32 height; - u32 code; - u32 hblank; + u32 hblank_min[2]; u32 vblank; u32 vblank_min; u32 vblank_max; - u64 pclk; u32 link_freq_idx; + struct v4l2_rect crop; struct ov9282_reg_list reg_list; }; @@ -111,13 +145,13 @@ struct ov9282_mode { * @inclk: Sensor input clock * @ctrl_handler: V4L2 control handler * @link_freq_ctrl: Pointer to link frequency control - * @pclk_ctrl: Pointer to pixel clock control * @hblank_ctrl: Pointer to horizontal blanking control * @vblank_ctrl: Pointer to vertical blanking control * @exp_ctrl: Pointer to exposure control * @again_ctrl: Pointer to analog gain control * @vblank: Vertical blanking in lines * @cur_mode: Pointer to current selected sensor mode + * @code: Mbus code currently selected * @mutex: Mutex for serializing sensor controls * @streaming: Flag indicating streaming state */ @@ -130,15 +164,17 @@ struct ov9282 { struct clk *inclk; struct v4l2_ctrl_handler ctrl_handler; struct v4l2_ctrl *link_freq_ctrl; - struct v4l2_ctrl *pclk_ctrl; struct v4l2_ctrl *hblank_ctrl; struct v4l2_ctrl *vblank_ctrl; struct { struct v4l2_ctrl *exp_ctrl; struct v4l2_ctrl *again_ctrl; }; + struct v4l2_ctrl *pixel_rate; u32 vblank; + bool noncontinuous_clock; const struct ov9282_mode *cur_mode; + u32 code; struct mutex mutex; bool streaming; }; @@ -147,10 +183,15 @@ static const s64 link_freq[] = { OV9282_LINK_FREQ, }; -/* Sensor mode registers */ -static const struct ov9282_reg mode_1280x720_regs[] = { +/* + * Common registers + * + * Note: Do NOT include a software reset (0x0103, 0x01) in any of these + * register arrays as some settings are written as part of ov9282_power_on, + * and the reset will clear them. + */ +static const struct ov9282_reg common_regs[] = { {0x0302, 0x32}, - {0x030d, 0x50}, {0x030e, 0x02}, {0x3001, 0x00}, {0x3004, 0x00}, @@ -163,14 +204,10 @@ static const struct ov9282_reg mode_1280x720_regs[] = { {0x3030, 0x10}, {0x3039, 0x32}, {0x303a, 0x00}, - {0x3500, 0x00}, - {0x3501, 0x5f}, - {0x3502, 0x1e}, {0x3503, 0x08}, {0x3505, 0x8c}, {0x3507, 0x03}, {0x3508, 0x00}, - {0x3509, 0x10}, {0x3610, 0x80}, {0x3611, 0xa0}, {0x3620, 0x6e}, @@ -183,13 +220,85 @@ static const struct ov9282_reg mode_1280x720_regs[] = { {0x372d, 0x22}, {0x3731, 0x80}, {0x3732, 0x30}, - {0x3778, 0x00}, {0x377d, 0x22}, {0x3788, 0x02}, {0x3789, 0xa4}, {0x378a, 0x00}, {0x378b, 0x4a}, {0x3799, 0x20}, + {0x3881, 0x42}, + {0x38a8, 0x02}, + {0x38a9, 0x80}, + {0x38b1, 0x00}, + {0x38c4, 0x00}, + {0x38c5, 0xc0}, + {0x38c6, 0x04}, + {0x38c7, 0x80}, + {0x3920, 0xff}, + {0x4010, 0x40}, + {0x4043, 0x40}, + {0x4307, 0x30}, + {0x4317, 0x00}, + {0x4501, 0x00}, + {0x450a, 0x08}, + {0x4601, 0x04}, + {0x470f, 0x00}, + {0x4f07, 0x00}, + {0x5000, 0x9f}, + {0x5001, 0x00}, + {0x5e00, 0x00}, + {0x5d00, 0x07}, + {0x5d01, 0x00}, + {0x0101, 0x01}, + {0x1000, 0x03}, + {0x5a08, 0x84}, +}; + +struct ov9282_reg_list common_regs_list = { + .num_of_regs = ARRAY_SIZE(common_regs), + .regs = common_regs, +}; + +#define MODE_1280_800 0 +#define MODE_1280_720 1 +#define MODE_640_400 2 + +#define DEFAULT_MODE MODE_1280_720 + +/* Sensor mode registers */ +static const struct ov9282_reg mode_1280x800_regs[] = { + {0x3778, 0x00}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x05}, + {0x3805, 0x0f}, + {0x3806, 0x03}, + {0x3807, 0x2f}, + {0x3808, 0x05}, + {0x3809, 0x00}, + {0x380a, 0x03}, + {0x380b, 0x20}, + {0x3810, 0x00}, + {0x3811, 0x08}, + {0x3812, 0x00}, + {0x3813, 0x08}, + {0x3814, 0x11}, + {0x3815, 0x11}, + {0x3820, 0x40}, + {0x3821, 0x00}, + {0x4003, 0x40}, + {0x4008, 0x04}, + {0x4009, 0x0b}, + {0x400c, 0x00}, + {0x400d, 0x07}, + {0x4507, 0x00}, + {0x4509, 0x00}, +}; + +static const struct ov9282_reg mode_1280x720_regs[] = { + {0x3778, 0x00}, {0x3800, 0x00}, {0x3801, 0x00}, {0x3802, 0x00}, @@ -202,10 +311,6 @@ static const struct ov9282_reg mode_1280x720_regs[] = { {0x3809, 0x00}, {0x380a, 0x02}, {0x380b, 0xd0}, - {0x380c, 0x05}, - {0x380d, 0xfa}, - {0x380e, 0x06}, - {0x380f, 0xce}, {0x3810, 0x00}, {0x3811, 0x08}, {0x3812, 0x00}, @@ -214,56 +319,107 @@ static const struct ov9282_reg mode_1280x720_regs[] = { {0x3815, 0x11}, {0x3820, 0x3c}, {0x3821, 0x84}, - {0x3881, 0x42}, - {0x38a8, 0x02}, - {0x38a9, 0x80}, - {0x38b1, 0x00}, - {0x38c4, 0x00}, - {0x38c5, 0xc0}, - {0x38c6, 0x04}, - {0x38c7, 0x80}, - {0x3920, 0xff}, {0x4003, 0x40}, {0x4008, 0x02}, {0x4009, 0x05}, {0x400c, 0x00}, {0x400d, 0x03}, - {0x4010, 0x40}, - {0x4043, 0x40}, - {0x4307, 0x30}, - {0x4317, 0x00}, - {0x4501, 0x00}, {0x4507, 0x00}, {0x4509, 0x80}, - {0x450a, 0x08}, - {0x4601, 0x04}, - {0x470f, 0x00}, - {0x4f07, 0x00}, - {0x4800, 0x20}, - {0x5000, 0x9f}, - {0x5001, 0x00}, - {0x5e00, 0x00}, - {0x5d00, 0x07}, - {0x5d01, 0x00}, - {0x0101, 0x01}, - {0x1000, 0x03}, - {0x5a08, 0x84}, +}; + +static const struct ov9282_reg mode_640x400_regs[] = { + {0x3778, 0x10}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x05}, + {0x3805, 0x0f}, + {0x3806, 0x03}, + {0x3807, 0x2f}, + {0x3808, 0x02}, + {0x3809, 0x80}, + {0x380a, 0x01}, + {0x380b, 0x90}, + {0x3810, 0x00}, + {0x3811, 0x04}, + {0x3812, 0x00}, + {0x3813, 0x04}, + {0x3814, 0x31}, + {0x3815, 0x22}, + {0x3820, 0x60}, + {0x3821, 0x01}, + {0x4008, 0x02}, + {0x4009, 0x05}, + {0x400c, 0x00}, + {0x400d, 0x03}, + {0x4507, 0x03}, + {0x4509, 0x80}, }; /* Supported sensor mode configurations */ -static const struct ov9282_mode supported_mode = { - .width = 1280, - .height = 720, - .hblank = 250, - .vblank = 1022, - .vblank_min = 151, - .vblank_max = 51540, - .pclk = 160000000, - .link_freq_idx = 0, - .code = MEDIA_BUS_FMT_Y10_1X10, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_1280x720_regs), - .regs = mode_1280x720_regs, +static const struct ov9282_mode supported_modes[] = { + [MODE_1280_800] = { + .width = 1280, + .height = 800, + .hblank_min = { 250, 176 }, + .vblank = 1022, + .vblank_min = 110, + .vblank_max = 51540, + .link_freq_idx = 0, + .crop = { + .left = OV9282_PIXEL_ARRAY_LEFT, + .top = OV9282_PIXEL_ARRAY_TOP, + .width = 1280, + .height = 800 + }, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1280x800_regs), + .regs = mode_1280x800_regs, + }, + }, + [MODE_1280_720] = { + .width = 1280, + .height = 720, + .hblank_min = { 250, 176 }, + .vblank = 1022, + .vblank_min = 41, + .vblank_max = 51540, + .link_freq_idx = 0, + .crop = { + /* + * Note that this mode takes the top 720 lines from the + * 800 of the sensor. It does not take a middle crop. + */ + .left = OV9282_PIXEL_ARRAY_LEFT, + .top = OV9282_PIXEL_ARRAY_TOP, + .width = 1280, + .height = 720 + }, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1280x720_regs), + .regs = mode_1280x720_regs, + }, + }, + [MODE_640_400] = { + .width = 640, + .height = 400, + .hblank_min = { 890, 816 }, + .vblank = 1022, + .vblank_min = 22, + .vblank_max = 51540, + .link_freq_idx = 0, + .crop = { + .left = OV9282_PIXEL_ARRAY_LEFT, + .top = OV9282_PIXEL_ARRAY_TOP, + .width = 1280, + .height = 800 + }, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_640x400_regs), + .regs = mode_640x400_regs, + }, }, }; @@ -373,19 +529,33 @@ static int ov9282_write_regs(struct ov9282 *ov9282, * ov9282_update_controls() - Update control ranges based on streaming mode * @ov9282: pointer to ov9282 device * @mode: pointer to ov9282_mode sensor mode + * @fmt: pointer to the requested mode * * Return: 0 if successful, error code otherwise. */ static int ov9282_update_controls(struct ov9282 *ov9282, - const struct ov9282_mode *mode) + const struct ov9282_mode *mode, + const struct v4l2_subdev_format *fmt) { + u32 hblank_min; + s64 pixel_rate; int ret; ret = __v4l2_ctrl_s_ctrl(ov9282->link_freq_ctrl, mode->link_freq_idx); if (ret) return ret; - ret = __v4l2_ctrl_s_ctrl(ov9282->hblank_ctrl, mode->hblank); + pixel_rate = (fmt->format.code == MEDIA_BUS_FMT_Y10_1X10) ? + OV9282_PIXEL_RATE_10BIT : OV9282_PIXEL_RATE_8BIT; + ret = __v4l2_ctrl_modify_range(ov9282->pixel_rate, pixel_rate, + pixel_rate, 1, pixel_rate); + if (ret) + return ret; + + hblank_min = mode->hblank_min[ov9282->noncontinuous_clock ? 0 : 1]; + ret = __v4l2_ctrl_modify_range(ov9282->hblank_ctrl, hblank_min, + OV9282_TIMING_HTS_MAX - mode->width, 1, + hblank_min); if (ret) return ret; @@ -403,22 +573,15 @@ static int ov9282_update_controls(struct ov9282 *ov9282, */ static int ov9282_update_exp_gain(struct ov9282 *ov9282, u32 exposure, u32 gain) { - u32 lpfr; int ret; - lpfr = ov9282->vblank + ov9282->cur_mode->height; - - dev_dbg(ov9282->dev, "Set exp %u, analog gain %u, lpfr %u", - exposure, gain, lpfr); + dev_dbg(ov9282->dev, "Set exp %u, analog gain %u", + exposure, gain); ret = ov9282_write_reg(ov9282, OV9282_REG_HOLD, 1, 1); if (ret) return ret; - ret = ov9282_write_reg(ov9282, OV9282_REG_LPFR, 2, lpfr); - if (ret) - goto error_release_group_hold; - ret = ov9282_write_reg(ov9282, OV9282_REG_EXPOSURE, 3, exposure << 4); if (ret) goto error_release_group_hold; @@ -431,6 +594,40 @@ error_release_group_hold: return ret; } +static int ov9282_set_ctrl_hflip(struct ov9282 *ov9282, int value) +{ + u32 current_val; + int ret = ov9282_read_reg(ov9282, OV9282_REG_TIMING_FORMAT_2, 1, + ¤t_val); + if (ret) + return ret; + + if (value) + current_val |= OV9282_FLIP_BIT; + else + current_val &= ~OV9282_FLIP_BIT; + + return ov9282_write_reg(ov9282, OV9282_REG_TIMING_FORMAT_2, 1, + current_val); +} + +static int ov9282_set_ctrl_vflip(struct ov9282 *ov9282, int value) +{ + u32 current_val; + int ret = ov9282_read_reg(ov9282, OV9282_REG_TIMING_FORMAT_1, 1, + ¤t_val); + if (ret) + return ret; + + if (value) + current_val |= OV9282_FLIP_BIT; + else + current_val &= ~OV9282_FLIP_BIT; + + return ov9282_write_reg(ov9282, OV9282_REG_TIMING_FORMAT_1, 1, + current_val); +} + /** * ov9282_set_ctrl() - Set subdevice control * @ctrl: pointer to v4l2_ctrl structure @@ -449,6 +646,7 @@ static int ov9282_set_ctrl(struct v4l2_ctrl *ctrl) container_of(ctrl->handler, struct ov9282, ctrl_handler); u32 analog_gain; u32 exposure; + u32 lpfr; int ret; switch (ctrl->id) { @@ -466,11 +664,14 @@ static int ov9282_set_ctrl(struct v4l2_ctrl *ctrl) OV9282_EXPOSURE_OFFSET, 1, OV9282_EXPOSURE_DEFAULT); break; - case V4L2_CID_EXPOSURE: - /* Set controls only if sensor is in power on state */ - if (!pm_runtime_get_if_in_use(ov9282->dev)) - return 0; + } + + /* Set controls only if sensor is in power on state */ + if (!pm_runtime_get_if_in_use(ov9282->dev)) + return 0; + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: exposure = ctrl->val; analog_gain = ov9282->again_ctrl->val; @@ -478,15 +679,28 @@ static int ov9282_set_ctrl(struct v4l2_ctrl *ctrl) exposure, analog_gain); ret = ov9282_update_exp_gain(ov9282, exposure, analog_gain); - - pm_runtime_put(ov9282->dev); - + break; + case V4L2_CID_VBLANK: + lpfr = ov9282->vblank + ov9282->cur_mode->height; + ret = ov9282_write_reg(ov9282, OV9282_REG_LPFR, 2, lpfr); + break; + case V4L2_CID_HFLIP: + ret = ov9282_set_ctrl_hflip(ov9282, ctrl->val); + break; + case V4L2_CID_VFLIP: + ret = ov9282_set_ctrl_vflip(ov9282, ctrl->val); + break; + case V4L2_CID_HBLANK: + ret = ov9282_write_reg(ov9282, OV9282_REG_TIMING_HTS, 2, + (ctrl->val + ov9282->cur_mode->width) >> 1); break; default: dev_err(ov9282->dev, "Invalid control %d", ctrl->id); ret = -EINVAL; } + pm_runtime_put(ov9282->dev); + return ret; } @@ -507,10 +721,16 @@ static int ov9282_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { - if (code->index > 0) + switch (code->index) { + case 0: + code->code = MEDIA_BUS_FMT_Y10_1X10; + break; + case 1: + code->code = MEDIA_BUS_FMT_Y8_1X8; + break; + default: return -EINVAL; - - code->code = supported_mode.code; + } return 0; } @@ -527,15 +747,16 @@ static int ov9282_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fsize) { - if (fsize->index > 0) + if (fsize->index >= ARRAY_SIZE(supported_modes)) return -EINVAL; - if (fsize->code != supported_mode.code) + if (fsize->code != MEDIA_BUS_FMT_Y10_1X10 && + fsize->code != MEDIA_BUS_FMT_Y8_1X8) return -EINVAL; - fsize->min_width = supported_mode.width; + fsize->min_width = supported_modes[fsize->index].width; fsize->max_width = fsize->min_width; - fsize->min_height = supported_mode.height; + fsize->min_height = supported_modes[fsize->index].height; fsize->max_height = fsize->min_height; return 0; @@ -546,15 +767,17 @@ static int ov9282_enum_frame_size(struct v4l2_subdev *sd, * from selected sensor mode * @ov9282: pointer to ov9282 device * @mode: pointer to ov9282_mode sensor mode + * @code: mbus code to be stored * @fmt: V4L2 sub-device format need to be filled */ static void ov9282_fill_pad_format(struct ov9282 *ov9282, const struct ov9282_mode *mode, + u32 code, struct v4l2_subdev_format *fmt) { fmt->format.width = mode->width; fmt->format.height = mode->height; - fmt->format.code = mode->code; + fmt->format.code = code; fmt->format.field = V4L2_FIELD_NONE; fmt->format.colorspace = V4L2_COLORSPACE_RAW; fmt->format.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; @@ -584,7 +807,8 @@ static int ov9282_get_pad_format(struct v4l2_subdev *sd, framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); fmt->format = *framefmt; } else { - ov9282_fill_pad_format(ov9282, ov9282->cur_mode, fmt); + ov9282_fill_pad_format(ov9282, ov9282->cur_mode, ov9282->code, + fmt); } mutex_unlock(&ov9282->mutex); @@ -606,12 +830,22 @@ static int ov9282_set_pad_format(struct v4l2_subdev *sd, { struct ov9282 *ov9282 = to_ov9282(sd); const struct ov9282_mode *mode; + u32 code; int ret = 0; mutex_lock(&ov9282->mutex); - mode = &supported_mode; - ov9282_fill_pad_format(ov9282, mode, fmt); + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), + width, height, + fmt->format.width, + fmt->format.height); + if (fmt->format.code == MEDIA_BUS_FMT_Y8_1X8) + code = MEDIA_BUS_FMT_Y8_1X8; + else + code = MEDIA_BUS_FMT_Y10_1X10; + + ov9282_fill_pad_format(ov9282, mode, code, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *framefmt; @@ -619,9 +853,11 @@ static int ov9282_set_pad_format(struct v4l2_subdev *sd, framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); *framefmt = fmt->format; } else { - ret = ov9282_update_controls(ov9282, mode); - if (!ret) + ret = ov9282_update_controls(ov9282, mode, fmt); + if (!ret) { ov9282->cur_mode = mode; + ov9282->code = code; + } } mutex_unlock(&ov9282->mutex); @@ -643,11 +879,64 @@ static int ov9282_init_pad_cfg(struct v4l2_subdev *sd, struct v4l2_subdev_format fmt = { 0 }; fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; - ov9282_fill_pad_format(ov9282, &supported_mode, &fmt); + ov9282_fill_pad_format(ov9282, &supported_modes[DEFAULT_MODE], + ov9282->code, &fmt); return ov9282_set_pad_format(sd, sd_state, &fmt); } +static const struct v4l2_rect * +__ov9282_get_pad_crop(struct ov9282 *ov9282, + struct v4l2_subdev_state *sd_state, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_crop(&ov9282->sd, sd_state, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &ov9282->cur_mode->crop; + } + + return NULL; +} + +static int ov9282_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: { + struct ov9282 *ov9282 = to_ov9282(sd); + + mutex_lock(&ov9282->mutex); + sel->r = *__ov9282_get_pad_crop(ov9282, sd_state, sel->pad, + sel->which); + mutex_unlock(&ov9282->mutex); + + return 0; + } + + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = OV9282_NATIVE_WIDTH; + sel->r.height = OV9282_NATIVE_HEIGHT; + + return 0; + + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = OV9282_PIXEL_ARRAY_TOP; + sel->r.left = OV9282_PIXEL_ARRAY_LEFT; + sel->r.width = OV9282_PIXEL_ARRAY_WIDTH; + sel->r.height = OV9282_PIXEL_ARRAY_HEIGHT; + + return 0; + } + + return -EINVAL; +} + /** * ov9282_start_streaming() - Start sensor stream * @ov9282: pointer to ov9282 device @@ -656,9 +945,34 @@ static int ov9282_init_pad_cfg(struct v4l2_subdev *sd, */ static int ov9282_start_streaming(struct ov9282 *ov9282) { + const struct ov9282_reg bitdepth_regs[2][2] = { + { + {OV9282_REG_PLL_CTRL_0D, OV9282_PLL_CTRL_0D_RAW10}, + {OV9282_REG_ANA_CORE_2, OV9282_ANA_CORE2_RAW10}, + }, { + {OV9282_REG_PLL_CTRL_0D, OV9282_PLL_CTRL_0D_RAW8}, + {OV9282_REG_ANA_CORE_2, OV9282_ANA_CORE2_RAW8}, + } + }; const struct ov9282_reg_list *reg_list; + int bitdepth_index; int ret; + /* Write common registers */ + ret = ov9282_write_regs(ov9282, common_regs_list.regs, + common_regs_list.num_of_regs); + if (ret) { + dev_err(ov9282->dev, "fail to write common registers"); + return ret; + } + + bitdepth_index = ov9282->code == MEDIA_BUS_FMT_Y10_1X10 ? 0 : 1; + ret = ov9282_write_regs(ov9282, bitdepth_regs[bitdepth_index], 2); + if (ret) { + dev_err(ov9282->dev, "fail to write bitdepth regs"); + return ret; + } + /* Write sensor mode registers */ reg_list = &ov9282->cur_mode->reg_list; ret = ov9282_write_regs(ov9282, reg_list->regs, reg_list->num_of_regs); @@ -818,6 +1132,9 @@ static int ov9282_parse_hw_config(struct ov9282 *ov9282) if (ret) return ret; + ov9282->noncontinuous_clock = + bus_cfg.bus.mipi_csi2.flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK; + if (bus_cfg.bus.mipi_csi2.num_data_lanes != OV9282_NUM_DATA_LANES) { dev_err(ov9282->dev, "number of CSI2 data lanes %d is not supported", @@ -845,6 +1162,11 @@ done_endpoint_free: } /* V4l2 subdevice ops */ +static const struct v4l2_subdev_core_ops ov9282_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + static const struct v4l2_subdev_video_ops ov9282_video_ops = { .s_stream = ov9282_set_stream, }; @@ -855,9 +1177,11 @@ static const struct v4l2_subdev_pad_ops ov9282_pad_ops = { .enum_frame_size = ov9282_enum_frame_size, .get_fmt = ov9282_get_pad_format, .set_fmt = ov9282_set_pad_format, + .get_selection = ov9282_get_selection, }; static const struct v4l2_subdev_ops ov9282_subdev_ops = { + .core = &ov9282_core_ops, .video = &ov9282_video_ops, .pad = &ov9282_pad_ops, }; @@ -886,6 +1210,14 @@ static int ov9282_power_on(struct device *dev) usleep_range(400, 600); + ret = ov9282_write_reg(ov9282, OV9282_REG_MIPI_CTRL00, 1, + ov9282->noncontinuous_clock ? + OV9282_GATED_CLOCK : 0); + if (ret) { + dev_err(ov9282->dev, "fail to write MIPI_CTRL00"); + return ret; + } + return 0; error_reset: @@ -922,10 +1254,12 @@ static int ov9282_init_controls(struct ov9282 *ov9282) { struct v4l2_ctrl_handler *ctrl_hdlr = &ov9282->ctrl_handler; const struct ov9282_mode *mode = ov9282->cur_mode; + struct v4l2_fwnode_device_properties props; + u32 hblank_min; u32 lpfr; int ret; - ret = v4l2_ctrl_handler_init(ctrl_hdlr, 6); + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10); if (ret) return ret; @@ -959,12 +1293,18 @@ static int ov9282_init_controls(struct ov9282 *ov9282) mode->vblank_max, 1, mode->vblank); + v4l2_ctrl_new_std(ctrl_hdlr, &ov9282_ctrl_ops, V4L2_CID_VFLIP, + 0, 1, 1, 1); + + v4l2_ctrl_new_std(ctrl_hdlr, &ov9282_ctrl_ops, V4L2_CID_HFLIP, + 0, 1, 1, 1); + /* Read only controls */ - ov9282->pclk_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, - &ov9282_ctrl_ops, - V4L2_CID_PIXEL_RATE, - mode->pclk, mode->pclk, - 1, mode->pclk); + ov9282->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov9282_ctrl_ops, + V4L2_CID_PIXEL_RATE, + OV9282_PIXEL_RATE_10BIT, + OV9282_PIXEL_RATE_10BIT, 1, + OV9282_PIXEL_RATE_10BIT); ov9282->link_freq_ctrl = v4l2_ctrl_new_int_menu(ctrl_hdlr, &ov9282_ctrl_ops, @@ -976,16 +1316,22 @@ static int ov9282_init_controls(struct ov9282 *ov9282) if (ov9282->link_freq_ctrl) ov9282->link_freq_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + hblank_min = mode->hblank_min[ov9282->noncontinuous_clock ? 0 : 1]; ov9282->hblank_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &ov9282_ctrl_ops, V4L2_CID_HBLANK, - OV9282_REG_MIN, - OV9282_REG_MAX, - 1, mode->hblank); - if (ov9282->hblank_ctrl) - ov9282->hblank_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + hblank_min, + OV9282_TIMING_HTS_MAX - mode->width, + 1, hblank_min); + + ret = v4l2_fwnode_device_parse(ov9282->dev, &props); + if (!ret) { + /* Failure sets ctrl_hdlr->error, which we check afterwards anyway */ + v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &ov9282_ctrl_ops, + &props); + } - if (ctrl_hdlr->error) { + if (ctrl_hdlr->error || ret) { dev_err(ov9282->dev, "control init failed: %d", ctrl_hdlr->error); v4l2_ctrl_handler_free(ctrl_hdlr); @@ -1038,8 +1384,9 @@ static int ov9282_probe(struct i2c_client *client) goto error_power_off; } - /* Set default mode to max resolution */ - ov9282->cur_mode = &supported_mode; + /* Set default mode to first mode */ + ov9282->cur_mode = &supported_modes[DEFAULT_MODE]; + ov9282->code = MEDIA_BUS_FMT_Y10_1X10; ov9282->vblank = ov9282->cur_mode->vblank; ret = ov9282_init_controls(ov9282); @@ -1049,7 +1396,8 @@ static int ov9282_probe(struct i2c_client *client) } /* Initialize subdev */ - ov9282->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + ov9282->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; ov9282->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; /* Initialize source pad */ diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c index 8b80be33c5f4..a80fa59bf2ae 100644 --- a/drivers/media/i2c/ov9640.c +++ b/drivers/media/i2c/ov9640.c @@ -682,8 +682,7 @@ static const struct v4l2_subdev_ops ov9640_subdev_ops = { /* * i2c_driver function */ -static int ov9640_probe(struct i2c_client *client, - const struct i2c_device_id *did) +static int ov9640_probe(struct i2c_client *client) { struct ov9640_priv *priv; int ret; @@ -763,7 +762,7 @@ static struct i2c_driver ov9640_i2c_driver = { .driver = { .name = "ov9640", }, - .probe = ov9640_probe, + .probe_new = ov9640_probe, .remove = ov9640_remove, .id_table = ov9640_id, }; diff --git a/drivers/media/i2c/rj54n1cb0c.c b/drivers/media/i2c/rj54n1cb0c.c index 1c3502f34cd3..9db5473daba0 100644 --- a/drivers/media/i2c/rj54n1cb0c.c +++ b/drivers/media/i2c/rj54n1cb0c.c @@ -1297,8 +1297,7 @@ done: return ret; } -static int rj54n1_probe(struct i2c_client *client, - const struct i2c_device_id *did) +static int rj54n1_probe(struct i2c_client *client) { struct rj54n1 *rj54n1; struct i2c_adapter *adapter = client->adapter; @@ -1422,7 +1421,7 @@ static struct i2c_driver rj54n1_i2c_driver = { .driver = { .name = "rj54n1cb0c", }, - .probe = rj54n1_probe, + .probe_new = rj54n1_probe, .remove = rj54n1_remove, .id_table = rj54n1_id, }; diff --git a/drivers/media/i2c/s5k4ecgx.c b/drivers/media/i2c/s5k4ecgx.c index 3dddcd9dd351..f266e848f52b 100644 --- a/drivers/media/i2c/s5k4ecgx.c +++ b/drivers/media/i2c/s5k4ecgx.c @@ -929,8 +929,7 @@ static int s5k4ecgx_init_v4l2_ctrls(struct s5k4ecgx *priv) return 0; }; -static int s5k4ecgx_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int s5k4ecgx_probe(struct i2c_client *client) { struct s5k4ecgx_platform_data *pdata = client->dev.platform_data; struct v4l2_subdev *sd; @@ -1018,7 +1017,7 @@ static struct i2c_driver v4l2_i2c_driver = { .driver = { .name = S5K4ECGX_DRIVER_NAME, }, - .probe = s5k4ecgx_probe, + .probe_new = s5k4ecgx_probe, .remove = s5k4ecgx_remove, .id_table = s5k4ecgx_id, }; diff --git a/drivers/media/i2c/s5k6aa.c b/drivers/media/i2c/s5k6aa.c index 059211788a65..5996153371fc 100644 --- a/drivers/media/i2c/s5k6aa.c +++ b/drivers/media/i2c/s5k6aa.c @@ -1544,8 +1544,7 @@ static int s5k6aa_configure_gpios(struct s5k6aa *s5k6aa, return 0; } -static int s5k6aa_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int s5k6aa_probe(struct i2c_client *client) { const struct s5k6aa_platform_data *pdata = client->dev.platform_data; struct v4l2_subdev *sd; @@ -1641,7 +1640,7 @@ static struct i2c_driver s5k6aa_i2c_driver = { .driver = { .name = DRIVER_NAME }, - .probe = s5k6aa_probe, + .probe_new = s5k6aa_probe, .remove = s5k6aa_remove, .id_table = s5k6aa_id, }; diff --git a/drivers/media/i2c/saa6588.c b/drivers/media/i2c/saa6588.c index d6a51beabd02..8752f7cff611 100644 --- a/drivers/media/i2c/saa6588.c +++ b/drivers/media/i2c/saa6588.c @@ -448,8 +448,7 @@ static const struct v4l2_subdev_ops saa6588_ops = { /* ---------------------------------------------------------------------- */ -static int saa6588_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int saa6588_probe(struct i2c_client *client) { struct saa6588 *s; struct v4l2_subdev *sd; @@ -506,7 +505,7 @@ static struct i2c_driver saa6588_driver = { .driver = { .name = "saa6588", }, - .probe = saa6588_probe, + .probe_new = saa6588_probe, .remove = saa6588_remove, .id_table = saa6588_id, }; diff --git a/drivers/media/i2c/saa6752hs.c b/drivers/media/i2c/saa6752hs.c index 5928cc6f4595..892d64fe6e81 100644 --- a/drivers/media/i2c/saa6752hs.c +++ b/drivers/media/i2c/saa6752hs.c @@ -659,8 +659,7 @@ static const struct v4l2_subdev_ops saa6752hs_ops = { .pad = &saa6752hs_pad_ops, }; -static int saa6752hs_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int saa6752hs_probe(struct i2c_client *client) { struct saa6752hs_state *h; struct v4l2_subdev *sd; @@ -782,7 +781,7 @@ static struct i2c_driver saa6752hs_driver = { .driver = { .name = "saa6752hs", }, - .probe = saa6752hs_probe, + .probe_new = saa6752hs_probe, .remove = saa6752hs_remove, .id_table = saa6752hs_id, }; diff --git a/drivers/media/i2c/saa7110.c b/drivers/media/i2c/saa7110.c index 5067525d8b11..b58e71517376 100644 --- a/drivers/media/i2c/saa7110.c +++ b/drivers/media/i2c/saa7110.c @@ -358,8 +358,7 @@ static const struct v4l2_subdev_ops saa7110_ops = { /* ----------------------------------------------------------------------- */ -static int saa7110_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int saa7110_probe(struct i2c_client *client) { struct saa7110 *decoder; struct v4l2_subdev *sd; @@ -449,7 +448,7 @@ static struct i2c_driver saa7110_driver = { .driver = { .name = "saa7110", }, - .probe = saa7110_probe, + .probe_new = saa7110_probe, .remove = saa7110_remove, .id_table = saa7110_id, }; diff --git a/drivers/media/i2c/saa717x.c b/drivers/media/i2c/saa717x.c index 4f3d1b432a4e..df01059076fa 100644 --- a/drivers/media/i2c/saa717x.c +++ b/drivers/media/i2c/saa717x.c @@ -1228,8 +1228,7 @@ static const struct v4l2_subdev_ops saa717x_ops = { /* i2c implementation */ /* ----------------------------------------------------------------------- */ -static int saa717x_probe(struct i2c_client *client, - const struct i2c_device_id *did) +static int saa717x_probe(struct i2c_client *client) { struct saa717x_state *decoder; struct v4l2_ctrl_handler *hdl; @@ -1344,7 +1343,7 @@ static struct i2c_driver saa717x_driver = { .driver = { .name = "saa717x", }, - .probe = saa717x_probe, + .probe_new = saa717x_probe, .remove = saa717x_remove, .id_table = saa717x_id, }; diff --git a/drivers/media/i2c/saa7185.c b/drivers/media/i2c/saa7185.c index 266462325d30..c78f2e95ba37 100644 --- a/drivers/media/i2c/saa7185.c +++ b/drivers/media/i2c/saa7185.c @@ -290,8 +290,7 @@ static const struct v4l2_subdev_ops saa7185_ops = { /* ----------------------------------------------------------------------- */ -static int saa7185_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int saa7185_probe(struct i2c_client *client) { int i; struct saa7185 *encoder; @@ -344,7 +343,7 @@ static struct i2c_driver saa7185_driver = { .driver = { .name = "saa7185", }, - .probe = saa7185_probe, + .probe_new = saa7185_probe, .remove = saa7185_remove, .id_table = saa7185_id, }; diff --git a/drivers/media/i2c/sony-btf-mpx.c b/drivers/media/i2c/sony-btf-mpx.c index 927a9ec41463..eef6c8a7c9c9 100644 --- a/drivers/media/i2c/sony-btf-mpx.c +++ b/drivers/media/i2c/sony-btf-mpx.c @@ -331,8 +331,7 @@ static const struct v4l2_subdev_ops sony_btf_mpx_ops = { /* --------------------------------------------------------------------------*/ -static int sony_btf_mpx_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int sony_btf_mpx_probe(struct i2c_client *client) { struct sony_btf_mpx *t; struct v4l2_subdev *sd; @@ -376,7 +375,7 @@ static struct i2c_driver sony_btf_mpx_driver = { .driver = { .name = "sony-btf-mpx", }, - .probe = sony_btf_mpx_probe, + .probe_new = sony_btf_mpx_probe, .remove = sony_btf_mpx_remove, .id_table = sony_btf_mpx_id, }; diff --git a/drivers/media/i2c/sr030pc30.c b/drivers/media/i2c/sr030pc30.c index ff18693beb5c..a83c8bf1c5dd 100644 --- a/drivers/media/i2c/sr030pc30.c +++ b/drivers/media/i2c/sr030pc30.c @@ -675,8 +675,7 @@ static int sr030pc30_detect(struct i2c_client *client) } -static int sr030pc30_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int sr030pc30_probe(struct i2c_client *client) { struct sr030pc30_info *info; struct v4l2_subdev *sd; @@ -751,7 +750,7 @@ static struct i2c_driver sr030pc30_i2c_driver = { .driver = { .name = MODULE_NAME }, - .probe = sr030pc30_probe, + .probe_new = sr030pc30_probe, .remove = sr030pc30_remove, .id_table = sr030pc30_id, }; diff --git a/drivers/media/i2c/tda7432.c b/drivers/media/i2c/tda7432.c index 11e918311b13..bbceaac8e0b3 100644 --- a/drivers/media/i2c/tda7432.c +++ b/drivers/media/i2c/tda7432.c @@ -343,8 +343,7 @@ static const struct v4l2_subdev_ops tda7432_ops = { * i2c interface functions * * *********************** */ -static int tda7432_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tda7432_probe(struct i2c_client *client) { struct tda7432 *t; struct v4l2_subdev *sd; @@ -410,7 +409,7 @@ static struct i2c_driver tda7432_driver = { .driver = { .name = "tda7432", }, - .probe = tda7432_probe, + .probe_new = tda7432_probe, .remove = tda7432_remove, .id_table = tda7432_id, }; diff --git a/drivers/media/i2c/tda9840.c b/drivers/media/i2c/tda9840.c index aaa74944fc7c..25fbd7e3950e 100644 --- a/drivers/media/i2c/tda9840.c +++ b/drivers/media/i2c/tda9840.c @@ -149,8 +149,7 @@ static const struct v4l2_subdev_ops tda9840_ops = { /* ----------------------------------------------------------------------- */ -static int tda9840_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tda9840_probe(struct i2c_client *client) { struct v4l2_subdev *sd; @@ -192,7 +191,7 @@ static struct i2c_driver tda9840_driver = { .driver = { .name = "tda9840", }, - .probe = tda9840_probe, + .probe_new = tda9840_probe, .remove = tda9840_remove, .id_table = tda9840_id, }; diff --git a/drivers/media/i2c/tea6415c.c b/drivers/media/i2c/tea6415c.c index 50e74314f315..d375d2d24354 100644 --- a/drivers/media/i2c/tea6415c.c +++ b/drivers/media/i2c/tea6415c.c @@ -116,8 +116,7 @@ static const struct v4l2_subdev_ops tea6415c_ops = { .video = &tea6415c_video_ops, }; -static int tea6415c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tea6415c_probe(struct i2c_client *client) { struct v4l2_subdev *sd; @@ -151,7 +150,7 @@ static struct i2c_driver tea6415c_driver = { .driver = { .name = "tea6415c", }, - .probe = tea6415c_probe, + .probe_new = tea6415c_probe, .remove = tea6415c_remove, .id_table = tea6415c_id, }; diff --git a/drivers/media/i2c/tea6420.c b/drivers/media/i2c/tea6420.c index 246f2b10ccc7..9da1f3b02c57 100644 --- a/drivers/media/i2c/tea6420.c +++ b/drivers/media/i2c/tea6420.c @@ -87,8 +87,7 @@ static const struct v4l2_subdev_ops tea6420_ops = { .audio = &tea6420_audio_ops, }; -static int tea6420_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tea6420_probe(struct i2c_client *client) { struct v4l2_subdev *sd; int err, i; @@ -133,7 +132,7 @@ static struct i2c_driver tea6420_driver = { .driver = { .name = "tea6420", }, - .probe = tea6420_probe, + .probe_new = tea6420_probe, .remove = tea6420_remove, .id_table = tea6420_id, }; diff --git a/drivers/media/i2c/ths7303.c b/drivers/media/i2c/ths7303.c index 2a0f9a3d1a66..67de90cf696e 100644 --- a/drivers/media/i2c/ths7303.c +++ b/drivers/media/i2c/ths7303.c @@ -322,8 +322,7 @@ static const struct v4l2_subdev_ops ths7303_ops = { .video = &ths7303_video_ops, }; -static int ths7303_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ths7303_probe(struct i2c_client *client) { struct ths7303_platform_data *pdata = client->dev.platform_data; struct ths7303_state *state; @@ -377,7 +376,7 @@ static struct i2c_driver ths7303_driver = { .driver = { .name = "ths73x3", }, - .probe = ths7303_probe, + .probe_new = ths7303_probe, .remove = ths7303_remove, .id_table = ths7303_id, }; diff --git a/drivers/media/i2c/tlv320aic23b.c b/drivers/media/i2c/tlv320aic23b.c index 937fa1dbaecb..47198e803817 100644 --- a/drivers/media/i2c/tlv320aic23b.c +++ b/drivers/media/i2c/tlv320aic23b.c @@ -129,8 +129,7 @@ static const struct v4l2_subdev_ops tlv320aic23b_ops = { * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' */ -static int tlv320aic23b_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tlv320aic23b_probe(struct i2c_client *client) { struct tlv320aic23b_state *state; struct v4l2_subdev *sd; @@ -198,7 +197,7 @@ static struct i2c_driver tlv320aic23b_driver = { .driver = { .name = "tlv320aic23b", }, - .probe = tlv320aic23b_probe, + .probe_new = tlv320aic23b_probe, .remove = tlv320aic23b_remove, .id_table = tlv320aic23b_id, }; diff --git a/drivers/media/i2c/tw2804.c b/drivers/media/i2c/tw2804.c index c7c8dfe8a8a8..710790ece11b 100644 --- a/drivers/media/i2c/tw2804.c +++ b/drivers/media/i2c/tw2804.c @@ -343,8 +343,7 @@ static const struct v4l2_subdev_ops tw2804_ops = { .video = &tw2804_video_ops, }; -static int tw2804_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tw2804_probe(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; struct tw2804 *state; @@ -424,7 +423,7 @@ static struct i2c_driver tw2804_driver = { .driver = { .name = "tw2804", }, - .probe = tw2804_probe, + .probe_new = tw2804_probe, .remove = tw2804_remove, .id_table = tw2804_id, }; diff --git a/drivers/media/i2c/tw9903.c b/drivers/media/i2c/tw9903.c index d7eef7986b75..428ee55787e1 100644 --- a/drivers/media/i2c/tw9903.c +++ b/drivers/media/i2c/tw9903.c @@ -189,8 +189,7 @@ static const struct v4l2_subdev_ops tw9903_ops = { /* --------------------------------------------------------------------------*/ -static int tw9903_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tw9903_probe(struct i2c_client *client) { struct tw9903 *dec; struct v4l2_subdev *sd; @@ -255,7 +254,7 @@ static struct i2c_driver tw9903_driver = { .driver = { .name = "tw9903", }, - .probe = tw9903_probe, + .probe_new = tw9903_probe, .remove = tw9903_remove, .id_table = tw9903_id, }; diff --git a/drivers/media/i2c/tw9906.c b/drivers/media/i2c/tw9906.c index 549ad8f72f12..7824ed9b04ed 100644 --- a/drivers/media/i2c/tw9906.c +++ b/drivers/media/i2c/tw9906.c @@ -157,8 +157,7 @@ static const struct v4l2_subdev_ops tw9906_ops = { .video = &tw9906_video_ops, }; -static int tw9906_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tw9906_probe(struct i2c_client *client) { struct tw9906 *dec; struct v4l2_subdev *sd; @@ -223,7 +222,7 @@ static struct i2c_driver tw9906_driver = { .driver = { .name = "tw9906", }, - .probe = tw9906_probe, + .probe_new = tw9906_probe, .remove = tw9906_remove, .id_table = tw9906_id, }; diff --git a/drivers/media/i2c/tw9910.c b/drivers/media/i2c/tw9910.c index 853b5acead32..459fa22f4341 100644 --- a/drivers/media/i2c/tw9910.c +++ b/drivers/media/i2c/tw9910.c @@ -928,8 +928,7 @@ static const struct v4l2_subdev_ops tw9910_subdev_ops = { * i2c_driver function */ -static int tw9910_probe(struct i2c_client *client, - const struct i2c_device_id *did) +static int tw9910_probe(struct i2c_client *client) { struct tw9910_priv *priv; @@ -1013,7 +1012,7 @@ static struct i2c_driver tw9910_i2c_driver = { .driver = { .name = "tw9910", }, - .probe = tw9910_probe, + .probe_new = tw9910_probe, .remove = tw9910_remove, .id_table = tw9910_id, }; diff --git a/drivers/media/i2c/uda1342.c b/drivers/media/i2c/uda1342.c index d0659c4392f2..b6873d866272 100644 --- a/drivers/media/i2c/uda1342.c +++ b/drivers/media/i2c/uda1342.c @@ -45,8 +45,7 @@ static const struct v4l2_subdev_ops uda1342_ops = { .audio = &uda1342_audio_ops, }; -static int uda1342_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int uda1342_probe(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; struct v4l2_subdev *sd; @@ -89,7 +88,7 @@ static struct i2c_driver uda1342_driver = { .driver = { .name = "uda1342", }, - .probe = uda1342_probe, + .probe_new = uda1342_probe, .remove = uda1342_remove, .id_table = uda1342_id, }; diff --git a/drivers/media/i2c/upd64031a.c b/drivers/media/i2c/upd64031a.c index 4de26ed2ba00..47eed3aab060 100644 --- a/drivers/media/i2c/upd64031a.c +++ b/drivers/media/i2c/upd64031a.c @@ -183,8 +183,7 @@ static const struct v4l2_subdev_ops upd64031a_ops = { /* i2c implementation */ -static int upd64031a_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int upd64031a_probe(struct i2c_client *client) { struct upd64031a_state *state; struct v4l2_subdev *sd; @@ -229,7 +228,7 @@ static struct i2c_driver upd64031a_driver = { .driver = { .name = "upd64031a", }, - .probe = upd64031a_probe, + .probe_new = upd64031a_probe, .remove = upd64031a_remove, .id_table = upd64031a_id, }; diff --git a/drivers/media/i2c/upd64083.c b/drivers/media/i2c/upd64083.c index 2bfd5443d406..3f5a7d4853a1 100644 --- a/drivers/media/i2c/upd64083.c +++ b/drivers/media/i2c/upd64083.c @@ -154,8 +154,7 @@ static const struct v4l2_subdev_ops upd64083_ops = { /* i2c implementation */ -static int upd64083_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int upd64083_probe(struct i2c_client *client) { struct upd64083_state *state; struct v4l2_subdev *sd; @@ -200,7 +199,7 @@ static struct i2c_driver upd64083_driver = { .driver = { .name = "upd64083", }, - .probe = upd64083_probe, + .probe_new = upd64083_probe, .remove = upd64083_remove, .id_table = upd64083_id, }; diff --git a/drivers/media/i2c/vp27smpx.c b/drivers/media/i2c/vp27smpx.c index c832edad5fa7..ed1c58ea8ed3 100644 --- a/drivers/media/i2c/vp27smpx.c +++ b/drivers/media/i2c/vp27smpx.c @@ -138,8 +138,7 @@ static const struct v4l2_subdev_ops vp27smpx_ops = { * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' */ -static int vp27smpx_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int vp27smpx_probe(struct i2c_client *client) { struct vp27smpx_state *state; struct v4l2_subdev *sd; @@ -182,7 +181,7 @@ static struct i2c_driver vp27smpx_driver = { .driver = { .name = "vp27smpx", }, - .probe = vp27smpx_probe, + .probe_new = vp27smpx_probe, .remove = vp27smpx_remove, .id_table = vp27smpx_id, }; diff --git a/drivers/media/i2c/vpx3220.c b/drivers/media/i2c/vpx3220.c index b481ec196b88..aa73d5dcc3e7 100644 --- a/drivers/media/i2c/vpx3220.c +++ b/drivers/media/i2c/vpx3220.c @@ -456,8 +456,7 @@ static const struct v4l2_subdev_ops vpx3220_ops = { * Client management code */ -static int vpx3220_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int vpx3220_probe(struct i2c_client *client) { struct vpx3220 *decoder; struct v4l2_subdev *sd; @@ -547,7 +546,7 @@ static struct i2c_driver vpx3220_driver = { .driver = { .name = "vpx3220", }, - .probe = vpx3220_probe, + .probe_new = vpx3220_probe, .remove = vpx3220_remove, .id_table = vpx3220_id, }; diff --git a/drivers/media/i2c/vs6624.c b/drivers/media/i2c/vs6624.c index d496bb45f201..d35c5ec148f4 100644 --- a/drivers/media/i2c/vs6624.c +++ b/drivers/media/i2c/vs6624.c @@ -738,8 +738,7 @@ static const struct v4l2_subdev_ops vs6624_ops = { .pad = &vs6624_pad_ops, }; -static int vs6624_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int vs6624_probe(struct i2c_client *client) { struct vs6624 *sensor; struct v4l2_subdev *sd; @@ -843,7 +842,7 @@ static struct i2c_driver vs6624_driver = { .driver = { .name = "vs6624", }, - .probe = vs6624_probe, + .probe_new = vs6624_probe, .remove = vs6624_remove, .id_table = vs6624_id, }; diff --git a/drivers/media/i2c/wm8739.c b/drivers/media/i2c/wm8739.c index 180b35347521..8b34a673ffd3 100644 --- a/drivers/media/i2c/wm8739.c +++ b/drivers/media/i2c/wm8739.c @@ -178,8 +178,7 @@ static const struct v4l2_subdev_ops wm8739_ops = { /* i2c implementation */ -static int wm8739_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int wm8739_probe(struct i2c_client *client) { struct wm8739_state *state; struct v4l2_subdev *sd; @@ -253,7 +252,7 @@ static struct i2c_driver wm8739_driver = { .driver = { .name = "wm8739", }, - .probe = wm8739_probe, + .probe_new = wm8739_probe, .remove = wm8739_remove, .id_table = wm8739_id, }; diff --git a/drivers/media/i2c/wm8775.c b/drivers/media/i2c/wm8775.c index 8ff97867d3cd..56d98518f7eb 100644 --- a/drivers/media/i2c/wm8775.c +++ b/drivers/media/i2c/wm8775.c @@ -190,8 +190,7 @@ static const struct v4l2_subdev_ops wm8775_ops = { * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' */ -static int wm8775_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int wm8775_probe(struct i2c_client *client) { struct wm8775_state *state; struct v4l2_subdev *sd; @@ -299,7 +298,7 @@ static struct i2c_driver wm8775_driver = { .driver = { .name = "wm8775", }, - .probe = wm8775_probe, + .probe_new = wm8775_probe, .remove = wm8775_remove, .id_table = wm8775_id, }; diff --git a/drivers/media/pci/bt8xx/bttv.h b/drivers/media/pci/bt8xx/bttv.h index d24b9ef9f59f..eed7eeb3b963 100644 --- a/drivers/media/pci/bt8xx/bttv.h +++ b/drivers/media/pci/bt8xx/bttv.h @@ -289,7 +289,6 @@ extern void bttv_init_card2(struct bttv *btv); extern void bttv_init_tuner(struct bttv *btv); /* card-specific functions */ -extern void tea5757_set_freq(struct bttv *btv, unsigned short freq); extern u32 bttv_tda9880_setnorm(struct bttv *btv, u32 gpiobits); /* extra tweaks for some chipsets */ diff --git a/drivers/media/pci/cx25821/cx25821-video.h b/drivers/media/pci/cx25821/cx25821-video.h index cf0d3f5509e7..080c8490a250 100644 --- a/drivers/media/pci/cx25821/cx25821-video.h +++ b/drivers/media/pci/cx25821/cx25821-video.h @@ -36,9 +36,6 @@ do { \ } while (0) #define FORMAT_FLAGS_PACKED 0x01 -extern void cx25821_video_wakeup(struct cx25821_dev *dev, - struct cx25821_dmaqueue *q, u32 count); - extern int cx25821_start_video_dma(struct cx25821_dev *dev, struct cx25821_dmaqueue *q, struct cx25821_buffer *buf, diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h index 49fe0f6bacba..5c9b2912a9d1 100644 --- a/drivers/media/pci/saa7134/saa7134.h +++ b/drivers/media/pci/saa7134/saa7134.h @@ -866,7 +866,6 @@ int saa7134_ts_stop(struct saa7134_dev *dev); /* saa7134-vbi.c */ extern const struct vb2_ops saa7134_vbi_qops; -extern struct video_device saa7134_vbi_template; int saa7134_vbi_init1(struct saa7134_dev *dev); int saa7134_vbi_fini(struct saa7134_dev *dev); @@ -897,9 +896,6 @@ void saa7134_enable_i2s(struct saa7134_dev *dev); /* ----------------------------------------------------------- */ /* saa7134-oss.c */ -extern const struct file_operations saa7134_dsp_fops; -extern const struct file_operations saa7134_mixer_fops; - int saa7134_oss_init1(struct saa7134_dev *dev); int saa7134_oss_fini(struct saa7134_dev *dev); void saa7134_irq_oss_done(struct saa7134_dev *dev, unsigned long status); diff --git a/drivers/media/pci/saa7164/saa7164-core.c b/drivers/media/pci/saa7164/saa7164-core.c index d5f32e3ff544..01d75ef2342d 100644 --- a/drivers/media/pci/saa7164/saa7164-core.c +++ b/drivers/media/pci/saa7164/saa7164-core.c @@ -352,7 +352,7 @@ static void saa7164_work_enchandler(struct work_struct *w) container_of(w, struct saa7164_port, workenc); struct saa7164_dev *dev = port->dev; - u32 wp, mcb, rp, cnt = 0; + u32 wp, mcb, rp; port->last_svc_msecs_diff = port->last_svc_msecs; port->last_svc_msecs = jiffies_to_msecs(jiffies); @@ -405,7 +405,6 @@ static void saa7164_work_enchandler(struct work_struct *w) saa7164_work_enchandler_helper(port, rp); port->last_svc_rp = rp; - cnt++; if (rp == mcb) break; @@ -429,7 +428,7 @@ static void saa7164_work_vbihandler(struct work_struct *w) container_of(w, struct saa7164_port, workenc); struct saa7164_dev *dev = port->dev; - u32 wp, mcb, rp, cnt = 0; + u32 wp, mcb, rp; port->last_svc_msecs_diff = port->last_svc_msecs; port->last_svc_msecs = jiffies_to_msecs(jiffies); @@ -481,7 +480,6 @@ static void saa7164_work_vbihandler(struct work_struct *w) saa7164_work_enchandler_helper(port, rp); port->last_svc_rp = rp; - cnt++; if (rp == mcb) break; diff --git a/drivers/media/pci/saa7164/saa7164.h b/drivers/media/pci/saa7164/saa7164.h index 4b4eb156e214..eede47b686a3 100644 --- a/drivers/media/pci/saa7164/saa7164.h +++ b/drivers/media/pci/saa7164/saa7164.h @@ -493,8 +493,6 @@ int saa7164_downloadfirmware(struct saa7164_dev *dev); /* saa7164-i2c.c */ extern int saa7164_i2c_register(struct saa7164_i2c *bus); extern int saa7164_i2c_unregister(struct saa7164_i2c *bus); -extern void saa7164_call_i2c_clients(struct saa7164_i2c *bus, - unsigned int cmd, void *arg); /* ----------------------------------------------------------- */ /* saa7164-bus.c */ diff --git a/drivers/media/pci/zoran/zoran_device.h b/drivers/media/pci/zoran/zoran_device.h index 34fd5cc914eb..237e830ae726 100644 --- a/drivers/media/pci/zoran/zoran_device.h +++ b/drivers/media/pci/zoran/zoran_device.h @@ -47,8 +47,6 @@ void zr36057_restart(struct zoran *zr); extern const struct zoran_format zoran_formats[]; -extern int v4l_bufsize; -extern int jpg_bufsize; extern int pass_through; /* i2c */ diff --git a/drivers/media/platform/amphion/vdec.c b/drivers/media/platform/amphion/vdec.c index 6f2aff80314f..b27e6bed85f0 100644 --- a/drivers/media/platform/amphion/vdec.c +++ b/drivers/media/platform/amphion/vdec.c @@ -755,6 +755,9 @@ static bool vdec_check_source_change(struct vpu_inst *inst) if (!inst->fh.m2m_ctx) return false; + if (vdec->reset_codec) + return false; + if (!vb2_is_streaming(v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx))) return true; fmt = vpu_helper_find_format(inst, inst->cap_format.type, vdec->codec_info.pixfmt); @@ -1090,7 +1093,8 @@ static void vdec_event_seq_hdr(struct vpu_inst *inst, struct vpu_dec_codec_info vdec->seq_tag = vdec->codec_info.tag; if (vdec->is_source_changed) { vdec_update_state(inst, VPU_CODEC_STATE_DYAMIC_RESOLUTION_CHANGE, 0); - vpu_notify_source_change(inst); + vdec->source_change++; + vdec_handle_resolution_change(inst); vdec->is_source_changed = false; } } @@ -1337,6 +1341,8 @@ static void vdec_abort(struct vpu_inst *inst) vdec->decoded_frame_count, vdec->display_frame_count, vdec->sequence); + if (!vdec->seq_hdr_found) + vdec->reset_codec = true; vdec->params.end_flag = 0; vdec->drain = 0; vdec->params.frame_count = 0; @@ -1344,6 +1350,7 @@ static void vdec_abort(struct vpu_inst *inst) vdec->display_frame_count = 0; vdec->sequence = 0; vdec->aborting = false; + inst->extra_size = 0; } static void vdec_stop(struct vpu_inst *inst, bool free) @@ -1466,8 +1473,7 @@ static int vdec_start_session(struct vpu_inst *inst, u32 type) } if (V4L2_TYPE_IS_OUTPUT(type)) { - if (inst->state == VPU_CODEC_STATE_SEEK) - vdec_update_state(inst, vdec->state, 1); + vdec_update_state(inst, vdec->state, 1); vdec->eos_received = 0; vpu_process_output_buffer(inst); } else { @@ -1631,6 +1637,7 @@ static int vdec_open(struct file *file) return ret; vdec->fixed_fmt = false; + vdec->state = VPU_CODEC_STATE_ACTIVE; inst->min_buffer_cap = VDEC_MIN_BUFFER_CAP; inst->min_buffer_out = VDEC_MIN_BUFFER_OUT; vdec_init(file); diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platform/aspeed/aspeed-video.c index 018da317d2f1..794d4dc3a654 100644 --- a/drivers/media/platform/aspeed/aspeed-video.c +++ b/drivers/media/platform/aspeed/aspeed-video.c @@ -1961,19 +1961,7 @@ static int aspeed_video_debugfs_show(struct seq_file *s, void *data) return 0; } - -static int aspeed_video_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, aspeed_video_debugfs_show, inode->i_private); -} - -static const struct file_operations aspeed_video_debugfs_ops = { - .owner = THIS_MODULE, - .open = aspeed_video_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(aspeed_video_debugfs); static struct dentry *debugfs_entry; @@ -1987,7 +1975,7 @@ static int aspeed_video_debugfs_create(struct aspeed_video *video) { debugfs_entry = debugfs_create_file(DEVICE_NAME, 0444, NULL, video, - &aspeed_video_debugfs_ops); + &aspeed_video_debugfs_fops); if (!debugfs_entry) aspeed_video_debugfs_remove(video); diff --git a/drivers/media/platform/chips-media/coda-jpeg.c b/drivers/media/platform/chips-media/coda-jpeg.c index 435e7030fc2a..ba8f41002917 100644 --- a/drivers/media/platform/chips-media/coda-jpeg.c +++ b/drivers/media/platform/chips-media/coda-jpeg.c @@ -1052,10 +1052,16 @@ static int coda9_jpeg_start_encoding(struct coda_ctx *ctx) v4l2_err(&dev->v4l2_dev, "error loading Huffman tables\n"); return ret; } - if (!ctx->params.jpeg_qmat_tab[0]) + if (!ctx->params.jpeg_qmat_tab[0]) { ctx->params.jpeg_qmat_tab[0] = kmalloc(64, GFP_KERNEL); - if (!ctx->params.jpeg_qmat_tab[1]) + if (!ctx->params.jpeg_qmat_tab[0]) + return -ENOMEM; + } + if (!ctx->params.jpeg_qmat_tab[1]) { ctx->params.jpeg_qmat_tab[1] = kmalloc(64, GFP_KERNEL); + if (!ctx->params.jpeg_qmat_tab[1]) + return -ENOMEM; + } coda_set_jpeg_compression_quality(ctx, ctx->params.jpeg_quality); return 0; diff --git a/drivers/media/platform/mediatek/mdp3/Kconfig b/drivers/media/platform/mediatek/mdp3/Kconfig index 50ae07b75b5f..846e759a8f6a 100644 --- a/drivers/media/platform/mediatek/mdp3/Kconfig +++ b/drivers/media/platform/mediatek/mdp3/Kconfig @@ -9,7 +9,6 @@ config VIDEO_MEDIATEK_MDP3 select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV select MTK_MMSYS - select VIDEO_MEDIATEK_VPU select MTK_CMDQ select MTK_SCP default n diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c index d810a78dde51..d65800a3b89d 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c @@ -1397,7 +1397,10 @@ int mtk_vcodec_enc_ctrls_setup(struct mtk_vcodec_ctx *ctx) 0, V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE); v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_MPEG_VIDEO_H264_PROFILE, V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, - 0, V4L2_MPEG_VIDEO_H264_PROFILE_HIGH); + ~((1 << V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) | + (1 << V4L2_MPEG_VIDEO_H264_PROFILE_HIGH)), + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH); v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_MPEG_VIDEO_H264_LEVEL, h264_max_level, 0, V4L2_MPEG_VIDEO_H264_LEVEL_4_0); diff --git a/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_multi_if.c b/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_multi_if.c index 4cc92700692b..18e048755d11 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_multi_if.c +++ b/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_multi_if.c @@ -539,6 +539,29 @@ vdec_dec_end: return 0; } +static void vdec_h264_insert_startcode(struct mtk_vcodec_dev *vcodec_dev, unsigned char *buf, + size_t *bs_size, struct mtk_h264_pps_param *pps) +{ + struct device *dev = &vcodec_dev->plat_dev->dev; + + /* Need to add pending data at the end of bitstream when bs_sz is small than + * 20 bytes for cavlc bitstream, or lat will decode fail. This pending data is + * useful for mt8192 and mt8195 platform. + * + * cavlc bitstream when entropy_coding_mode_flag is false. + */ + if (pps->entropy_coding_mode_flag || *bs_size > 20 || + !(of_device_is_compatible(dev->of_node, "mediatek,mt8192-vcodec-dec") || + of_device_is_compatible(dev->of_node, "mediatek,mt8195-vcodec-dec"))) + return; + + buf[*bs_size] = 0; + buf[*bs_size + 1] = 0; + buf[*bs_size + 2] = 1; + buf[*bs_size + 3] = 0xff; + (*bs_size) += 4; +} + static int vdec_h264_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, struct vdec_fb *fb, bool *res_chg) { @@ -582,9 +605,6 @@ static int vdec_h264_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, } inst->vsi->dec.nal_info = buf[nal_start_idx]; - inst->vsi->dec.bs_buf_addr = (u64)bs->dma_addr; - inst->vsi->dec.bs_buf_size = bs->size; - lat_buf->src_buf_req = src_buf_info->m2m_buf.vb.vb2_buf.req_obj.req; v4l2_m2m_buf_copy_metadata(&src_buf_info->m2m_buf.vb, &lat_buf->ts_info, true); @@ -592,6 +612,12 @@ static int vdec_h264_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, if (err) goto err_free_fb_out; + vdec_h264_insert_startcode(inst->ctx->dev, buf, &bs->size, + &share_info->h264_slice_params.pps); + + inst->vsi->dec.bs_buf_addr = (uint64_t)bs->dma_addr; + inst->vsi->dec.bs_buf_size = bs->size; + *res_chg = inst->resolution_changed; if (inst->resolution_changed) { mtk_vcodec_debug(inst, "- resolution changed -"); diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c index bb2bc28b55ed..6cd015a35f7c 100644 --- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c @@ -2431,6 +2431,8 @@ static int mxc_jpeg_probe(struct platform_device *pdev) unsigned int slot; of_id = of_match_node(mxc_jpeg_match, dev->of_node); + if (!of_id) + return -ENODEV; mode = *(const int *)of_id->data; jpeg = devm_kzalloc(dev, sizeof(struct mxc_jpeg_dev), GFP_KERNEL); diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c index 19a9b54ff761..8b08306dabbf 100644 --- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c @@ -1062,10 +1062,10 @@ static int s5p_mfc_mmap(struct file *file, struct vm_area_struct *vma) int ret; if (offset < DST_QUEUE_OFF_BASE) { - mfc_debug(2, "mmaping source\n"); + mfc_debug(2, "mmapping source\n"); ret = vb2_mmap(&ctx->vq_src, vma); } else { /* capture */ - mfc_debug(2, "mmaping destination\n"); + mfc_debug(2, "mmapping destination\n"); vma->vm_pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT); ret = vb2_mmap(&ctx->vq_dst, vma); } diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c index cefe6b7bfdc4..4c5027a0480d 100644 --- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c +++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c @@ -24,16 +24,18 @@ #include <linux/module.h> #include <linux/of_gpio.h> #include <linux/of_platform.h> +#include <linux/pinctrl/consumer.h> +#include <linux/pinctrl/pinctrl.h> #include <linux/platform_device.h> -#include <linux/usb.h> #include <linux/slab.h> #include <linux/time.h> +#include <linux/usb.h> #include <linux/wait.h> -#include <linux/pinctrl/pinctrl.h> -#include "c8sectpfe-core.h" #include "c8sectpfe-common.h" +#include "c8sectpfe-core.h" #include "c8sectpfe-debugfs.h" + #include <media/dmxdev.h> #include <media/dvb_demux.h> #include <media/dvb_frontend.h> diff --git a/drivers/media/platform/st/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c index 37458d4d9564..7d393f696bff 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmi.c +++ b/drivers/media/platform/st/stm32/stm32-dcmi.c @@ -1997,10 +1997,8 @@ static int dcmi_probe(struct platform_device *pdev) } dcmi->regs = devm_ioremap_resource(&pdev->dev, dcmi->res); - if (IS_ERR(dcmi->regs)) { - dev_err(&pdev->dev, "Could not map registers\n"); + if (IS_ERR(dcmi->regs)) return PTR_ERR(dcmi->regs); - } mclk = devm_clk_get(&pdev->dev, "mclk"); if (IS_ERR(mclk)) { diff --git a/drivers/media/platform/sunxi/sun6i-csi/Makefile b/drivers/media/platform/sunxi/sun6i-csi/Makefile index e7e315347804..87e7a715140a 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/Makefile +++ b/drivers/media/platform/sunxi/sun6i-csi/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only -sun6i-csi-y += sun6i_video.o sun6i_csi.o +sun6i-csi-y += sun6i_csi.o sun6i_csi_bridge.o sun6i_csi_capture.o obj-$(CONFIG_VIDEO_SUN6I_CSI) += sun6i-csi.o diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c index 9119f5e0e05e..e3e6650181c8 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c @@ -1,18 +1,14 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright (c) 2011-2018 Magewell Electronics Co., Ltd. (Nanjing) - * All rights reserved. * Author: Yong Deng <[email protected]> + * Copyright 2021-2022 Bootlin + * Author: Paul Kocialkowski <[email protected]> */ #include <linux/clk.h> -#include <linux/delay.h> -#include <linux/dma-mapping.h> #include <linux/err.h> -#include <linux/fs.h> #include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/ioctl.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_device.h> @@ -20,561 +16,56 @@ #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/reset.h> -#include <linux/sched.h> -#include <linux/sizes.h> -#include <linux/slab.h> +#include <media/v4l2-device.h> #include <media/v4l2-mc.h> #include "sun6i_csi.h" +#include "sun6i_csi_bridge.h" +#include "sun6i_csi_capture.h" #include "sun6i_csi_reg.h" -/* Helpers */ +/* ISP */ -/* TODO add 10&12 bit YUV, RGB support */ -bool sun6i_csi_is_format_supported(struct sun6i_csi_device *csi_dev, - u32 pixformat, u32 mbus_code) +int sun6i_csi_isp_complete(struct sun6i_csi_device *csi_dev, + struct v4l2_device *v4l2_dev) { - struct sun6i_csi_v4l2 *v4l2 = &csi_dev->v4l2; - - /* - * Some video receivers have the ability to be compatible with - * 8bit and 16bit bus width. - * Identify the media bus format from device tree. - */ - if ((v4l2->v4l2_ep.bus_type == V4L2_MBUS_PARALLEL - || v4l2->v4l2_ep.bus_type == V4L2_MBUS_BT656) - && v4l2->v4l2_ep.bus.parallel.bus_width == 16) { - switch (pixformat) { - case V4L2_PIX_FMT_NV12_16L16: - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21: - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV61: - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_YVU420: - case V4L2_PIX_FMT_YUV422P: - switch (mbus_code) { - case MEDIA_BUS_FMT_UYVY8_1X16: - case MEDIA_BUS_FMT_VYUY8_1X16: - case MEDIA_BUS_FMT_YUYV8_1X16: - case MEDIA_BUS_FMT_YVYU8_1X16: - return true; - default: - dev_dbg(csi_dev->dev, - "Unsupported mbus code: 0x%x\n", - mbus_code); - break; - } - break; - default: - dev_dbg(csi_dev->dev, "Unsupported pixformat: 0x%x\n", - pixformat); - break; - } - return false; - } + if (csi_dev->v4l2_dev && csi_dev->v4l2_dev != v4l2_dev) + return -EINVAL; - switch (pixformat) { - case V4L2_PIX_FMT_SBGGR8: - return (mbus_code == MEDIA_BUS_FMT_SBGGR8_1X8); - case V4L2_PIX_FMT_SGBRG8: - return (mbus_code == MEDIA_BUS_FMT_SGBRG8_1X8); - case V4L2_PIX_FMT_SGRBG8: - return (mbus_code == MEDIA_BUS_FMT_SGRBG8_1X8); - case V4L2_PIX_FMT_SRGGB8: - return (mbus_code == MEDIA_BUS_FMT_SRGGB8_1X8); - case V4L2_PIX_FMT_SBGGR10: - return (mbus_code == MEDIA_BUS_FMT_SBGGR10_1X10); - case V4L2_PIX_FMT_SGBRG10: - return (mbus_code == MEDIA_BUS_FMT_SGBRG10_1X10); - case V4L2_PIX_FMT_SGRBG10: - return (mbus_code == MEDIA_BUS_FMT_SGRBG10_1X10); - case V4L2_PIX_FMT_SRGGB10: - return (mbus_code == MEDIA_BUS_FMT_SRGGB10_1X10); - case V4L2_PIX_FMT_SBGGR12: - return (mbus_code == MEDIA_BUS_FMT_SBGGR12_1X12); - case V4L2_PIX_FMT_SGBRG12: - return (mbus_code == MEDIA_BUS_FMT_SGBRG12_1X12); - case V4L2_PIX_FMT_SGRBG12: - return (mbus_code == MEDIA_BUS_FMT_SGRBG12_1X12); - case V4L2_PIX_FMT_SRGGB12: - return (mbus_code == MEDIA_BUS_FMT_SRGGB12_1X12); - - case V4L2_PIX_FMT_YUYV: - return (mbus_code == MEDIA_BUS_FMT_YUYV8_2X8); - case V4L2_PIX_FMT_YVYU: - return (mbus_code == MEDIA_BUS_FMT_YVYU8_2X8); - case V4L2_PIX_FMT_UYVY: - return (mbus_code == MEDIA_BUS_FMT_UYVY8_2X8); - case V4L2_PIX_FMT_VYUY: - return (mbus_code == MEDIA_BUS_FMT_VYUY8_2X8); - - case V4L2_PIX_FMT_NV12_16L16: - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21: - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV61: - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_YVU420: - case V4L2_PIX_FMT_YUV422P: - switch (mbus_code) { - case MEDIA_BUS_FMT_UYVY8_2X8: - case MEDIA_BUS_FMT_VYUY8_2X8: - case MEDIA_BUS_FMT_YUYV8_2X8: - case MEDIA_BUS_FMT_YVYU8_2X8: - return true; - default: - dev_dbg(csi_dev->dev, "Unsupported mbus code: 0x%x\n", - mbus_code); - break; - } - break; - - case V4L2_PIX_FMT_RGB565: - return (mbus_code == MEDIA_BUS_FMT_RGB565_2X8_LE); - case V4L2_PIX_FMT_RGB565X: - return (mbus_code == MEDIA_BUS_FMT_RGB565_2X8_BE); - - case V4L2_PIX_FMT_JPEG: - return (mbus_code == MEDIA_BUS_FMT_JPEG_1X8); - - default: - dev_dbg(csi_dev->dev, "Unsupported pixformat: 0x%x\n", - pixformat); - break; - } + csi_dev->v4l2_dev = v4l2_dev; + csi_dev->media_dev = v4l2_dev->mdev; - return false; + return sun6i_csi_capture_setup(csi_dev); } -int sun6i_csi_set_power(struct sun6i_csi_device *csi_dev, bool enable) +static int sun6i_csi_isp_detect(struct sun6i_csi_device *csi_dev) { struct device *dev = csi_dev->dev; - struct regmap *regmap = csi_dev->regmap; - int ret; - - if (!enable) { - regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, 0); - pm_runtime_put(dev); + struct fwnode_handle *handle; + /* + * ISP is not available if not connected via fwnode graph. + * This will also check that the remote parent node is available. + */ + handle = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), + SUN6I_CSI_PORT_ISP, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!handle) return 0; - } - - ret = pm_runtime_resume_and_get(dev); - if (ret < 0) - return ret; - - regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, CSI_EN_CSI_EN); - - return 0; -} - -static enum csi_input_fmt get_csi_input_format(struct sun6i_csi_device *csi_dev, - u32 mbus_code, u32 pixformat) -{ - /* non-YUV */ - if ((mbus_code & 0xF000) != 0x2000) - return CSI_INPUT_FORMAT_RAW; - - switch (pixformat) { - case V4L2_PIX_FMT_YUYV: - case V4L2_PIX_FMT_YVYU: - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_VYUY: - return CSI_INPUT_FORMAT_RAW; - default: - break; - } - /* not support YUV420 input format yet */ - dev_dbg(csi_dev->dev, "Select YUV422 as default input format of CSI.\n"); - return CSI_INPUT_FORMAT_YUV422; -} - -static enum csi_output_fmt -get_csi_output_format(struct sun6i_csi_device *csi_dev, u32 pixformat, - u32 field) -{ - bool buf_interlaced = false; - - if (field == V4L2_FIELD_INTERLACED - || field == V4L2_FIELD_INTERLACED_TB - || field == V4L2_FIELD_INTERLACED_BT) - buf_interlaced = true; - - switch (pixformat) { - case V4L2_PIX_FMT_SBGGR8: - case V4L2_PIX_FMT_SGBRG8: - case V4L2_PIX_FMT_SGRBG8: - case V4L2_PIX_FMT_SRGGB8: - return buf_interlaced ? CSI_FRAME_RAW_8 : CSI_FIELD_RAW_8; - case V4L2_PIX_FMT_SBGGR10: - case V4L2_PIX_FMT_SGBRG10: - case V4L2_PIX_FMT_SGRBG10: - case V4L2_PIX_FMT_SRGGB10: - return buf_interlaced ? CSI_FRAME_RAW_10 : CSI_FIELD_RAW_10; - case V4L2_PIX_FMT_SBGGR12: - case V4L2_PIX_FMT_SGBRG12: - case V4L2_PIX_FMT_SGRBG12: - case V4L2_PIX_FMT_SRGGB12: - return buf_interlaced ? CSI_FRAME_RAW_12 : CSI_FIELD_RAW_12; - - case V4L2_PIX_FMT_YUYV: - case V4L2_PIX_FMT_YVYU: - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_VYUY: - return buf_interlaced ? CSI_FRAME_RAW_8 : CSI_FIELD_RAW_8; - - case V4L2_PIX_FMT_NV12_16L16: - return buf_interlaced ? CSI_FRAME_MB_YUV420 : - CSI_FIELD_MB_YUV420; - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21: - return buf_interlaced ? CSI_FRAME_UV_CB_YUV420 : - CSI_FIELD_UV_CB_YUV420; - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_YVU420: - return buf_interlaced ? CSI_FRAME_PLANAR_YUV420 : - CSI_FIELD_PLANAR_YUV420; - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV61: - return buf_interlaced ? CSI_FRAME_UV_CB_YUV422 : - CSI_FIELD_UV_CB_YUV422; - case V4L2_PIX_FMT_YUV422P: - return buf_interlaced ? CSI_FRAME_PLANAR_YUV422 : - CSI_FIELD_PLANAR_YUV422; - - case V4L2_PIX_FMT_RGB565: - case V4L2_PIX_FMT_RGB565X: - return buf_interlaced ? CSI_FRAME_RGB565 : CSI_FIELD_RGB565; - - case V4L2_PIX_FMT_JPEG: - return buf_interlaced ? CSI_FRAME_RAW_8 : CSI_FIELD_RAW_8; - - default: - dev_warn(csi_dev->dev, "Unsupported pixformat: 0x%x\n", pixformat); - break; - } + fwnode_handle_put(handle); - return CSI_FIELD_RAW_8; -} - -static enum csi_input_seq get_csi_input_seq(struct sun6i_csi_device *csi_dev, - u32 mbus_code, u32 pixformat) -{ - /* Input sequence does not apply to non-YUV formats */ - if ((mbus_code & 0xF000) != 0x2000) + if (!IS_ENABLED(CONFIG_VIDEO_SUN6I_ISP)) { + dev_warn(dev, + "ISP link is detected but not enabled in kernel config!"); return 0; - - switch (pixformat) { - case V4L2_PIX_FMT_NV12_16L16: - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_YUV422P: - switch (mbus_code) { - case MEDIA_BUS_FMT_UYVY8_2X8: - case MEDIA_BUS_FMT_UYVY8_1X16: - return CSI_INPUT_SEQ_UYVY; - case MEDIA_BUS_FMT_VYUY8_2X8: - case MEDIA_BUS_FMT_VYUY8_1X16: - return CSI_INPUT_SEQ_VYUY; - case MEDIA_BUS_FMT_YUYV8_2X8: - case MEDIA_BUS_FMT_YUYV8_1X16: - return CSI_INPUT_SEQ_YUYV; - case MEDIA_BUS_FMT_YVYU8_1X16: - case MEDIA_BUS_FMT_YVYU8_2X8: - return CSI_INPUT_SEQ_YVYU; - default: - dev_warn(csi_dev->dev, "Unsupported mbus code: 0x%x\n", - mbus_code); - break; - } - break; - case V4L2_PIX_FMT_NV21: - case V4L2_PIX_FMT_NV61: - case V4L2_PIX_FMT_YVU420: - switch (mbus_code) { - case MEDIA_BUS_FMT_UYVY8_2X8: - case MEDIA_BUS_FMT_UYVY8_1X16: - return CSI_INPUT_SEQ_VYUY; - case MEDIA_BUS_FMT_VYUY8_2X8: - case MEDIA_BUS_FMT_VYUY8_1X16: - return CSI_INPUT_SEQ_UYVY; - case MEDIA_BUS_FMT_YUYV8_2X8: - case MEDIA_BUS_FMT_YUYV8_1X16: - return CSI_INPUT_SEQ_YVYU; - case MEDIA_BUS_FMT_YVYU8_1X16: - case MEDIA_BUS_FMT_YVYU8_2X8: - return CSI_INPUT_SEQ_YUYV; - default: - dev_warn(csi_dev->dev, "Unsupported mbus code: 0x%x\n", - mbus_code); - break; - } - break; - - case V4L2_PIX_FMT_YUYV: - return CSI_INPUT_SEQ_YUYV; - - default: - dev_warn(csi_dev->dev, "Unsupported pixformat: 0x%x, defaulting to YUYV\n", - pixformat); - break; - } - - return CSI_INPUT_SEQ_YUYV; -} - -static void sun6i_csi_setup_bus(struct sun6i_csi_device *csi_dev) -{ - struct v4l2_fwnode_endpoint *endpoint = &csi_dev->v4l2.v4l2_ep; - struct sun6i_csi_config *config = &csi_dev->config; - unsigned char bus_width; - u32 flags; - u32 cfg; - bool input_interlaced = false; - - if (config->field == V4L2_FIELD_INTERLACED - || config->field == V4L2_FIELD_INTERLACED_TB - || config->field == V4L2_FIELD_INTERLACED_BT) - input_interlaced = true; - - bus_width = endpoint->bus.parallel.bus_width; - - regmap_read(csi_dev->regmap, CSI_IF_CFG_REG, &cfg); - - cfg &= ~(CSI_IF_CFG_CSI_IF_MASK | CSI_IF_CFG_MIPI_IF_MASK | - CSI_IF_CFG_IF_DATA_WIDTH_MASK | - CSI_IF_CFG_CLK_POL_MASK | CSI_IF_CFG_VREF_POL_MASK | - CSI_IF_CFG_HREF_POL_MASK | CSI_IF_CFG_FIELD_MASK | - CSI_IF_CFG_SRC_TYPE_MASK); - - if (input_interlaced) - cfg |= CSI_IF_CFG_SRC_TYPE_INTERLACED; - else - cfg |= CSI_IF_CFG_SRC_TYPE_PROGRESSED; - - switch (endpoint->bus_type) { - case V4L2_MBUS_PARALLEL: - cfg |= CSI_IF_CFG_MIPI_IF_CSI; - - flags = endpoint->bus.parallel.flags; - - cfg |= (bus_width == 16) ? CSI_IF_CFG_CSI_IF_YUV422_16BIT : - CSI_IF_CFG_CSI_IF_YUV422_INTLV; - - if (flags & V4L2_MBUS_FIELD_EVEN_LOW) - cfg |= CSI_IF_CFG_FIELD_POSITIVE; - - if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) - cfg |= CSI_IF_CFG_VREF_POL_POSITIVE; - if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) - cfg |= CSI_IF_CFG_HREF_POL_POSITIVE; - - if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING) - cfg |= CSI_IF_CFG_CLK_POL_FALLING_EDGE; - break; - case V4L2_MBUS_BT656: - cfg |= CSI_IF_CFG_MIPI_IF_CSI; - - flags = endpoint->bus.parallel.flags; - - cfg |= (bus_width == 16) ? CSI_IF_CFG_CSI_IF_BT1120 : - CSI_IF_CFG_CSI_IF_BT656; - - if (flags & V4L2_MBUS_FIELD_EVEN_LOW) - cfg |= CSI_IF_CFG_FIELD_POSITIVE; - - if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) - cfg |= CSI_IF_CFG_CLK_POL_FALLING_EDGE; - break; - default: - dev_warn(csi_dev->dev, "Unsupported bus type: %d\n", - endpoint->bus_type); - break; - } - - switch (bus_width) { - case 8: - cfg |= CSI_IF_CFG_IF_DATA_WIDTH_8BIT; - break; - case 10: - cfg |= CSI_IF_CFG_IF_DATA_WIDTH_10BIT; - break; - case 12: - cfg |= CSI_IF_CFG_IF_DATA_WIDTH_12BIT; - break; - case 16: /* No need to configure DATA_WIDTH for 16bit */ - break; - default: - dev_warn(csi_dev->dev, "Unsupported bus width: %u\n", bus_width); - break; - } - - regmap_write(csi_dev->regmap, CSI_IF_CFG_REG, cfg); -} - -static void sun6i_csi_set_format(struct sun6i_csi_device *csi_dev) -{ - struct sun6i_csi_config *config = &csi_dev->config; - u32 cfg; - u32 val; - - regmap_read(csi_dev->regmap, CSI_CH_CFG_REG, &cfg); - - cfg &= ~(CSI_CH_CFG_INPUT_FMT_MASK | - CSI_CH_CFG_OUTPUT_FMT_MASK | CSI_CH_CFG_VFLIP_EN | - CSI_CH_CFG_HFLIP_EN | CSI_CH_CFG_FIELD_SEL_MASK | - CSI_CH_CFG_INPUT_SEQ_MASK); - - val = get_csi_input_format(csi_dev, config->code, - config->pixelformat); - cfg |= CSI_CH_CFG_INPUT_FMT(val); - - val = get_csi_output_format(csi_dev, config->pixelformat, - config->field); - cfg |= CSI_CH_CFG_OUTPUT_FMT(val); - - val = get_csi_input_seq(csi_dev, config->code, - config->pixelformat); - cfg |= CSI_CH_CFG_INPUT_SEQ(val); - - if (config->field == V4L2_FIELD_TOP) - cfg |= CSI_CH_CFG_FIELD_SEL_FIELD0; - else if (config->field == V4L2_FIELD_BOTTOM) - cfg |= CSI_CH_CFG_FIELD_SEL_FIELD1; - else - cfg |= CSI_CH_CFG_FIELD_SEL_BOTH; - - regmap_write(csi_dev->regmap, CSI_CH_CFG_REG, cfg); -} - -static void sun6i_csi_set_window(struct sun6i_csi_device *csi_dev) -{ - struct sun6i_csi_config *config = &csi_dev->config; - u32 bytesperline_y; - u32 bytesperline_c; - int *planar_offset = csi_dev->planar_offset; - u32 width = config->width; - u32 height = config->height; - u32 hor_len = width; - - switch (config->pixelformat) { - case V4L2_PIX_FMT_YUYV: - case V4L2_PIX_FMT_YVYU: - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_VYUY: - dev_dbg(csi_dev->dev, - "Horizontal length should be 2 times of width for packed YUV formats!\n"); - hor_len = width * 2; - break; - default: - break; - } - - regmap_write(csi_dev->regmap, CSI_CH_HSIZE_REG, - CSI_CH_HSIZE_HOR_LEN(hor_len) | - CSI_CH_HSIZE_HOR_START(0)); - regmap_write(csi_dev->regmap, CSI_CH_VSIZE_REG, - CSI_CH_VSIZE_VER_LEN(height) | - CSI_CH_VSIZE_VER_START(0)); - - planar_offset[0] = 0; - switch (config->pixelformat) { - case V4L2_PIX_FMT_NV12_16L16: - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21: - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV61: - bytesperline_y = width; - bytesperline_c = width; - planar_offset[1] = bytesperline_y * height; - planar_offset[2] = -1; - break; - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_YVU420: - bytesperline_y = width; - bytesperline_c = width / 2; - planar_offset[1] = bytesperline_y * height; - planar_offset[2] = planar_offset[1] + - bytesperline_c * height / 2; - break; - case V4L2_PIX_FMT_YUV422P: - bytesperline_y = width; - bytesperline_c = width / 2; - planar_offset[1] = bytesperline_y * height; - planar_offset[2] = planar_offset[1] + - bytesperline_c * height; - break; - default: /* raw */ - dev_dbg(csi_dev->dev, - "Calculating pixelformat(0x%x)'s bytesperline as a packed format\n", - config->pixelformat); - bytesperline_y = (sun6i_csi_get_bpp(config->pixelformat) * - config->width) / 8; - bytesperline_c = 0; - planar_offset[1] = -1; - planar_offset[2] = -1; - break; } - regmap_write(csi_dev->regmap, CSI_CH_BUF_LEN_REG, - CSI_CH_BUF_LEN_BUF_LEN_C(bytesperline_c) | - CSI_CH_BUF_LEN_BUF_LEN_Y(bytesperline_y)); -} - -int sun6i_csi_update_config(struct sun6i_csi_device *csi_dev, - struct sun6i_csi_config *config) -{ - if (!config) - return -EINVAL; - - memcpy(&csi_dev->config, config, sizeof(csi_dev->config)); - - sun6i_csi_setup_bus(csi_dev); - sun6i_csi_set_format(csi_dev); - sun6i_csi_set_window(csi_dev); + csi_dev->isp_available = true; return 0; } -void sun6i_csi_update_buf_addr(struct sun6i_csi_device *csi_dev, - dma_addr_t addr) -{ - regmap_write(csi_dev->regmap, CSI_CH_F0_BUFA_REG, - (addr + csi_dev->planar_offset[0]) >> 2); - if (csi_dev->planar_offset[1] != -1) - regmap_write(csi_dev->regmap, CSI_CH_F1_BUFA_REG, - (addr + csi_dev->planar_offset[1]) >> 2); - if (csi_dev->planar_offset[2] != -1) - regmap_write(csi_dev->regmap, CSI_CH_F2_BUFA_REG, - (addr + csi_dev->planar_offset[2]) >> 2); -} - -void sun6i_csi_set_stream(struct sun6i_csi_device *csi_dev, bool enable) -{ - struct regmap *regmap = csi_dev->regmap; - - if (!enable) { - regmap_update_bits(regmap, CSI_CAP_REG, CSI_CAP_CH0_VCAP_ON, 0); - regmap_write(regmap, CSI_CH_INT_EN_REG, 0); - return; - } - - regmap_write(regmap, CSI_CH_INT_STA_REG, 0xFF); - regmap_write(regmap, CSI_CH_INT_EN_REG, - CSI_CH_INT_EN_HB_OF_INT_EN | - CSI_CH_INT_EN_FIFO2_OF_INT_EN | - CSI_CH_INT_EN_FIFO1_OF_INT_EN | - CSI_CH_INT_EN_FIFO0_OF_INT_EN | - CSI_CH_INT_EN_FD_INT_EN | - CSI_CH_INT_EN_CD_INT_EN); - - regmap_update_bits(regmap, CSI_CAP_REG, CSI_CAP_CH0_VCAP_ON, - CSI_CAP_CH0_VCAP_ON); -} - /* Media */ static const struct media_device_ops sun6i_csi_media_ops = { @@ -583,103 +74,11 @@ static const struct media_device_ops sun6i_csi_media_ops = { /* V4L2 */ -static int sun6i_csi_link_entity(struct sun6i_csi_device *csi_dev, - struct media_entity *entity, - struct fwnode_handle *fwnode) -{ - struct media_entity *sink; - struct media_pad *sink_pad; - int src_pad_index; - int ret; - - ret = media_entity_get_fwnode_pad(entity, fwnode, MEDIA_PAD_FL_SOURCE); - if (ret < 0) { - dev_err(csi_dev->dev, - "%s: no source pad in external entity %s\n", __func__, - entity->name); - return -EINVAL; - } - - src_pad_index = ret; - - sink = &csi_dev->video.video_dev.entity; - sink_pad = &csi_dev->video.pad; - - dev_dbg(csi_dev->dev, "creating %s:%u -> %s:%u link\n", - entity->name, src_pad_index, sink->name, sink_pad->index); - ret = media_create_pad_link(entity, src_pad_index, sink, - sink_pad->index, - MEDIA_LNK_FL_ENABLED | - MEDIA_LNK_FL_IMMUTABLE); - if (ret < 0) { - dev_err(csi_dev->dev, "failed to create %s:%u -> %s:%u link\n", - entity->name, src_pad_index, - sink->name, sink_pad->index); - return ret; - } - - return 0; -} - -static int sun6i_subdev_notify_complete(struct v4l2_async_notifier *notifier) -{ - struct sun6i_csi_device *csi_dev = - container_of(notifier, struct sun6i_csi_device, - v4l2.notifier); - struct sun6i_csi_v4l2 *v4l2 = &csi_dev->v4l2; - struct v4l2_device *v4l2_dev = &v4l2->v4l2_dev; - struct v4l2_subdev *sd; - int ret; - - dev_dbg(csi_dev->dev, "notify complete, all subdevs registered\n"); - - sd = list_first_entry(&v4l2_dev->subdevs, struct v4l2_subdev, list); - if (!sd) - return -EINVAL; - - ret = sun6i_csi_link_entity(csi_dev, &sd->entity, sd->fwnode); - if (ret < 0) - return ret; - - ret = v4l2_device_register_subdev_nodes(v4l2_dev); - if (ret < 0) - return ret; - - return 0; -} - -static const struct v4l2_async_notifier_operations sun6i_csi_async_ops = { - .complete = sun6i_subdev_notify_complete, -}; - -static int sun6i_csi_fwnode_parse(struct device *dev, - struct v4l2_fwnode_endpoint *vep, - struct v4l2_async_subdev *asd) -{ - struct sun6i_csi_device *csi_dev = dev_get_drvdata(dev); - - if (vep->base.port || vep->base.id) { - dev_warn(dev, "Only support a single port with one endpoint\n"); - return -ENOTCONN; - } - - switch (vep->bus_type) { - case V4L2_MBUS_PARALLEL: - case V4L2_MBUS_BT656: - csi_dev->v4l2.v4l2_ep = *vep; - return 0; - default: - dev_err(dev, "Unsupported media bus type\n"); - return -ENOTCONN; - } -} - static int sun6i_csi_v4l2_setup(struct sun6i_csi_device *csi_dev) { struct sun6i_csi_v4l2 *v4l2 = &csi_dev->v4l2; struct media_device *media_dev = &v4l2->media_dev; struct v4l2_device *v4l2_dev = &v4l2->v4l2_dev; - struct v4l2_async_notifier *notifier = &v4l2->notifier; struct device *dev = csi_dev->dev; int ret; @@ -709,42 +108,11 @@ static int sun6i_csi_v4l2_setup(struct sun6i_csi_device *csi_dev) goto error_media; } - /* Video */ - - ret = sun6i_video_setup(csi_dev); - if (ret) - goto error_v4l2_device; - - /* V4L2 Async */ - - v4l2_async_nf_init(notifier); - notifier->ops = &sun6i_csi_async_ops; - - ret = v4l2_async_nf_parse_fwnode_endpoints(dev, notifier, - sizeof(struct - v4l2_async_subdev), - sun6i_csi_fwnode_parse); - if (ret) - goto error_video; - - ret = v4l2_async_nf_register(v4l2_dev, notifier); - if (ret) { - dev_err(dev, "failed to register v4l2 async notifier: %d\n", - ret); - goto error_v4l2_async_notifier; - } + csi_dev->v4l2_dev = v4l2_dev; + csi_dev->media_dev = media_dev; return 0; -error_v4l2_async_notifier: - v4l2_async_nf_cleanup(notifier); - -error_video: - sun6i_video_cleanup(csi_dev); - -error_v4l2_device: - v4l2_device_unregister(&v4l2->v4l2_dev); - error_media: media_device_unregister(media_dev); media_device_cleanup(media_dev); @@ -757,9 +125,6 @@ static void sun6i_csi_v4l2_cleanup(struct sun6i_csi_device *csi_dev) struct sun6i_csi_v4l2 *v4l2 = &csi_dev->v4l2; media_device_unregister(&v4l2->media_dev); - v4l2_async_nf_unregister(&v4l2->notifier); - v4l2_async_nf_cleanup(&v4l2->notifier); - sun6i_video_cleanup(csi_dev); v4l2_device_unregister(&v4l2->v4l2_dev); media_device_cleanup(&v4l2->media_dev); } @@ -769,29 +134,39 @@ static void sun6i_csi_v4l2_cleanup(struct sun6i_csi_device *csi_dev) static irqreturn_t sun6i_csi_interrupt(int irq, void *private) { struct sun6i_csi_device *csi_dev = private; + bool capture_streaming = csi_dev->capture.state.streaming; struct regmap *regmap = csi_dev->regmap; - u32 status; + u32 status = 0, enable = 0; - regmap_read(regmap, CSI_CH_INT_STA_REG, &status); + regmap_read(regmap, SUN6I_CSI_CH_INT_STA_REG, &status); + regmap_read(regmap, SUN6I_CSI_CH_INT_EN_REG, &enable); - if (!(status & 0xFF)) + if (!status) return IRQ_NONE; - - if ((status & CSI_CH_INT_STA_FIFO0_OF_PD) || - (status & CSI_CH_INT_STA_FIFO1_OF_PD) || - (status & CSI_CH_INT_STA_FIFO2_OF_PD) || - (status & CSI_CH_INT_STA_HB_OF_PD)) { - regmap_write(regmap, CSI_CH_INT_STA_REG, status); - regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, 0); - regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, - CSI_EN_CSI_EN); + else if (!(status & enable) || !capture_streaming) + goto complete; + + if ((status & SUN6I_CSI_CH_INT_STA_FIFO0_OF) || + (status & SUN6I_CSI_CH_INT_STA_FIFO1_OF) || + (status & SUN6I_CSI_CH_INT_STA_FIFO2_OF) || + (status & SUN6I_CSI_CH_INT_STA_HB_OF)) { + regmap_write(regmap, SUN6I_CSI_CH_INT_STA_REG, status); + + regmap_update_bits(regmap, SUN6I_CSI_EN_REG, + SUN6I_CSI_EN_CSI_EN, 0); + regmap_update_bits(regmap, SUN6I_CSI_EN_REG, + SUN6I_CSI_EN_CSI_EN, SUN6I_CSI_EN_CSI_EN); return IRQ_HANDLED; } - if (status & CSI_CH_INT_STA_FD_PD) - sun6i_video_frame_done(csi_dev); + if (status & SUN6I_CSI_CH_INT_STA_FD) + sun6i_csi_capture_frame_done(csi_dev); + + if (status & SUN6I_CSI_CH_INT_STA_VS) + sun6i_csi_capture_sync(csi_dev); - regmap_write(regmap, CSI_CH_INT_STA_REG, status); +complete: + regmap_write(regmap, SUN6I_CSI_CH_INT_STA_REG, status); return IRQ_HANDLED; } @@ -917,8 +292,8 @@ static int sun6i_csi_resources_setup(struct sun6i_csi_device *csi_dev, goto error_clock_rate_exclusive; } - ret = devm_request_irq(dev, irq, sun6i_csi_interrupt, 0, SUN6I_CSI_NAME, - csi_dev); + ret = devm_request_irq(dev, irq, sun6i_csi_interrupt, IRQF_SHARED, + SUN6I_CSI_NAME, csi_dev); if (ret) { dev_err(dev, "failed to request interrupt\n"); goto error_clock_rate_exclusive; @@ -959,12 +334,41 @@ static int sun6i_csi_probe(struct platform_device *platform_dev) if (ret) return ret; - ret = sun6i_csi_v4l2_setup(csi_dev); + ret = sun6i_csi_isp_detect(csi_dev); if (ret) goto error_resources; + /* + * Register our own v4l2 and media devices when there is no ISP around. + * Otherwise the ISP will use async subdev registration with our bridge, + * which will provide v4l2 and media devices that are used to register + * the video interface. + */ + if (!csi_dev->isp_available) { + ret = sun6i_csi_v4l2_setup(csi_dev); + if (ret) + goto error_resources; + } + + ret = sun6i_csi_bridge_setup(csi_dev); + if (ret) + goto error_v4l2; + + if (!csi_dev->isp_available) { + ret = sun6i_csi_capture_setup(csi_dev); + if (ret) + goto error_bridge; + } + return 0; +error_bridge: + sun6i_csi_bridge_cleanup(csi_dev); + +error_v4l2: + if (!csi_dev->isp_available) + sun6i_csi_v4l2_cleanup(csi_dev); + error_resources: sun6i_csi_resources_cleanup(csi_dev); @@ -975,7 +379,12 @@ static int sun6i_csi_remove(struct platform_device *pdev) { struct sun6i_csi_device *csi_dev = platform_get_drvdata(pdev); - sun6i_csi_v4l2_cleanup(csi_dev); + sun6i_csi_capture_cleanup(csi_dev); + sun6i_csi_bridge_cleanup(csi_dev); + + if (!csi_dev->isp_available) + sun6i_csi_v4l2_cleanup(csi_dev); + sun6i_csi_resources_cleanup(csi_dev); return 0; @@ -1029,4 +438,5 @@ module_platform_driver(sun6i_csi_platform_driver); MODULE_DESCRIPTION("Allwinner A31 Camera Sensor Interface driver"); MODULE_AUTHOR("Yong Deng <[email protected]>"); +MODULE_AUTHOR("Paul Kocialkowski <[email protected]>"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h index bab705678280..bc3f0dae35df 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h @@ -1,166 +1,63 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /* * Copyright (c) 2011-2018 Magewell Electronics Co., Ltd. (Nanjing) - * All rights reserved. * Author: Yong Deng <[email protected]> + * Copyright 2021-2022 Bootlin + * Author: Paul Kocialkowski <[email protected]> */ -#ifndef __SUN6I_CSI_H__ -#define __SUN6I_CSI_H__ +#ifndef _SUN6I_CSI_H_ +#define _SUN6I_CSI_H_ #include <media/v4l2-device.h> -#include <media/v4l2-fwnode.h> #include <media/videobuf2-v4l2.h> -#include "sun6i_video.h" +#include "sun6i_csi_bridge.h" +#include "sun6i_csi_capture.h" #define SUN6I_CSI_NAME "sun6i-csi" #define SUN6I_CSI_DESCRIPTION "Allwinner A31 CSI Device" +enum sun6i_csi_port { + SUN6I_CSI_PORT_PARALLEL = 0, + SUN6I_CSI_PORT_MIPI_CSI2 = 1, + SUN6I_CSI_PORT_ISP = 2, +}; + struct sun6i_csi_buffer { struct vb2_v4l2_buffer v4l2_buffer; struct list_head list; - - dma_addr_t dma_addr; - bool queued_to_csi; -}; - -/** - * struct sun6i_csi_config - configs for sun6i csi - * @pixelformat: v4l2 pixel format (V4L2_PIX_FMT_*) - * @code: media bus format code (MEDIA_BUS_FMT_*) - * @field: used interlacing type (enum v4l2_field) - * @width: frame width - * @height: frame height - */ -struct sun6i_csi_config { - u32 pixelformat; - u32 code; - u32 field; - u32 width; - u32 height; }; struct sun6i_csi_v4l2 { struct v4l2_device v4l2_dev; struct media_device media_dev; - - struct v4l2_async_notifier notifier; - /* video port settings */ - struct v4l2_fwnode_endpoint v4l2_ep; }; struct sun6i_csi_device { struct device *dev; + struct v4l2_device *v4l2_dev; + struct media_device *media_dev; - struct sun6i_csi_config config; struct sun6i_csi_v4l2 v4l2; - struct sun6i_video video; + struct sun6i_csi_bridge bridge; + struct sun6i_csi_capture capture; struct regmap *regmap; struct clk *clock_mod; struct clk *clock_ram; struct reset_control *reset; - int planar_offset[3]; + bool isp_available; }; struct sun6i_csi_variant { unsigned long clock_mod_rate; }; -/** - * sun6i_csi_is_format_supported() - check if the format supported by csi - * @csi_dev: pointer to the csi device - * @pixformat: v4l2 pixel format (V4L2_PIX_FMT_*) - * @mbus_code: media bus format code (MEDIA_BUS_FMT_*) - * - * Return: true if format is supported, false otherwise. - */ -bool sun6i_csi_is_format_supported(struct sun6i_csi_device *csi_dev, - u32 pixformat, u32 mbus_code); - -/** - * sun6i_csi_set_power() - power on/off the csi - * @csi_dev: pointer to the csi device - * @enable: on/off - * - * Return: 0 if successful, error code otherwise. - */ -int sun6i_csi_set_power(struct sun6i_csi_device *csi_dev, bool enable); - -/** - * sun6i_csi_update_config() - update the csi register settings - * @csi_dev: pointer to the csi device - * @config: see struct sun6i_csi_config - * - * Return: 0 if successful, error code otherwise. - */ -int sun6i_csi_update_config(struct sun6i_csi_device *csi_dev, - struct sun6i_csi_config *config); - -/** - * sun6i_csi_update_buf_addr() - update the csi frame buffer address - * @csi_dev: pointer to the csi device - * @addr: frame buffer's physical address - */ -void sun6i_csi_update_buf_addr(struct sun6i_csi_device *csi_dev, - dma_addr_t addr); - -/** - * sun6i_csi_set_stream() - start/stop csi streaming - * @csi_dev: pointer to the csi device - * @enable: start/stop - */ -void sun6i_csi_set_stream(struct sun6i_csi_device *csi_dev, bool enable); - -/* get bpp form v4l2 pixformat */ -static inline int sun6i_csi_get_bpp(unsigned int pixformat) -{ - switch (pixformat) { - case V4L2_PIX_FMT_SBGGR8: - case V4L2_PIX_FMT_SGBRG8: - case V4L2_PIX_FMT_SGRBG8: - case V4L2_PIX_FMT_SRGGB8: - case V4L2_PIX_FMT_JPEG: - return 8; - case V4L2_PIX_FMT_SBGGR10: - case V4L2_PIX_FMT_SGBRG10: - case V4L2_PIX_FMT_SGRBG10: - case V4L2_PIX_FMT_SRGGB10: - return 10; - case V4L2_PIX_FMT_SBGGR12: - case V4L2_PIX_FMT_SGBRG12: - case V4L2_PIX_FMT_SGRBG12: - case V4L2_PIX_FMT_SRGGB12: - case V4L2_PIX_FMT_NV12_16L16: - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21: - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_YVU420: - return 12; - case V4L2_PIX_FMT_YUYV: - case V4L2_PIX_FMT_YVYU: - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_VYUY: - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV61: - case V4L2_PIX_FMT_YUV422P: - case V4L2_PIX_FMT_RGB565: - case V4L2_PIX_FMT_RGB565X: - return 16; - case V4L2_PIX_FMT_RGB24: - case V4L2_PIX_FMT_BGR24: - return 24; - case V4L2_PIX_FMT_RGB32: - case V4L2_PIX_FMT_BGR32: - return 32; - default: - WARN(1, "Unsupported pixformat: 0x%x\n", pixformat); - break; - } +/* ISP */ - return 0; -} +int sun6i_csi_isp_complete(struct sun6i_csi_device *csi_dev, + struct v4l2_device *v4l2_dev); -#endif /* __SUN6I_CSI_H__ */ +#endif diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c new file mode 100644 index 000000000000..86d20c1c35ed --- /dev/null +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c @@ -0,0 +1,868 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021-2022 Bootlin + * Author: Paul Kocialkowski <[email protected]> + */ + +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> + +#include "sun6i_csi.h" +#include "sun6i_csi_bridge.h" +#include "sun6i_csi_reg.h" + +/* Helpers */ + +void sun6i_csi_bridge_dimensions(struct sun6i_csi_device *csi_dev, + unsigned int *width, unsigned int *height) +{ + if (width) + *width = csi_dev->bridge.mbus_format.width; + if (height) + *height = csi_dev->bridge.mbus_format.height; +} + +void sun6i_csi_bridge_format(struct sun6i_csi_device *csi_dev, + u32 *mbus_code, u32 *field) +{ + if (mbus_code) + *mbus_code = csi_dev->bridge.mbus_format.code; + if (field) + *field = csi_dev->bridge.mbus_format.field; +} + +/* Format */ + +static const struct sun6i_csi_bridge_format sun6i_csi_bridge_formats[] = { + /* Bayer */ + { + .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, + .input_format = SUN6I_CSI_INPUT_FMT_RAW, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, + .input_format = SUN6I_CSI_INPUT_FMT_RAW, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, + .input_format = SUN6I_CSI_INPUT_FMT_RAW, + }, + { + .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, + .input_format = SUN6I_CSI_INPUT_FMT_RAW, + }, + { + .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, + .input_format = SUN6I_CSI_INPUT_FMT_RAW, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, + .input_format = SUN6I_CSI_INPUT_FMT_RAW, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, + .input_format = SUN6I_CSI_INPUT_FMT_RAW, + }, + { + .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, + .input_format = SUN6I_CSI_INPUT_FMT_RAW, + }, + { + .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12, + .input_format = SUN6I_CSI_INPUT_FMT_RAW, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12, + .input_format = SUN6I_CSI_INPUT_FMT_RAW, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, + .input_format = SUN6I_CSI_INPUT_FMT_RAW, + }, + { + .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12, + .input_format = SUN6I_CSI_INPUT_FMT_RAW, + }, + /* RGB */ + { + .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .input_format = SUN6I_CSI_INPUT_FMT_RAW, + }, + { + .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_BE, + .input_format = SUN6I_CSI_INPUT_FMT_RAW, + }, + /* YUV422 */ + { + .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, + .input_format = SUN6I_CSI_INPUT_FMT_YUV422, + .input_yuv_seq = SUN6I_CSI_INPUT_YUV_SEQ_YUYV, + .input_yuv_seq_invert = SUN6I_CSI_INPUT_YUV_SEQ_YVYU, + }, + { + .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, + .input_format = SUN6I_CSI_INPUT_FMT_YUV422, + .input_yuv_seq = SUN6I_CSI_INPUT_YUV_SEQ_UYVY, + .input_yuv_seq_invert = SUN6I_CSI_INPUT_YUV_SEQ_VYUY, + }, + { + .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8, + .input_format = SUN6I_CSI_INPUT_FMT_YUV422, + .input_yuv_seq = SUN6I_CSI_INPUT_YUV_SEQ_YVYU, + .input_yuv_seq_invert = SUN6I_CSI_INPUT_YUV_SEQ_YUYV, + }, + { + .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, + .input_format = SUN6I_CSI_INPUT_FMT_YUV422, + .input_yuv_seq = SUN6I_CSI_INPUT_YUV_SEQ_UYVY, + .input_yuv_seq_invert = SUN6I_CSI_INPUT_YUV_SEQ_VYUY, + }, + { + .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8, + .input_format = SUN6I_CSI_INPUT_FMT_YUV422, + .input_yuv_seq = SUN6I_CSI_INPUT_YUV_SEQ_VYUY, + .input_yuv_seq_invert = SUN6I_CSI_INPUT_YUV_SEQ_UYVY, + }, + { + .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16, + .input_format = SUN6I_CSI_INPUT_FMT_YUV422, + .input_yuv_seq = SUN6I_CSI_INPUT_YUV_SEQ_YUYV, + .input_yuv_seq_invert = SUN6I_CSI_INPUT_YUV_SEQ_YVYU, + }, + { + .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16, + .input_format = SUN6I_CSI_INPUT_FMT_YUV422, + .input_yuv_seq = SUN6I_CSI_INPUT_YUV_SEQ_UYVY, + .input_yuv_seq_invert = SUN6I_CSI_INPUT_YUV_SEQ_VYUY, + }, + { + .mbus_code = MEDIA_BUS_FMT_YVYU8_1X16, + .input_format = SUN6I_CSI_INPUT_FMT_YUV422, + .input_yuv_seq = SUN6I_CSI_INPUT_YUV_SEQ_YVYU, + .input_yuv_seq_invert = SUN6I_CSI_INPUT_YUV_SEQ_YUYV, + }, + { + .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16, + .input_format = SUN6I_CSI_INPUT_FMT_YUV422, + .input_yuv_seq = SUN6I_CSI_INPUT_YUV_SEQ_UYVY, + .input_yuv_seq_invert = SUN6I_CSI_INPUT_YUV_SEQ_VYUY, + }, + { + .mbus_code = MEDIA_BUS_FMT_VYUY8_1X16, + .input_format = SUN6I_CSI_INPUT_FMT_YUV422, + .input_yuv_seq = SUN6I_CSI_INPUT_YUV_SEQ_VYUY, + .input_yuv_seq_invert = SUN6I_CSI_INPUT_YUV_SEQ_UYVY, + }, + /* Compressed */ + { + .mbus_code = MEDIA_BUS_FMT_JPEG_1X8, + .input_format = SUN6I_CSI_INPUT_FMT_RAW, + }, +}; + +const struct sun6i_csi_bridge_format * +sun6i_csi_bridge_format_find(u32 mbus_code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(sun6i_csi_bridge_formats); i++) + if (sun6i_csi_bridge_formats[i].mbus_code == mbus_code) + return &sun6i_csi_bridge_formats[i]; + + return NULL; +} + +/* Bridge */ + +static void sun6i_csi_bridge_irq_enable(struct sun6i_csi_device *csi_dev) +{ + struct regmap *regmap = csi_dev->regmap; + + regmap_write(regmap, SUN6I_CSI_CH_INT_EN_REG, + SUN6I_CSI_CH_INT_EN_VS | + SUN6I_CSI_CH_INT_EN_HB_OF | + SUN6I_CSI_CH_INT_EN_FIFO2_OF | + SUN6I_CSI_CH_INT_EN_FIFO1_OF | + SUN6I_CSI_CH_INT_EN_FIFO0_OF | + SUN6I_CSI_CH_INT_EN_FD | + SUN6I_CSI_CH_INT_EN_CD); +} + +static void sun6i_csi_bridge_irq_disable(struct sun6i_csi_device *csi_dev) +{ + struct regmap *regmap = csi_dev->regmap; + + regmap_write(regmap, SUN6I_CSI_CH_INT_EN_REG, 0); +} + +static void sun6i_csi_bridge_irq_clear(struct sun6i_csi_device *csi_dev) +{ + struct regmap *regmap = csi_dev->regmap; + + regmap_write(regmap, SUN6I_CSI_CH_INT_EN_REG, 0); + regmap_write(regmap, SUN6I_CSI_CH_INT_STA_REG, + SUN6I_CSI_CH_INT_STA_CLEAR); +} + +static void sun6i_csi_bridge_enable(struct sun6i_csi_device *csi_dev) +{ + struct regmap *regmap = csi_dev->regmap; + + regmap_update_bits(regmap, SUN6I_CSI_EN_REG, SUN6I_CSI_EN_CSI_EN, + SUN6I_CSI_EN_CSI_EN); + + regmap_update_bits(regmap, SUN6I_CSI_CAP_REG, SUN6I_CSI_CAP_VCAP_ON, + SUN6I_CSI_CAP_VCAP_ON); +} + +static void sun6i_csi_bridge_disable(struct sun6i_csi_device *csi_dev) +{ + struct regmap *regmap = csi_dev->regmap; + + regmap_update_bits(regmap, SUN6I_CSI_CAP_REG, SUN6I_CSI_CAP_VCAP_ON, 0); + regmap_update_bits(regmap, SUN6I_CSI_EN_REG, SUN6I_CSI_EN_CSI_EN, 0); +} + +static void +sun6i_csi_bridge_configure_parallel(struct sun6i_csi_device *csi_dev) +{ + struct device *dev = csi_dev->dev; + struct regmap *regmap = csi_dev->regmap; + struct v4l2_fwnode_endpoint *endpoint = + &csi_dev->bridge.source_parallel.endpoint; + unsigned char bus_width = endpoint->bus.parallel.bus_width; + unsigned int flags = endpoint->bus.parallel.flags; + u32 field; + u32 value = SUN6I_CSI_IF_CFG_IF_CSI; + + sun6i_csi_bridge_format(csi_dev, NULL, &field); + + if (field == V4L2_FIELD_INTERLACED || + field == V4L2_FIELD_INTERLACED_TB || + field == V4L2_FIELD_INTERLACED_BT) + value |= SUN6I_CSI_IF_CFG_SRC_TYPE_INTERLACED | + SUN6I_CSI_IF_CFG_FIELD_DT_PCLK_SHIFT(1) | + SUN6I_CSI_IF_CFG_FIELD_DT_FIELD_VSYNC; + else + value |= SUN6I_CSI_IF_CFG_SRC_TYPE_PROGRESSIVE; + + switch (endpoint->bus_type) { + case V4L2_MBUS_PARALLEL: + if (bus_width == 16) + value |= SUN6I_CSI_IF_CFG_IF_CSI_YUV_COMBINED; + else + value |= SUN6I_CSI_IF_CFG_IF_CSI_YUV_RAW; + + if (flags & V4L2_MBUS_FIELD_EVEN_LOW) + value |= SUN6I_CSI_IF_CFG_FIELD_NEGATIVE; + else + value |= SUN6I_CSI_IF_CFG_FIELD_POSITIVE; + + if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) + value |= SUN6I_CSI_IF_CFG_VREF_POL_NEGATIVE; + else + value |= SUN6I_CSI_IF_CFG_VREF_POL_POSITIVE; + + if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) + value |= SUN6I_CSI_IF_CFG_HREF_POL_NEGATIVE; + else + value |= SUN6I_CSI_IF_CFG_HREF_POL_POSITIVE; + + if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING) + value |= SUN6I_CSI_IF_CFG_CLK_POL_RISING; + else + value |= SUN6I_CSI_IF_CFG_CLK_POL_FALLING; + break; + case V4L2_MBUS_BT656: + if (bus_width == 16) + value |= SUN6I_CSI_IF_CFG_IF_CSI_BT1120; + else + value |= SUN6I_CSI_IF_CFG_IF_CSI_BT656; + + if (flags & V4L2_MBUS_FIELD_EVEN_LOW) + value |= SUN6I_CSI_IF_CFG_FIELD_NEGATIVE; + else + value |= SUN6I_CSI_IF_CFG_FIELD_POSITIVE; + + if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) + value |= SUN6I_CSI_IF_CFG_CLK_POL_RISING; + else + value |= SUN6I_CSI_IF_CFG_CLK_POL_FALLING; + break; + default: + dev_warn(dev, "unsupported bus type: %d\n", endpoint->bus_type); + break; + } + + switch (bus_width) { + case 8: + /* 16-bit YUV formats use a doubled width in 8-bit mode. */ + case 16: + value |= SUN6I_CSI_IF_CFG_DATA_WIDTH_8; + break; + case 10: + value |= SUN6I_CSI_IF_CFG_DATA_WIDTH_10; + break; + case 12: + value |= SUN6I_CSI_IF_CFG_DATA_WIDTH_12; + break; + default: + dev_warn(dev, "unsupported bus width: %u\n", bus_width); + break; + } + + regmap_write(regmap, SUN6I_CSI_IF_CFG_REG, value); +} + +static void +sun6i_csi_bridge_configure_mipi_csi2(struct sun6i_csi_device *csi_dev) +{ + struct regmap *regmap = csi_dev->regmap; + u32 value = SUN6I_CSI_IF_CFG_IF_MIPI; + u32 field; + + sun6i_csi_bridge_format(csi_dev, NULL, &field); + + if (field == V4L2_FIELD_INTERLACED || + field == V4L2_FIELD_INTERLACED_TB || + field == V4L2_FIELD_INTERLACED_BT) + value |= SUN6I_CSI_IF_CFG_SRC_TYPE_INTERLACED; + else + value |= SUN6I_CSI_IF_CFG_SRC_TYPE_PROGRESSIVE; + + regmap_write(regmap, SUN6I_CSI_IF_CFG_REG, value); +} + +static void sun6i_csi_bridge_configure_format(struct sun6i_csi_device *csi_dev) +{ + struct regmap *regmap = csi_dev->regmap; + bool capture_streaming = csi_dev->capture.state.streaming; + const struct sun6i_csi_bridge_format *bridge_format; + const struct sun6i_csi_capture_format *capture_format; + u32 mbus_code, field, pixelformat; + u8 input_format, input_yuv_seq, output_format; + u32 value = 0; + + sun6i_csi_bridge_format(csi_dev, &mbus_code, &field); + + bridge_format = sun6i_csi_bridge_format_find(mbus_code); + if (WARN_ON(!bridge_format)) + return; + + input_format = bridge_format->input_format; + input_yuv_seq = bridge_format->input_yuv_seq; + + if (capture_streaming) { + sun6i_csi_capture_format(csi_dev, &pixelformat, NULL); + + capture_format = sun6i_csi_capture_format_find(pixelformat); + if (WARN_ON(!capture_format)) + return; + + if (capture_format->input_format_raw) + input_format = SUN6I_CSI_INPUT_FMT_RAW; + + if (capture_format->input_yuv_seq_invert) + input_yuv_seq = bridge_format->input_yuv_seq_invert; + + if (field == V4L2_FIELD_INTERLACED || + field == V4L2_FIELD_INTERLACED_TB || + field == V4L2_FIELD_INTERLACED_BT) + output_format = capture_format->output_format_field; + else + output_format = capture_format->output_format_frame; + + value |= SUN6I_CSI_CH_CFG_OUTPUT_FMT(output_format); + } + + value |= SUN6I_CSI_CH_CFG_INPUT_FMT(input_format); + value |= SUN6I_CSI_CH_CFG_INPUT_YUV_SEQ(input_yuv_seq); + + if (field == V4L2_FIELD_TOP) + value |= SUN6I_CSI_CH_CFG_FIELD_SEL_FIELD0; + else if (field == V4L2_FIELD_BOTTOM) + value |= SUN6I_CSI_CH_CFG_FIELD_SEL_FIELD1; + else + value |= SUN6I_CSI_CH_CFG_FIELD_SEL_EITHER; + + regmap_write(regmap, SUN6I_CSI_CH_CFG_REG, value); +} + +static void sun6i_csi_bridge_configure(struct sun6i_csi_device *csi_dev, + struct sun6i_csi_bridge_source *source) +{ + struct sun6i_csi_bridge *bridge = &csi_dev->bridge; + + if (source == &bridge->source_parallel) + sun6i_csi_bridge_configure_parallel(csi_dev); + else + sun6i_csi_bridge_configure_mipi_csi2(csi_dev); + + sun6i_csi_bridge_configure_format(csi_dev); +} + +/* V4L2 Subdev */ + +static int sun6i_csi_bridge_s_stream(struct v4l2_subdev *subdev, int on) +{ + struct sun6i_csi_device *csi_dev = v4l2_get_subdevdata(subdev); + struct sun6i_csi_bridge *bridge = &csi_dev->bridge; + struct media_pad *local_pad = &bridge->pads[SUN6I_CSI_BRIDGE_PAD_SINK]; + bool capture_streaming = csi_dev->capture.state.streaming; + struct device *dev = csi_dev->dev; + struct sun6i_csi_bridge_source *source; + struct v4l2_subdev *source_subdev; + struct media_pad *remote_pad; + /* Initialize to 0 to use both in disable label (ret != 0) and off. */ + int ret = 0; + + /* Source */ + + remote_pad = media_pad_remote_pad_unique(local_pad); + if (IS_ERR(remote_pad)) { + dev_err(dev, + "zero or more than a single source connected to the bridge\n"); + return PTR_ERR(remote_pad); + } + + source_subdev = media_entity_to_v4l2_subdev(remote_pad->entity); + + if (source_subdev == bridge->source_parallel.subdev) + source = &bridge->source_parallel; + else + source = &bridge->source_mipi_csi2; + + if (!on) { + v4l2_subdev_call(source_subdev, video, s_stream, 0); + goto disable; + } + + /* PM */ + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + /* Clear */ + + sun6i_csi_bridge_irq_clear(csi_dev); + + /* Configure */ + + sun6i_csi_bridge_configure(csi_dev, source); + + if (capture_streaming) + sun6i_csi_capture_configure(csi_dev); + + /* State Update */ + + if (capture_streaming) + sun6i_csi_capture_state_update(csi_dev); + + /* Enable */ + + if (capture_streaming) + sun6i_csi_bridge_irq_enable(csi_dev); + + sun6i_csi_bridge_enable(csi_dev); + + ret = v4l2_subdev_call(source_subdev, video, s_stream, 1); + if (ret && ret != -ENOIOCTLCMD) + goto disable; + + return 0; + +disable: + if (capture_streaming) + sun6i_csi_bridge_irq_disable(csi_dev); + + sun6i_csi_bridge_disable(csi_dev); + + pm_runtime_put(dev); + + return ret; +} + +static const struct v4l2_subdev_video_ops sun6i_csi_bridge_video_ops = { + .s_stream = sun6i_csi_bridge_s_stream, +}; + +static void +sun6i_csi_bridge_mbus_format_prepare(struct v4l2_mbus_framefmt *mbus_format) +{ + if (!sun6i_csi_bridge_format_find(mbus_format->code)) + mbus_format->code = sun6i_csi_bridge_formats[0].mbus_code; + + mbus_format->field = V4L2_FIELD_NONE; + mbus_format->colorspace = V4L2_COLORSPACE_RAW; + mbus_format->quantization = V4L2_QUANTIZATION_DEFAULT; + mbus_format->xfer_func = V4L2_XFER_FUNC_DEFAULT; +} + +static int sun6i_csi_bridge_init_cfg(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state) +{ + struct sun6i_csi_device *csi_dev = v4l2_get_subdevdata(subdev); + unsigned int pad = SUN6I_CSI_BRIDGE_PAD_SINK; + struct v4l2_mbus_framefmt *mbus_format = + v4l2_subdev_get_try_format(subdev, state, pad); + struct mutex *lock = &csi_dev->bridge.lock; + + mutex_lock(lock); + + mbus_format->code = sun6i_csi_bridge_formats[0].mbus_code; + mbus_format->width = 1280; + mbus_format->height = 720; + + sun6i_csi_bridge_mbus_format_prepare(mbus_format); + + mutex_unlock(lock); + + return 0; +} + +static int +sun6i_csi_bridge_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code_enum) +{ + if (code_enum->index >= ARRAY_SIZE(sun6i_csi_bridge_formats)) + return -EINVAL; + + code_enum->code = sun6i_csi_bridge_formats[code_enum->index].mbus_code; + + return 0; +} + +static int sun6i_csi_bridge_get_fmt(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct sun6i_csi_device *csi_dev = v4l2_get_subdevdata(subdev); + struct v4l2_mbus_framefmt *mbus_format = &format->format; + struct mutex *lock = &csi_dev->bridge.lock; + + mutex_lock(lock); + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) + *mbus_format = *v4l2_subdev_get_try_format(subdev, state, + format->pad); + else + *mbus_format = csi_dev->bridge.mbus_format; + + mutex_unlock(lock); + + return 0; +} + +static int sun6i_csi_bridge_set_fmt(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct sun6i_csi_device *csi_dev = v4l2_get_subdevdata(subdev); + struct v4l2_mbus_framefmt *mbus_format = &format->format; + struct mutex *lock = &csi_dev->bridge.lock; + + mutex_lock(lock); + + sun6i_csi_bridge_mbus_format_prepare(mbus_format); + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) + *v4l2_subdev_get_try_format(subdev, state, format->pad) = + *mbus_format; + else + csi_dev->bridge.mbus_format = *mbus_format; + + mutex_unlock(lock); + + return 0; +} + +static const struct v4l2_subdev_pad_ops sun6i_csi_bridge_pad_ops = { + .init_cfg = sun6i_csi_bridge_init_cfg, + .enum_mbus_code = sun6i_csi_bridge_enum_mbus_code, + .get_fmt = sun6i_csi_bridge_get_fmt, + .set_fmt = sun6i_csi_bridge_set_fmt, +}; + +const struct v4l2_subdev_ops sun6i_csi_bridge_subdev_ops = { + .video = &sun6i_csi_bridge_video_ops, + .pad = &sun6i_csi_bridge_pad_ops, +}; + +/* Media Entity */ + +static const struct media_entity_operations sun6i_csi_bridge_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +/* V4L2 Async */ + +static int sun6i_csi_bridge_link(struct sun6i_csi_device *csi_dev, + int sink_pad_index, + struct v4l2_subdev *remote_subdev, + bool enabled) +{ + struct device *dev = csi_dev->dev; + struct v4l2_subdev *subdev = &csi_dev->bridge.subdev; + struct media_entity *sink_entity = &subdev->entity; + struct media_entity *source_entity = &remote_subdev->entity; + int source_pad_index; + int ret; + + /* Get the first remote source pad. */ + ret = media_entity_get_fwnode_pad(source_entity, remote_subdev->fwnode, + MEDIA_PAD_FL_SOURCE); + if (ret < 0) { + dev_err(dev, "missing source pad in external entity %s\n", + source_entity->name); + return -EINVAL; + } + + source_pad_index = ret; + + dev_dbg(dev, "creating %s:%u -> %s:%u link\n", source_entity->name, + source_pad_index, sink_entity->name, sink_pad_index); + + ret = media_create_pad_link(source_entity, source_pad_index, + sink_entity, sink_pad_index, + enabled ? MEDIA_LNK_FL_ENABLED : 0); + if (ret < 0) { + dev_err(dev, "failed to create %s:%u -> %s:%u link\n", + source_entity->name, source_pad_index, + sink_entity->name, sink_pad_index); + return ret; + } + + return 0; +} + +static int +sun6i_csi_bridge_notifier_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *remote_subdev, + struct v4l2_async_subdev *async_subdev) +{ + struct sun6i_csi_device *csi_dev = + container_of(notifier, struct sun6i_csi_device, + bridge.notifier); + struct sun6i_csi_bridge_async_subdev *bridge_async_subdev = + container_of(async_subdev, struct sun6i_csi_bridge_async_subdev, + async_subdev); + struct sun6i_csi_bridge *bridge = &csi_dev->bridge; + struct sun6i_csi_bridge_source *source = bridge_async_subdev->source; + bool enabled; + int ret; + + switch (source->endpoint.base.port) { + case SUN6I_CSI_PORT_PARALLEL: + enabled = true; + break; + case SUN6I_CSI_PORT_MIPI_CSI2: + enabled = !bridge->source_parallel.expected; + break; + default: + break; + } + + source->subdev = remote_subdev; + + if (csi_dev->isp_available) { + /* + * Hook to the first available remote subdev to get v4l2 and + * media devices and register the capture device then. + */ + ret = sun6i_csi_isp_complete(csi_dev, remote_subdev->v4l2_dev); + if (ret) + return ret; + } + + return sun6i_csi_bridge_link(csi_dev, SUN6I_CSI_BRIDGE_PAD_SINK, + remote_subdev, enabled); +} + +static int +sun6i_csi_bridge_notifier_complete(struct v4l2_async_notifier *notifier) +{ + struct sun6i_csi_device *csi_dev = + container_of(notifier, struct sun6i_csi_device, + bridge.notifier); + struct v4l2_device *v4l2_dev = &csi_dev->v4l2.v4l2_dev; + + if (csi_dev->isp_available) + return 0; + + return v4l2_device_register_subdev_nodes(v4l2_dev); +} + +static const struct v4l2_async_notifier_operations +sun6i_csi_bridge_notifier_ops = { + .bound = sun6i_csi_bridge_notifier_bound, + .complete = sun6i_csi_bridge_notifier_complete, +}; + +/* Bridge */ + +static int sun6i_csi_bridge_source_setup(struct sun6i_csi_device *csi_dev, + struct sun6i_csi_bridge_source *source, + u32 port, + enum v4l2_mbus_type *bus_types) +{ + struct device *dev = csi_dev->dev; + struct v4l2_async_notifier *notifier = &csi_dev->bridge.notifier; + struct v4l2_fwnode_endpoint *endpoint = &source->endpoint; + struct sun6i_csi_bridge_async_subdev *bridge_async_subdev; + struct fwnode_handle *handle; + int ret; + + handle = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), port, 0, 0); + if (!handle) + return -ENODEV; + + ret = v4l2_fwnode_endpoint_parse(handle, endpoint); + if (ret) + goto complete; + + if (bus_types) { + bool valid = false; + unsigned int i; + + for (i = 0; bus_types[i] != V4L2_MBUS_INVALID; i++) { + if (endpoint->bus_type == bus_types[i]) { + valid = true; + break; + } + } + + if (!valid) { + dev_err(dev, "unsupported bus type for port %d\n", + port); + ret = -EINVAL; + goto complete; + } + } + + bridge_async_subdev = + v4l2_async_nf_add_fwnode_remote(notifier, handle, + struct + sun6i_csi_bridge_async_subdev); + if (IS_ERR(bridge_async_subdev)) { + ret = PTR_ERR(bridge_async_subdev); + goto complete; + } + + bridge_async_subdev->source = source; + + source->expected = true; + +complete: + fwnode_handle_put(handle); + + return ret; +} + +int sun6i_csi_bridge_setup(struct sun6i_csi_device *csi_dev) +{ + struct device *dev = csi_dev->dev; + struct sun6i_csi_bridge *bridge = &csi_dev->bridge; + struct v4l2_device *v4l2_dev = csi_dev->v4l2_dev; + struct v4l2_subdev *subdev = &bridge->subdev; + struct v4l2_async_notifier *notifier = &bridge->notifier; + struct media_pad *pads = bridge->pads; + enum v4l2_mbus_type parallel_mbus_types[] = { + V4L2_MBUS_PARALLEL, + V4L2_MBUS_BT656, + V4L2_MBUS_INVALID + }; + int ret; + + mutex_init(&bridge->lock); + + /* V4L2 Subdev */ + + v4l2_subdev_init(subdev, &sun6i_csi_bridge_subdev_ops); + strscpy(subdev->name, SUN6I_CSI_BRIDGE_NAME, sizeof(subdev->name)); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + subdev->owner = THIS_MODULE; + subdev->dev = dev; + + v4l2_set_subdevdata(subdev, csi_dev); + + /* Media Entity */ + + subdev->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + subdev->entity.ops = &sun6i_csi_bridge_entity_ops; + + /* Media Pads */ + + pads[SUN6I_CSI_BRIDGE_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[SUN6I_CSI_BRIDGE_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE | + MEDIA_PAD_FL_MUST_CONNECT; + + ret = media_entity_pads_init(&subdev->entity, + SUN6I_CSI_BRIDGE_PAD_COUNT, pads); + if (ret < 0) + return ret; + + /* V4L2 Subdev */ + + if (csi_dev->isp_available) + ret = v4l2_async_register_subdev(subdev); + else + ret = v4l2_device_register_subdev(v4l2_dev, subdev); + + if (ret) { + dev_err(dev, "failed to register v4l2 subdev: %d\n", ret); + goto error_media_entity; + } + + /* V4L2 Async */ + + v4l2_async_nf_init(notifier); + notifier->ops = &sun6i_csi_bridge_notifier_ops; + + sun6i_csi_bridge_source_setup(csi_dev, &bridge->source_parallel, + SUN6I_CSI_PORT_PARALLEL, + parallel_mbus_types); + sun6i_csi_bridge_source_setup(csi_dev, &bridge->source_mipi_csi2, + SUN6I_CSI_PORT_MIPI_CSI2, NULL); + + if (csi_dev->isp_available) + ret = v4l2_async_subdev_nf_register(subdev, notifier); + else + ret = v4l2_async_nf_register(v4l2_dev, notifier); + if (ret) { + dev_err(dev, "failed to register v4l2 async notifier: %d\n", + ret); + goto error_v4l2_async_notifier; + } + + return 0; + +error_v4l2_async_notifier: + v4l2_async_nf_cleanup(notifier); + + if (csi_dev->isp_available) + v4l2_async_unregister_subdev(subdev); + else + v4l2_device_unregister_subdev(subdev); + +error_media_entity: + media_entity_cleanup(&subdev->entity); + + return ret; +} + +void sun6i_csi_bridge_cleanup(struct sun6i_csi_device *csi_dev) +{ + struct v4l2_subdev *subdev = &csi_dev->bridge.subdev; + struct v4l2_async_notifier *notifier = &csi_dev->bridge.notifier; + + v4l2_async_nf_unregister(notifier); + v4l2_async_nf_cleanup(notifier); + + v4l2_device_unregister_subdev(subdev); + + media_entity_cleanup(&subdev->entity); +} diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h new file mode 100644 index 000000000000..ee592a14b9c5 --- /dev/null +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2021-2022 Bootlin + * Author: Paul Kocialkowski <[email protected]> + */ + +#ifndef _SUN6I_CSI_BRIDGE_H_ +#define _SUN6I_CSI_BRIDGE_H_ + +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> + +#define SUN6I_CSI_BRIDGE_NAME "sun6i-csi-bridge" + +enum sun6i_csi_bridge_pad { + SUN6I_CSI_BRIDGE_PAD_SINK = 0, + SUN6I_CSI_BRIDGE_PAD_SOURCE = 1, + SUN6I_CSI_BRIDGE_PAD_COUNT = 2, +}; + +struct sun6i_csi_device; + +struct sun6i_csi_bridge_format { + u32 mbus_code; + u8 input_format; + u8 input_yuv_seq; + u8 input_yuv_seq_invert; +}; + +struct sun6i_csi_bridge_source { + struct v4l2_subdev *subdev; + struct v4l2_fwnode_endpoint endpoint; + bool expected; +}; + +struct sun6i_csi_bridge_async_subdev { + struct v4l2_async_subdev async_subdev; + struct sun6i_csi_bridge_source *source; +}; + +struct sun6i_csi_bridge { + struct v4l2_subdev subdev; + struct v4l2_async_notifier notifier; + struct media_pad pads[2]; + struct v4l2_mbus_framefmt mbus_format; + struct mutex lock; /* Mbus format lock. */ + + struct sun6i_csi_bridge_source source_parallel; + struct sun6i_csi_bridge_source source_mipi_csi2; +}; + +/* Helpers */ + +void sun6i_csi_bridge_dimensions(struct sun6i_csi_device *csi_dev, + unsigned int *width, unsigned int *height); +void sun6i_csi_bridge_format(struct sun6i_csi_device *csi_dev, + u32 *mbus_code, u32 *field); + +/* Format */ + +const struct sun6i_csi_bridge_format * +sun6i_csi_bridge_format_find(u32 mbus_code); + +/* Bridge */ + +int sun6i_csi_bridge_setup(struct sun6i_csi_device *csi_dev); +void sun6i_csi_bridge_cleanup(struct sun6i_csi_device *csi_dev); + +#endif diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c new file mode 100644 index 000000000000..6d34f5c0768f --- /dev/null +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c @@ -0,0 +1,1102 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2011-2018 Magewell Electronics Co., Ltd. (Nanjing) + * Author: Yong Deng <[email protected]> + * Copyright 2021-2022 Bootlin + * Author: Paul Kocialkowski <[email protected]> + */ + +#include <linux/of.h> +#include <linux/regmap.h> +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-mc.h> +#include <media/videobuf2-dma-contig.h> +#include <media/videobuf2-v4l2.h> + +#include "sun6i_csi.h" +#include "sun6i_csi_bridge.h" +#include "sun6i_csi_capture.h" +#include "sun6i_csi_reg.h" + +/* Helpers */ + +void sun6i_csi_capture_dimensions(struct sun6i_csi_device *csi_dev, + unsigned int *width, unsigned int *height) +{ + if (width) + *width = csi_dev->capture.format.fmt.pix.width; + if (height) + *height = csi_dev->capture.format.fmt.pix.height; +} + +void sun6i_csi_capture_format(struct sun6i_csi_device *csi_dev, + u32 *pixelformat, u32 *field) +{ + if (pixelformat) + *pixelformat = csi_dev->capture.format.fmt.pix.pixelformat; + + if (field) + *field = csi_dev->capture.format.fmt.pix.field; +} + +/* Format */ + +static const struct sun6i_csi_capture_format sun6i_csi_capture_formats[] = { + /* Bayer */ + { + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8, + }, + { + .pixelformat = V4L2_PIX_FMT_SGBRG8, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8, + }, + { + .pixelformat = V4L2_PIX_FMT_SGRBG8, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8, + }, + { + .pixelformat = V4L2_PIX_FMT_SRGGB8, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8, + }, + { + .pixelformat = V4L2_PIX_FMT_SBGGR10, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_10, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_10, + }, + { + .pixelformat = V4L2_PIX_FMT_SGBRG10, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_10, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_10, + }, + { + .pixelformat = V4L2_PIX_FMT_SGRBG10, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_10, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_10, + }, + { + .pixelformat = V4L2_PIX_FMT_SRGGB10, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_10, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_10, + }, + { + .pixelformat = V4L2_PIX_FMT_SBGGR12, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_12, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_12, + }, + { + .pixelformat = V4L2_PIX_FMT_SGBRG12, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_12, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_12, + }, + { + .pixelformat = V4L2_PIX_FMT_SGRBG12, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_12, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_12, + }, + { + .pixelformat = V4L2_PIX_FMT_SRGGB12, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_12, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_12, + }, + /* RGB */ + { + .pixelformat = V4L2_PIX_FMT_RGB565, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RGB565, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RGB565, + }, + { + .pixelformat = V4L2_PIX_FMT_RGB565X, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RGB565, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RGB565, + }, + /* YUV422 */ + { + .pixelformat = V4L2_PIX_FMT_YUYV, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8, + .input_format_raw = true, + .hsize_len_factor = 2, + }, + { + .pixelformat = V4L2_PIX_FMT_YVYU, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8, + .input_format_raw = true, + .hsize_len_factor = 2, + }, + { + .pixelformat = V4L2_PIX_FMT_UYVY, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8, + .input_format_raw = true, + .hsize_len_factor = 2, + }, + { + .pixelformat = V4L2_PIX_FMT_VYUY, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8, + .input_format_raw = true, + .hsize_len_factor = 2, + }, + { + .pixelformat = V4L2_PIX_FMT_NV16, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_YUV422SP, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_YUV422SP, + }, + { + .pixelformat = V4L2_PIX_FMT_NV61, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_YUV422SP, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_YUV422SP, + .input_yuv_seq_invert = true, + }, + { + .pixelformat = V4L2_PIX_FMT_YUV422P, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_YUV422P, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_YUV422P, + }, + /* YUV420 */ + { + .pixelformat = V4L2_PIX_FMT_NV12_16L16, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420MB, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420MB, + }, + { + .pixelformat = V4L2_PIX_FMT_NV12, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420SP, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420SP, + }, + { + .pixelformat = V4L2_PIX_FMT_NV21, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420SP, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420SP, + .input_yuv_seq_invert = true, + }, + + { + .pixelformat = V4L2_PIX_FMT_YUV420, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420P, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420P, + }, + { + .pixelformat = V4L2_PIX_FMT_YVU420, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420P, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420P, + .input_yuv_seq_invert = true, + }, + /* Compressed */ + { + .pixelformat = V4L2_PIX_FMT_JPEG, + .output_format_frame = SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8, + .output_format_field = SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8, + }, +}; + +const +struct sun6i_csi_capture_format *sun6i_csi_capture_format_find(u32 pixelformat) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(sun6i_csi_capture_formats); i++) + if (sun6i_csi_capture_formats[i].pixelformat == pixelformat) + return &sun6i_csi_capture_formats[i]; + + return NULL; +} + +/* RAW formats need an exact match between pixel and mbus formats. */ +static const +struct sun6i_csi_capture_format_match sun6i_csi_capture_format_matches[] = { + /* YUV420 */ + { + .pixelformat = V4L2_PIX_FMT_YUYV, + .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, + }, + { + .pixelformat = V4L2_PIX_FMT_YUYV, + .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16, + }, + { + .pixelformat = V4L2_PIX_FMT_YVYU, + .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8, + }, + { + .pixelformat = V4L2_PIX_FMT_YVYU, + .mbus_code = MEDIA_BUS_FMT_YVYU8_1X16, + }, + { + .pixelformat = V4L2_PIX_FMT_UYVY, + .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, + }, + { + .pixelformat = V4L2_PIX_FMT_UYVY, + .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16, + }, + { + .pixelformat = V4L2_PIX_FMT_VYUY, + .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8, + }, + { + .pixelformat = V4L2_PIX_FMT_VYUY, + .mbus_code = MEDIA_BUS_FMT_VYUY8_1X16, + }, + /* RGB */ + { + .pixelformat = V4L2_PIX_FMT_RGB565, + .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE, + }, + { + .pixelformat = V4L2_PIX_FMT_RGB565X, + .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_BE, + }, + /* Bayer */ + { + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, + }, + { + .pixelformat = V4L2_PIX_FMT_SGBRG8, + .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, + }, + { + .pixelformat = V4L2_PIX_FMT_SGRBG8, + .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, + }, + { + .pixelformat = V4L2_PIX_FMT_SRGGB8, + .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, + }, + { + .pixelformat = V4L2_PIX_FMT_SBGGR10, + .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, + }, + { + .pixelformat = V4L2_PIX_FMT_SGBRG10, + .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, + }, + { + .pixelformat = V4L2_PIX_FMT_SGRBG10, + .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, + }, + { + .pixelformat = V4L2_PIX_FMT_SRGGB10, + .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, + }, + { + .pixelformat = V4L2_PIX_FMT_SBGGR12, + .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12, + }, + { + .pixelformat = V4L2_PIX_FMT_SGBRG12, + .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12, + }, + { + .pixelformat = V4L2_PIX_FMT_SGRBG12, + .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12, + }, + { + .pixelformat = V4L2_PIX_FMT_SRGGB12, + .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12, + }, + /* Compressed */ + { + .pixelformat = V4L2_PIX_FMT_JPEG, + .mbus_code = MEDIA_BUS_FMT_JPEG_1X8, + }, +}; + +static bool sun6i_csi_capture_format_match(u32 pixelformat, u32 mbus_code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(sun6i_csi_capture_format_matches); i++) { + const struct sun6i_csi_capture_format_match *match = + &sun6i_csi_capture_format_matches[i]; + + if (match->pixelformat == pixelformat && + match->mbus_code == mbus_code) + return true; + } + + return false; +} + +/* Capture */ + +static void +sun6i_csi_capture_buffer_configure(struct sun6i_csi_device *csi_dev, + struct sun6i_csi_buffer *csi_buffer) +{ + struct regmap *regmap = csi_dev->regmap; + const struct v4l2_format_info *info; + struct vb2_buffer *vb2_buffer; + unsigned int width, height; + dma_addr_t address; + u32 pixelformat; + + vb2_buffer = &csi_buffer->v4l2_buffer.vb2_buf; + address = vb2_dma_contig_plane_dma_addr(vb2_buffer, 0); + + regmap_write(regmap, SUN6I_CSI_CH_FIFO0_ADDR_REG, + SUN6I_CSI_ADDR_VALUE(address)); + + sun6i_csi_capture_dimensions(csi_dev, &width, &height); + sun6i_csi_capture_format(csi_dev, &pixelformat, NULL); + + info = v4l2_format_info(pixelformat); + /* Unsupported formats are single-plane, so we can stop here. */ + if (!info) + return; + + if (info->comp_planes > 1) { + address += info->bpp[0] * width * height; + + regmap_write(regmap, SUN6I_CSI_CH_FIFO1_ADDR_REG, + SUN6I_CSI_ADDR_VALUE(address)); + } + + if (info->comp_planes > 2) { + address += info->bpp[1] * DIV_ROUND_UP(width, info->hdiv) * + DIV_ROUND_UP(height, info->vdiv); + + regmap_write(regmap, SUN6I_CSI_CH_FIFO2_ADDR_REG, + SUN6I_CSI_ADDR_VALUE(address)); + } +} + +void sun6i_csi_capture_configure(struct sun6i_csi_device *csi_dev) +{ + struct regmap *regmap = csi_dev->regmap; + const struct sun6i_csi_capture_format *format; + const struct v4l2_format_info *info; + u32 hsize_len, vsize_len; + u32 luma_line, chroma_line = 0; + u32 pixelformat, field; + u32 width, height; + + sun6i_csi_capture_dimensions(csi_dev, &width, &height); + sun6i_csi_capture_format(csi_dev, &pixelformat, &field); + + format = sun6i_csi_capture_format_find(pixelformat); + if (WARN_ON(!format)) + return; + + hsize_len = width; + vsize_len = height; + + /* + * When using 8-bit raw input/output (for packed YUV), we need to adapt + * the width to account for the difference in bpp when it's not 8-bit. + */ + if (format->hsize_len_factor) + hsize_len *= format->hsize_len_factor; + + regmap_write(regmap, SUN6I_CSI_CH_HSIZE_REG, + SUN6I_CSI_CH_HSIZE_LEN(hsize_len) | + SUN6I_CSI_CH_HSIZE_START(0)); + + regmap_write(regmap, SUN6I_CSI_CH_VSIZE_REG, + SUN6I_CSI_CH_VSIZE_LEN(vsize_len) | + SUN6I_CSI_CH_VSIZE_START(0)); + + switch (pixelformat) { + case V4L2_PIX_FMT_RGB565X: + luma_line = width * 2; + break; + case V4L2_PIX_FMT_NV12_16L16: + luma_line = width; + chroma_line = width; + break; + case V4L2_PIX_FMT_JPEG: + luma_line = width; + break; + default: + info = v4l2_format_info(pixelformat); + if (WARN_ON(!info)) + return; + + luma_line = width * info->bpp[0]; + + if (info->comp_planes > 1) + chroma_line = width * info->bpp[1] / info->hdiv; + break; + } + + regmap_write(regmap, SUN6I_CSI_CH_BUF_LEN_REG, + SUN6I_CSI_CH_BUF_LEN_CHROMA_LINE(chroma_line) | + SUN6I_CSI_CH_BUF_LEN_LUMA_LINE(luma_line)); +} + +/* State */ + +static void sun6i_csi_capture_state_cleanup(struct sun6i_csi_device *csi_dev, + bool error) +{ + struct sun6i_csi_capture_state *state = &csi_dev->capture.state; + struct sun6i_csi_buffer **csi_buffer_states[] = { + &state->pending, &state->current, &state->complete, + }; + struct sun6i_csi_buffer *csi_buffer; + struct vb2_buffer *vb2_buffer; + unsigned long flags; + unsigned int i; + + spin_lock_irqsave(&state->lock, flags); + + for (i = 0; i < ARRAY_SIZE(csi_buffer_states); i++) { + csi_buffer = *csi_buffer_states[i]; + if (!csi_buffer) + continue; + + vb2_buffer = &csi_buffer->v4l2_buffer.vb2_buf; + vb2_buffer_done(vb2_buffer, error ? VB2_BUF_STATE_ERROR : + VB2_BUF_STATE_QUEUED); + + *csi_buffer_states[i] = NULL; + } + + list_for_each_entry(csi_buffer, &state->queue, list) { + vb2_buffer = &csi_buffer->v4l2_buffer.vb2_buf; + vb2_buffer_done(vb2_buffer, error ? VB2_BUF_STATE_ERROR : + VB2_BUF_STATE_QUEUED); + } + + INIT_LIST_HEAD(&state->queue); + + spin_unlock_irqrestore(&state->lock, flags); +} + +void sun6i_csi_capture_state_update(struct sun6i_csi_device *csi_dev) +{ + struct sun6i_csi_capture_state *state = &csi_dev->capture.state; + struct sun6i_csi_buffer *csi_buffer; + unsigned long flags; + + spin_lock_irqsave(&state->lock, flags); + + if (list_empty(&state->queue)) + goto complete; + + if (state->pending) + goto complete; + + csi_buffer = list_first_entry(&state->queue, struct sun6i_csi_buffer, + list); + + sun6i_csi_capture_buffer_configure(csi_dev, csi_buffer); + + list_del(&csi_buffer->list); + + state->pending = csi_buffer; + +complete: + spin_unlock_irqrestore(&state->lock, flags); +} + +static void sun6i_csi_capture_state_complete(struct sun6i_csi_device *csi_dev) +{ + struct sun6i_csi_capture_state *state = &csi_dev->capture.state; + unsigned long flags; + + spin_lock_irqsave(&state->lock, flags); + + if (!state->pending) + goto complete; + + state->complete = state->current; + state->current = state->pending; + state->pending = NULL; + + if (state->complete) { + struct sun6i_csi_buffer *csi_buffer = state->complete; + struct vb2_buffer *vb2_buffer = + &csi_buffer->v4l2_buffer.vb2_buf; + + vb2_buffer->timestamp = ktime_get_ns(); + csi_buffer->v4l2_buffer.sequence = state->sequence; + + vb2_buffer_done(vb2_buffer, VB2_BUF_STATE_DONE); + + state->complete = NULL; + } + +complete: + spin_unlock_irqrestore(&state->lock, flags); +} + +void sun6i_csi_capture_frame_done(struct sun6i_csi_device *csi_dev) +{ + struct sun6i_csi_capture_state *state = &csi_dev->capture.state; + unsigned long flags; + + spin_lock_irqsave(&state->lock, flags); + state->sequence++; + spin_unlock_irqrestore(&state->lock, flags); +} + +void sun6i_csi_capture_sync(struct sun6i_csi_device *csi_dev) +{ + sun6i_csi_capture_state_complete(csi_dev); + sun6i_csi_capture_state_update(csi_dev); +} + +/* Queue */ + +static int sun6i_csi_capture_queue_setup(struct vb2_queue *queue, + unsigned int *buffers_count, + unsigned int *planes_count, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(queue); + unsigned int size = csi_dev->capture.format.fmt.pix.sizeimage; + + if (*planes_count) + return sizes[0] < size ? -EINVAL : 0; + + *planes_count = 1; + sizes[0] = size; + + return 0; +} + +static int sun6i_csi_capture_buffer_prepare(struct vb2_buffer *buffer) +{ + struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(buffer->vb2_queue); + struct sun6i_csi_capture *capture = &csi_dev->capture; + struct v4l2_device *v4l2_dev = csi_dev->v4l2_dev; + struct vb2_v4l2_buffer *v4l2_buffer = to_vb2_v4l2_buffer(buffer); + unsigned long size = capture->format.fmt.pix.sizeimage; + + if (vb2_plane_size(buffer, 0) < size) { + v4l2_err(v4l2_dev, "buffer too small (%lu < %lu)\n", + vb2_plane_size(buffer, 0), size); + return -EINVAL; + } + + vb2_set_plane_payload(buffer, 0, size); + + v4l2_buffer->field = capture->format.fmt.pix.field; + + return 0; +} + +static void sun6i_csi_capture_buffer_queue(struct vb2_buffer *buffer) +{ + struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(buffer->vb2_queue); + struct sun6i_csi_capture_state *state = &csi_dev->capture.state; + struct vb2_v4l2_buffer *v4l2_buffer = to_vb2_v4l2_buffer(buffer); + struct sun6i_csi_buffer *csi_buffer = + container_of(v4l2_buffer, struct sun6i_csi_buffer, v4l2_buffer); + unsigned long flags; + + spin_lock_irqsave(&state->lock, flags); + list_add_tail(&csi_buffer->list, &state->queue); + spin_unlock_irqrestore(&state->lock, flags); +} + +static int sun6i_csi_capture_start_streaming(struct vb2_queue *queue, + unsigned int count) +{ + struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(queue); + struct sun6i_csi_capture_state *state = &csi_dev->capture.state; + struct video_device *video_dev = &csi_dev->capture.video_dev; + struct v4l2_subdev *subdev = &csi_dev->bridge.subdev; + int ret; + + state->sequence = 0; + + ret = video_device_pipeline_alloc_start(video_dev); + if (ret < 0) + goto error_state; + + state->streaming = true; + + ret = v4l2_subdev_call(subdev, video, s_stream, 1); + if (ret && ret != -ENOIOCTLCMD) + goto error_streaming; + + return 0; + +error_streaming: + state->streaming = false; + + video_device_pipeline_stop(video_dev); + +error_state: + sun6i_csi_capture_state_cleanup(csi_dev, false); + + return ret; +} + +static void sun6i_csi_capture_stop_streaming(struct vb2_queue *queue) +{ + struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(queue); + struct sun6i_csi_capture_state *state = &csi_dev->capture.state; + struct video_device *video_dev = &csi_dev->capture.video_dev; + struct v4l2_subdev *subdev = &csi_dev->bridge.subdev; + + v4l2_subdev_call(subdev, video, s_stream, 0); + + state->streaming = false; + + video_device_pipeline_stop(video_dev); + + sun6i_csi_capture_state_cleanup(csi_dev, true); +} + +static const struct vb2_ops sun6i_csi_capture_queue_ops = { + .queue_setup = sun6i_csi_capture_queue_setup, + .buf_prepare = sun6i_csi_capture_buffer_prepare, + .buf_queue = sun6i_csi_capture_buffer_queue, + .start_streaming = sun6i_csi_capture_start_streaming, + .stop_streaming = sun6i_csi_capture_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +/* V4L2 Device */ + +static void sun6i_csi_capture_format_prepare(struct v4l2_format *format) +{ + struct v4l2_pix_format *pix_format = &format->fmt.pix; + const struct v4l2_format_info *info; + unsigned int width, height; + + v4l_bound_align_image(&pix_format->width, SUN6I_CSI_CAPTURE_WIDTH_MIN, + SUN6I_CSI_CAPTURE_WIDTH_MAX, 1, + &pix_format->height, SUN6I_CSI_CAPTURE_HEIGHT_MIN, + SUN6I_CSI_CAPTURE_HEIGHT_MAX, 1, 0); + + if (!sun6i_csi_capture_format_find(pix_format->pixelformat)) + pix_format->pixelformat = + sun6i_csi_capture_formats[0].pixelformat; + + width = pix_format->width; + height = pix_format->height; + + info = v4l2_format_info(pix_format->pixelformat); + + switch (pix_format->pixelformat) { + case V4L2_PIX_FMT_NV12_16L16: + pix_format->bytesperline = width * 12 / 8; + pix_format->sizeimage = pix_format->bytesperline * height; + break; + case V4L2_PIX_FMT_JPEG: + pix_format->bytesperline = width; + pix_format->sizeimage = pix_format->bytesperline * height; + break; + default: + v4l2_fill_pixfmt(pix_format, pix_format->pixelformat, + width, height); + break; + } + + if (pix_format->field == V4L2_FIELD_ANY) + pix_format->field = V4L2_FIELD_NONE; + + if (pix_format->pixelformat == V4L2_PIX_FMT_JPEG) + pix_format->colorspace = V4L2_COLORSPACE_JPEG; + else if (info && info->pixel_enc == V4L2_PIXEL_ENC_BAYER) + pix_format->colorspace = V4L2_COLORSPACE_RAW; + else + pix_format->colorspace = V4L2_COLORSPACE_SRGB; + + pix_format->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + pix_format->quantization = V4L2_QUANTIZATION_DEFAULT; + pix_format->xfer_func = V4L2_XFER_FUNC_DEFAULT; +} + +static int sun6i_csi_capture_querycap(struct file *file, void *private, + struct v4l2_capability *capability) +{ + struct sun6i_csi_device *csi_dev = video_drvdata(file); + struct video_device *video_dev = &csi_dev->capture.video_dev; + + strscpy(capability->driver, SUN6I_CSI_NAME, sizeof(capability->driver)); + strscpy(capability->card, video_dev->name, sizeof(capability->card)); + snprintf(capability->bus_info, sizeof(capability->bus_info), + "platform:%s", dev_name(csi_dev->dev)); + + return 0; +} + +static int sun6i_csi_capture_enum_fmt(struct file *file, void *private, + struct v4l2_fmtdesc *fmtdesc) +{ + u32 index = fmtdesc->index; + + if (index >= ARRAY_SIZE(sun6i_csi_capture_formats)) + return -EINVAL; + + fmtdesc->pixelformat = sun6i_csi_capture_formats[index].pixelformat; + + return 0; +} + +static int sun6i_csi_capture_g_fmt(struct file *file, void *private, + struct v4l2_format *format) +{ + struct sun6i_csi_device *csi_dev = video_drvdata(file); + + *format = csi_dev->capture.format; + + return 0; +} + +static int sun6i_csi_capture_s_fmt(struct file *file, void *private, + struct v4l2_format *format) +{ + struct sun6i_csi_device *csi_dev = video_drvdata(file); + struct sun6i_csi_capture *capture = &csi_dev->capture; + + if (vb2_is_busy(&capture->queue)) + return -EBUSY; + + sun6i_csi_capture_format_prepare(format); + + csi_dev->capture.format = *format; + + return 0; +} + +static int sun6i_csi_capture_try_fmt(struct file *file, void *private, + struct v4l2_format *format) +{ + sun6i_csi_capture_format_prepare(format); + + return 0; +} + +static int sun6i_csi_capture_enum_input(struct file *file, void *private, + struct v4l2_input *input) +{ + if (input->index != 0) + return -EINVAL; + + input->type = V4L2_INPUT_TYPE_CAMERA; + strscpy(input->name, "Camera", sizeof(input->name)); + + return 0; +} + +static int sun6i_csi_capture_g_input(struct file *file, void *private, + unsigned int *index) +{ + *index = 0; + + return 0; +} + +static int sun6i_csi_capture_s_input(struct file *file, void *private, + unsigned int index) +{ + if (index != 0) + return -EINVAL; + + return 0; +} + +static const struct v4l2_ioctl_ops sun6i_csi_capture_ioctl_ops = { + .vidioc_querycap = sun6i_csi_capture_querycap, + + .vidioc_enum_fmt_vid_cap = sun6i_csi_capture_enum_fmt, + .vidioc_g_fmt_vid_cap = sun6i_csi_capture_g_fmt, + .vidioc_s_fmt_vid_cap = sun6i_csi_capture_s_fmt, + .vidioc_try_fmt_vid_cap = sun6i_csi_capture_try_fmt, + + .vidioc_enum_input = sun6i_csi_capture_enum_input, + .vidioc_g_input = sun6i_csi_capture_g_input, + .vidioc_s_input = sun6i_csi_capture_s_input, + + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +}; + +/* V4L2 File */ + +static int sun6i_csi_capture_open(struct file *file) +{ + struct sun6i_csi_device *csi_dev = video_drvdata(file); + struct sun6i_csi_capture *capture = &csi_dev->capture; + int ret = 0; + + if (mutex_lock_interruptible(&capture->lock)) + return -ERESTARTSYS; + + ret = v4l2_pipeline_pm_get(&capture->video_dev.entity); + if (ret < 0) + goto error_lock; + + ret = v4l2_fh_open(file); + if (ret < 0) + goto error_pipeline; + + mutex_unlock(&capture->lock); + + return 0; + +error_pipeline: + v4l2_pipeline_pm_put(&capture->video_dev.entity); + +error_lock: + mutex_unlock(&capture->lock); + + return ret; +} + +static int sun6i_csi_capture_close(struct file *file) +{ + struct sun6i_csi_device *csi_dev = video_drvdata(file); + struct sun6i_csi_capture *capture = &csi_dev->capture; + + mutex_lock(&capture->lock); + + _vb2_fop_release(file, NULL); + v4l2_pipeline_pm_put(&capture->video_dev.entity); + + mutex_unlock(&capture->lock); + + return 0; +} + +static const struct v4l2_file_operations sun6i_csi_capture_fops = { + .owner = THIS_MODULE, + .open = sun6i_csi_capture_open, + .release = sun6i_csi_capture_close, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll +}; + +/* Media Entity */ + +static int sun6i_csi_capture_link_validate(struct media_link *link) +{ + struct video_device *video_dev = + media_entity_to_video_device(link->sink->entity); + struct sun6i_csi_device *csi_dev = video_get_drvdata(video_dev); + struct v4l2_device *v4l2_dev = csi_dev->v4l2_dev; + const struct sun6i_csi_capture_format *capture_format; + const struct sun6i_csi_bridge_format *bridge_format; + unsigned int capture_width, capture_height; + unsigned int bridge_width, bridge_height; + const struct v4l2_format_info *format_info; + u32 pixelformat, capture_field; + u32 mbus_code, bridge_field; + bool match; + + sun6i_csi_capture_dimensions(csi_dev, &capture_width, &capture_height); + + sun6i_csi_capture_format(csi_dev, &pixelformat, &capture_field); + capture_format = sun6i_csi_capture_format_find(pixelformat); + if (WARN_ON(!capture_format)) + return -EINVAL; + + sun6i_csi_bridge_dimensions(csi_dev, &bridge_width, &bridge_height); + + sun6i_csi_bridge_format(csi_dev, &mbus_code, &bridge_field); + bridge_format = sun6i_csi_bridge_format_find(mbus_code); + if (WARN_ON(!bridge_format)) + return -EINVAL; + + /* No cropping/scaling is supported. */ + if (capture_width != bridge_width || capture_height != bridge_height) { + v4l2_err(v4l2_dev, + "invalid input/output dimensions: %ux%u/%ux%u\n", + bridge_width, bridge_height, capture_width, + capture_height); + return -EINVAL; + } + + format_info = v4l2_format_info(pixelformat); + /* Some formats are not listed. */ + if (!format_info) + return 0; + + if (format_info->pixel_enc == V4L2_PIXEL_ENC_BAYER && + bridge_format->input_format != SUN6I_CSI_INPUT_FMT_RAW) + goto invalid; + + if (format_info->pixel_enc == V4L2_PIXEL_ENC_RGB && + bridge_format->input_format != SUN6I_CSI_INPUT_FMT_RAW) + goto invalid; + + if (format_info->pixel_enc == V4L2_PIXEL_ENC_YUV) { + if (bridge_format->input_format != SUN6I_CSI_INPUT_FMT_YUV420 && + bridge_format->input_format != SUN6I_CSI_INPUT_FMT_YUV422) + goto invalid; + + /* YUV420 input can't produce YUV422 output. */ + if (bridge_format->input_format == SUN6I_CSI_INPUT_FMT_YUV420 && + format_info->vdiv == 1) + goto invalid; + } + + /* With raw input mode, we need a 1:1 match between input and output. */ + if (bridge_format->input_format == SUN6I_CSI_INPUT_FMT_RAW || + capture_format->input_format_raw) { + match = sun6i_csi_capture_format_match(pixelformat, mbus_code); + if (!match) + goto invalid; + } + + return 0; + +invalid: + v4l2_err(v4l2_dev, "invalid input/output format combination\n"); + return -EINVAL; +} + +static const struct media_entity_operations sun6i_csi_capture_media_ops = { + .link_validate = sun6i_csi_capture_link_validate +}; + +/* Capture */ + +int sun6i_csi_capture_setup(struct sun6i_csi_device *csi_dev) +{ + struct sun6i_csi_capture *capture = &csi_dev->capture; + struct sun6i_csi_capture_state *state = &capture->state; + struct v4l2_device *v4l2_dev = csi_dev->v4l2_dev; + struct v4l2_subdev *bridge_subdev = &csi_dev->bridge.subdev; + struct video_device *video_dev = &capture->video_dev; + struct vb2_queue *queue = &capture->queue; + struct media_pad *pad = &capture->pad; + struct v4l2_format *format = &csi_dev->capture.format; + struct v4l2_pix_format *pix_format = &format->fmt.pix; + int ret; + + /* This may happen with multiple bridge notifier bound calls. */ + if (state->setup) + return 0; + + /* State */ + + INIT_LIST_HEAD(&state->queue); + spin_lock_init(&state->lock); + + /* Media Entity */ + + video_dev->entity.ops = &sun6i_csi_capture_media_ops; + + /* Media Pad */ + + pad->flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; + + ret = media_entity_pads_init(&video_dev->entity, 1, pad); + if (ret < 0) + return ret; + + /* Queue */ + + mutex_init(&capture->lock); + + queue->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + queue->io_modes = VB2_MMAP | VB2_DMABUF; + queue->buf_struct_size = sizeof(struct sun6i_csi_buffer); + queue->ops = &sun6i_csi_capture_queue_ops; + queue->mem_ops = &vb2_dma_contig_memops; + queue->min_buffers_needed = 2; + queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + queue->lock = &capture->lock; + queue->dev = csi_dev->dev; + queue->drv_priv = csi_dev; + + ret = vb2_queue_init(queue); + if (ret) { + v4l2_err(v4l2_dev, "failed to initialize vb2 queue: %d\n", ret); + goto error_media_entity; + } + + /* V4L2 Format */ + + format->type = queue->type; + pix_format->pixelformat = sun6i_csi_capture_formats[0].pixelformat; + pix_format->width = 1280; + pix_format->height = 720; + pix_format->field = V4L2_FIELD_NONE; + + sun6i_csi_capture_format_prepare(format); + + /* Video Device */ + + strscpy(video_dev->name, SUN6I_CSI_CAPTURE_NAME, + sizeof(video_dev->name)); + video_dev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + video_dev->vfl_dir = VFL_DIR_RX; + video_dev->release = video_device_release_empty; + video_dev->fops = &sun6i_csi_capture_fops; + video_dev->ioctl_ops = &sun6i_csi_capture_ioctl_ops; + video_dev->v4l2_dev = v4l2_dev; + video_dev->queue = queue; + video_dev->lock = &capture->lock; + + video_set_drvdata(video_dev, csi_dev); + + ret = video_register_device(video_dev, VFL_TYPE_VIDEO, -1); + if (ret < 0) { + v4l2_err(v4l2_dev, "failed to register video device: %d\n", + ret); + goto error_media_entity; + } + + /* Media Pad Link */ + + ret = media_create_pad_link(&bridge_subdev->entity, + SUN6I_CSI_BRIDGE_PAD_SOURCE, + &video_dev->entity, 0, + csi_dev->isp_available ? 0 : + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret < 0) { + v4l2_err(v4l2_dev, "failed to create %s:%u -> %s:%u link\n", + bridge_subdev->entity.name, + SUN6I_CSI_BRIDGE_PAD_SOURCE, + video_dev->entity.name, 0); + goto error_video_device; + } + + state->setup = true; + + return 0; + +error_video_device: + vb2_video_unregister_device(video_dev); + +error_media_entity: + media_entity_cleanup(&video_dev->entity); + + mutex_destroy(&capture->lock); + + return ret; +} + +void sun6i_csi_capture_cleanup(struct sun6i_csi_device *csi_dev) +{ + struct sun6i_csi_capture *capture = &csi_dev->capture; + struct video_device *video_dev = &capture->video_dev; + + /* This may happen if async registration failed to complete. */ + if (!capture->state.setup) + return; + + vb2_video_unregister_device(video_dev); + media_entity_cleanup(&video_dev->entity); + mutex_destroy(&capture->lock); + + capture->state.setup = false; +} diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.h new file mode 100644 index 000000000000..3ee5ccefbd10 --- /dev/null +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.h @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2011-2018 Magewell Electronics Co., Ltd. (Nanjing) + * Author: Yong Deng <[email protected]> + * Copyright 2021-2022 Bootlin + * Author: Paul Kocialkowski <[email protected]> + */ + +#ifndef _SUN6I_CAPTURE_H_ +#define _SUN6I_CAPTURE_H_ + +#include <media/v4l2-device.h> + +#define SUN6I_CSI_CAPTURE_NAME "sun6i-csi-capture" + +#define SUN6I_CSI_CAPTURE_WIDTH_MIN 32 +#define SUN6I_CSI_CAPTURE_WIDTH_MAX 4800 +#define SUN6I_CSI_CAPTURE_HEIGHT_MIN 32 +#define SUN6I_CSI_CAPTURE_HEIGHT_MAX 4800 + +struct sun6i_csi_device; + +struct sun6i_csi_capture_format { + u32 pixelformat; + u8 output_format_field; + u8 output_format_frame; + bool input_yuv_seq_invert; + bool input_format_raw; + u32 hsize_len_factor; +}; + +struct sun6i_csi_capture_format_match { + u32 pixelformat; + u32 mbus_code; +}; + +#undef current +struct sun6i_csi_capture_state { + struct list_head queue; + spinlock_t lock; /* Queue and buffers lock. */ + + struct sun6i_csi_buffer *pending; + struct sun6i_csi_buffer *current; + struct sun6i_csi_buffer *complete; + + unsigned int sequence; + bool streaming; + bool setup; +}; + +struct sun6i_csi_capture { + struct sun6i_csi_capture_state state; + + struct video_device video_dev; + struct vb2_queue queue; + struct mutex lock; /* Queue lock. */ + struct media_pad pad; + + struct v4l2_format format; +}; + +/* Helpers */ + +void sun6i_csi_capture_dimensions(struct sun6i_csi_device *csi_dev, + unsigned int *width, unsigned int *height); +void sun6i_csi_capture_format(struct sun6i_csi_device *csi_dev, + u32 *pixelformat, u32 *field); + +/* Format */ + +const +struct sun6i_csi_capture_format *sun6i_csi_capture_format_find(u32 pixelformat); + +/* Capture */ + +void sun6i_csi_capture_configure(struct sun6i_csi_device *csi_dev); +void sun6i_csi_capture_state_update(struct sun6i_csi_device *csi_dev); + +/* State */ + +void sun6i_csi_capture_sync(struct sun6i_csi_device *csi_dev); +void sun6i_csi_capture_frame_done(struct sun6i_csi_device *csi_dev); + +/* Capture */ + +int sun6i_csi_capture_setup(struct sun6i_csi_device *csi_dev); +void sun6i_csi_capture_cleanup(struct sun6i_csi_device *csi_dev); + +#endif diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_reg.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_reg.h index 703fa14bb313..e01c5b9c2d60 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_reg.h +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_reg.h @@ -1,196 +1,184 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /* * Copyright (c) 2011-2018 Magewell Electronics Co., Ltd. (Nanjing) - * All rights reserved. * Author: Yong Deng <[email protected]> + * Copyright 2021-2022 Bootlin + * Author: Paul Kocialkowski <[email protected]> */ -#ifndef __SUN6I_CSI_REG_H__ -#define __SUN6I_CSI_REG_H__ +#ifndef _SUN6I_CSI_REG_H_ +#define _SUN6I_CSI_REG_H_ #include <linux/kernel.h> -#define CSI_EN_REG 0x0 -#define CSI_EN_VER_EN BIT(30) -#define CSI_EN_CSI_EN BIT(0) - -#define CSI_IF_CFG_REG 0x4 -#define CSI_IF_CFG_SRC_TYPE_MASK BIT(21) -#define CSI_IF_CFG_SRC_TYPE_PROGRESSED ((0 << 21) & CSI_IF_CFG_SRC_TYPE_MASK) -#define CSI_IF_CFG_SRC_TYPE_INTERLACED ((1 << 21) & CSI_IF_CFG_SRC_TYPE_MASK) -#define CSI_IF_CFG_FPS_DS_EN BIT(20) -#define CSI_IF_CFG_FIELD_MASK BIT(19) -#define CSI_IF_CFG_FIELD_NEGATIVE ((0 << 19) & CSI_IF_CFG_FIELD_MASK) -#define CSI_IF_CFG_FIELD_POSITIVE ((1 << 19) & CSI_IF_CFG_FIELD_MASK) -#define CSI_IF_CFG_VREF_POL_MASK BIT(18) -#define CSI_IF_CFG_VREF_POL_NEGATIVE ((0 << 18) & CSI_IF_CFG_VREF_POL_MASK) -#define CSI_IF_CFG_VREF_POL_POSITIVE ((1 << 18) & CSI_IF_CFG_VREF_POL_MASK) -#define CSI_IF_CFG_HREF_POL_MASK BIT(17) -#define CSI_IF_CFG_HREF_POL_NEGATIVE ((0 << 17) & CSI_IF_CFG_HREF_POL_MASK) -#define CSI_IF_CFG_HREF_POL_POSITIVE ((1 << 17) & CSI_IF_CFG_HREF_POL_MASK) -#define CSI_IF_CFG_CLK_POL_MASK BIT(16) -#define CSI_IF_CFG_CLK_POL_RISING_EDGE ((0 << 16) & CSI_IF_CFG_CLK_POL_MASK) -#define CSI_IF_CFG_CLK_POL_FALLING_EDGE ((1 << 16) & CSI_IF_CFG_CLK_POL_MASK) -#define CSI_IF_CFG_IF_DATA_WIDTH_MASK GENMASK(10, 8) -#define CSI_IF_CFG_IF_DATA_WIDTH_8BIT ((0 << 8) & CSI_IF_CFG_IF_DATA_WIDTH_MASK) -#define CSI_IF_CFG_IF_DATA_WIDTH_10BIT ((1 << 8) & CSI_IF_CFG_IF_DATA_WIDTH_MASK) -#define CSI_IF_CFG_IF_DATA_WIDTH_12BIT ((2 << 8) & CSI_IF_CFG_IF_DATA_WIDTH_MASK) -#define CSI_IF_CFG_MIPI_IF_MASK BIT(7) -#define CSI_IF_CFG_MIPI_IF_CSI (0 << 7) -#define CSI_IF_CFG_MIPI_IF_MIPI BIT(7) -#define CSI_IF_CFG_CSI_IF_MASK GENMASK(4, 0) -#define CSI_IF_CFG_CSI_IF_YUV422_INTLV ((0 << 0) & CSI_IF_CFG_CSI_IF_MASK) -#define CSI_IF_CFG_CSI_IF_YUV422_16BIT ((1 << 0) & CSI_IF_CFG_CSI_IF_MASK) -#define CSI_IF_CFG_CSI_IF_BT656 ((4 << 0) & CSI_IF_CFG_CSI_IF_MASK) -#define CSI_IF_CFG_CSI_IF_BT1120 ((5 << 0) & CSI_IF_CFG_CSI_IF_MASK) - -#define CSI_CAP_REG 0x8 -#define CSI_CAP_CH0_CAP_MASK_MASK GENMASK(5, 2) -#define CSI_CAP_CH0_CAP_MASK(count) (((count) << 2) & CSI_CAP_CH0_CAP_MASK_MASK) -#define CSI_CAP_CH0_VCAP_ON BIT(1) -#define CSI_CAP_CH0_SCAP_ON BIT(0) - -#define CSI_SYNC_CNT_REG 0xc -#define CSI_FIFO_THRS_REG 0x10 -#define CSI_BT656_HEAD_CFG_REG 0x14 -#define CSI_PTN_LEN_REG 0x30 -#define CSI_PTN_ADDR_REG 0x34 -#define CSI_VER_REG 0x3c - -#define CSI_CH_CFG_REG 0x44 -#define CSI_CH_CFG_INPUT_FMT_MASK GENMASK(23, 20) -#define CSI_CH_CFG_INPUT_FMT(fmt) (((fmt) << 20) & CSI_CH_CFG_INPUT_FMT_MASK) -#define CSI_CH_CFG_OUTPUT_FMT_MASK GENMASK(19, 16) -#define CSI_CH_CFG_OUTPUT_FMT(fmt) (((fmt) << 16) & CSI_CH_CFG_OUTPUT_FMT_MASK) -#define CSI_CH_CFG_VFLIP_EN BIT(13) -#define CSI_CH_CFG_HFLIP_EN BIT(12) -#define CSI_CH_CFG_FIELD_SEL_MASK GENMASK(11, 10) -#define CSI_CH_CFG_FIELD_SEL_FIELD0 ((0 << 10) & CSI_CH_CFG_FIELD_SEL_MASK) -#define CSI_CH_CFG_FIELD_SEL_FIELD1 ((1 << 10) & CSI_CH_CFG_FIELD_SEL_MASK) -#define CSI_CH_CFG_FIELD_SEL_BOTH ((2 << 10) & CSI_CH_CFG_FIELD_SEL_MASK) -#define CSI_CH_CFG_INPUT_SEQ_MASK GENMASK(9, 8) -#define CSI_CH_CFG_INPUT_SEQ(seq) (((seq) << 8) & CSI_CH_CFG_INPUT_SEQ_MASK) - -#define CSI_CH_SCALE_REG 0x4c -#define CSI_CH_SCALE_QUART_EN BIT(0) - -#define CSI_CH_F0_BUFA_REG 0x50 - -#define CSI_CH_F1_BUFA_REG 0x58 - -#define CSI_CH_F2_BUFA_REG 0x60 - -#define CSI_CH_STA_REG 0x6c -#define CSI_CH_STA_FIELD_STA_MASK BIT(2) -#define CSI_CH_STA_FIELD_STA_FIELD0 ((0 << 2) & CSI_CH_STA_FIELD_STA_MASK) -#define CSI_CH_STA_FIELD_STA_FIELD1 ((1 << 2) & CSI_CH_STA_FIELD_STA_MASK) -#define CSI_CH_STA_VCAP_STA BIT(1) -#define CSI_CH_STA_SCAP_STA BIT(0) - -#define CSI_CH_INT_EN_REG 0x70 -#define CSI_CH_INT_EN_VS_INT_EN BIT(7) -#define CSI_CH_INT_EN_HB_OF_INT_EN BIT(6) -#define CSI_CH_INT_EN_MUL_ERR_INT_EN BIT(5) -#define CSI_CH_INT_EN_FIFO2_OF_INT_EN BIT(4) -#define CSI_CH_INT_EN_FIFO1_OF_INT_EN BIT(3) -#define CSI_CH_INT_EN_FIFO0_OF_INT_EN BIT(2) -#define CSI_CH_INT_EN_FD_INT_EN BIT(1) -#define CSI_CH_INT_EN_CD_INT_EN BIT(0) - -#define CSI_CH_INT_STA_REG 0x74 -#define CSI_CH_INT_STA_VS_PD BIT(7) -#define CSI_CH_INT_STA_HB_OF_PD BIT(6) -#define CSI_CH_INT_STA_MUL_ERR_PD BIT(5) -#define CSI_CH_INT_STA_FIFO2_OF_PD BIT(4) -#define CSI_CH_INT_STA_FIFO1_OF_PD BIT(3) -#define CSI_CH_INT_STA_FIFO0_OF_PD BIT(2) -#define CSI_CH_INT_STA_FD_PD BIT(1) -#define CSI_CH_INT_STA_CD_PD BIT(0) - -#define CSI_CH_FLD1_VSIZE_REG 0x78 - -#define CSI_CH_HSIZE_REG 0x80 -#define CSI_CH_HSIZE_HOR_LEN_MASK GENMASK(28, 16) -#define CSI_CH_HSIZE_HOR_LEN(len) (((len) << 16) & CSI_CH_HSIZE_HOR_LEN_MASK) -#define CSI_CH_HSIZE_HOR_START_MASK GENMASK(12, 0) -#define CSI_CH_HSIZE_HOR_START(start) (((start) << 0) & CSI_CH_HSIZE_HOR_START_MASK) - -#define CSI_CH_VSIZE_REG 0x84 -#define CSI_CH_VSIZE_VER_LEN_MASK GENMASK(28, 16) -#define CSI_CH_VSIZE_VER_LEN(len) (((len) << 16) & CSI_CH_VSIZE_VER_LEN_MASK) -#define CSI_CH_VSIZE_VER_START_MASK GENMASK(12, 0) -#define CSI_CH_VSIZE_VER_START(start) (((start) << 0) & CSI_CH_VSIZE_VER_START_MASK) - -#define CSI_CH_BUF_LEN_REG 0x88 -#define CSI_CH_BUF_LEN_BUF_LEN_C_MASK GENMASK(29, 16) -#define CSI_CH_BUF_LEN_BUF_LEN_C(len) (((len) << 16) & CSI_CH_BUF_LEN_BUF_LEN_C_MASK) -#define CSI_CH_BUF_LEN_BUF_LEN_Y_MASK GENMASK(13, 0) -#define CSI_CH_BUF_LEN_BUF_LEN_Y(len) (((len) << 0) & CSI_CH_BUF_LEN_BUF_LEN_Y_MASK) - -#define CSI_CH_FLIP_SIZE_REG 0x8c -#define CSI_CH_FLIP_SIZE_VER_LEN_MASK GENMASK(28, 16) -#define CSI_CH_FLIP_SIZE_VER_LEN(len) (((len) << 16) & CSI_CH_FLIP_SIZE_VER_LEN_MASK) -#define CSI_CH_FLIP_SIZE_VALID_LEN_MASK GENMASK(12, 0) -#define CSI_CH_FLIP_SIZE_VALID_LEN(len) (((len) << 0) & CSI_CH_FLIP_SIZE_VALID_LEN_MASK) - -#define CSI_CH_FRM_CLK_CNT_REG 0x90 -#define CSI_CH_ACC_ITNL_CLK_CNT_REG 0x94 -#define CSI_CH_FIFO_STAT_REG 0x98 -#define CSI_CH_PCLK_STAT_REG 0x9c - -/* - * csi input data format - */ -enum csi_input_fmt { - CSI_INPUT_FORMAT_RAW = 0, - CSI_INPUT_FORMAT_YUV422 = 3, - CSI_INPUT_FORMAT_YUV420 = 4, -}; - -/* - * csi output data format - */ -enum csi_output_fmt { - /* only when input format is RAW */ - CSI_FIELD_RAW_8 = 0, - CSI_FIELD_RAW_10 = 1, - CSI_FIELD_RAW_12 = 2, - CSI_FIELD_RGB565 = 4, - CSI_FIELD_RGB888 = 5, - CSI_FIELD_PRGB888 = 6, - CSI_FRAME_RAW_8 = 8, - CSI_FRAME_RAW_10 = 9, - CSI_FRAME_RAW_12 = 10, - CSI_FRAME_RGB565 = 12, - CSI_FRAME_RGB888 = 13, - CSI_FRAME_PRGB888 = 14, - - /* only when input format is YUV422 */ - CSI_FIELD_PLANAR_YUV422 = 0, - CSI_FIELD_PLANAR_YUV420 = 1, - CSI_FRAME_PLANAR_YUV420 = 2, - CSI_FRAME_PLANAR_YUV422 = 3, - CSI_FIELD_UV_CB_YUV422 = 4, - CSI_FIELD_UV_CB_YUV420 = 5, - CSI_FRAME_UV_CB_YUV420 = 6, - CSI_FRAME_UV_CB_YUV422 = 7, - CSI_FIELD_MB_YUV422 = 8, - CSI_FIELD_MB_YUV420 = 9, - CSI_FRAME_MB_YUV420 = 10, - CSI_FRAME_MB_YUV422 = 11, - CSI_FIELD_UV_CB_YUV422_10 = 12, - CSI_FIELD_UV_CB_YUV420_10 = 13, -}; - -/* - * csi YUV input data sequence - */ -enum csi_input_seq { - /* only when input format is YUV422 */ - CSI_INPUT_SEQ_YUYV = 0, - CSI_INPUT_SEQ_YVYU, - CSI_INPUT_SEQ_UYVY, - CSI_INPUT_SEQ_VYUY, -}; - -#endif /* __SUN6I_CSI_REG_H__ */ +#define SUN6I_CSI_ADDR_VALUE(a) ((a) >> 2) + +#define SUN6I_CSI_EN_REG 0x0 +#define SUN6I_CSI_EN_VER_EN BIT(30) +#define SUN6I_CSI_EN_PTN_CYCLE(v) (((v) << 16) & GENMASK(23, 16)) +#define SUN6I_CSI_EN_SRAM_PWDN BIT(8) +#define SUN6I_CSI_EN_PTN_START BIT(4) +#define SUN6I_CSI_EN_CLK_CNT_SPL_VSYNC BIT(3) +#define SUN6I_CSI_EN_CLK_CNT_EN BIT(2) +#define SUN6I_CSI_EN_PTN_GEN_EN BIT(1) +#define SUN6I_CSI_EN_CSI_EN BIT(0) + +/* Note that Allwinner manuals and code invert positive/negative definitions. */ + +#define SUN6I_CSI_IF_CFG_REG 0x4 +#define SUN6I_CSI_IF_CFG_FIELD_DT_PCLK_SHIFT(v) (((v) << 24) & GENMASK(27, 24)) +#define SUN6I_CSI_IF_CFG_SRC_TYPE_PROGRESSIVE (0 << 21) +#define SUN6I_CSI_IF_CFG_SRC_TYPE_INTERLACED (1 << 21) +#define SUN6I_CSI_IF_CFG_FPS_DS BIT(20) +#define SUN6I_CSI_IF_CFG_FIELD_POSITIVE (0 << 19) +#define SUN6I_CSI_IF_CFG_FIELD_NEGATIVE (1 << 19) +#define SUN6I_CSI_IF_CFG_VREF_POL_POSITIVE (0 << 18) +#define SUN6I_CSI_IF_CFG_VREF_POL_NEGATIVE (1 << 18) +#define SUN6I_CSI_IF_CFG_HREF_POL_POSITIVE (0 << 17) +#define SUN6I_CSI_IF_CFG_HREF_POL_NEGATIVE (1 << 17) +#define SUN6I_CSI_IF_CFG_CLK_POL_FALLING (0 << 16) +#define SUN6I_CSI_IF_CFG_CLK_POL_RISING (1 << 16) +#define SUN6I_CSI_IF_CFG_FIELD_DT_FIELD_VSYNC (0 << 14) +#define SUN6I_CSI_IF_CFG_FIELD_DT_FIELD (1 << 14) +#define SUN6I_CSI_IF_CFG_FIELD_DT_VSYNC (2 << 14) +#define SUN6I_CSI_IF_CFG_DATA_WIDTH_8 (0 << 8) +#define SUN6I_CSI_IF_CFG_DATA_WIDTH_10 (1 << 8) +#define SUN6I_CSI_IF_CFG_DATA_WIDTH_12 (2 << 8) +#define SUN6I_CSI_IF_CFG_DATA_WIDTH_8_PLUS_2 (3 << 8) +#define SUN6I_CSI_IF_CFG_DATA_WIDTH_2_TIMES_8 (4 << 8) +#define SUN6I_CSI_IF_CFG_IF_CSI (0 << 7) +#define SUN6I_CSI_IF_CFG_IF_MIPI (1 << 7) +#define SUN6I_CSI_IF_CFG_IF_CSI_YUV_RAW (0 << 0) +#define SUN6I_CSI_IF_CFG_IF_CSI_YUV_COMBINED (1 << 0) +#define SUN6I_CSI_IF_CFG_IF_CSI_BT656 (4 << 0) +#define SUN6I_CSI_IF_CFG_IF_CSI_BT1120 (5 << 0) + +#define SUN6I_CSI_CAP_REG 0x8 +#define SUN6I_CSI_CAP_MASK(v) (((v) << 2) & GENMASK(5, 2)) +#define SUN6I_CSI_CAP_VCAP_ON BIT(1) +#define SUN6I_CSI_CAP_SCAP_ON BIT(0) + +#define SUN6I_CSI_SYNC_CNT_REG 0xc +#define SUN6I_CSI_FIFO_THRS_REG 0x10 +#define SUN6I_CSI_BT656_HEAD_CFG_REG 0x14 + +#define SUN6I_CSI_PTN_LEN_REG 0x30 +#define SUN6I_CSI_PTN_ADDR_REG 0x34 +#define SUN6I_CSI_VER_REG 0x3c + +#define SUN6I_CSI_CH_CFG_REG 0x44 +#define SUN6I_CSI_CH_CFG_PAD_VAL(v) (((v) << 24) & GENMASK(31, 24)) +#define SUN6I_CSI_CH_CFG_INPUT_FMT(v) (((v) << 20) & GENMASK(23, 20)) +#define SUN6I_CSI_CH_CFG_OUTPUT_FMT(v) (((v) << 16) & GENMASK(19, 16)) +#define SUN6I_CSI_CH_CFG_VFLIP_EN BIT(13) +#define SUN6I_CSI_CH_CFG_HFLIP_EN BIT(12) +#define SUN6I_CSI_CH_CFG_FIELD_SEL_FIELD0 (0 << 10) +#define SUN6I_CSI_CH_CFG_FIELD_SEL_FIELD1 (1 << 10) +#define SUN6I_CSI_CH_CFG_FIELD_SEL_EITHER (2 << 10) +#define SUN6I_CSI_CH_CFG_INPUT_YUV_SEQ(v) (((v) << 8) & GENMASK(9, 8)) + +#define SUN6I_CSI_INPUT_FMT_RAW 0 +#define SUN6I_CSI_INPUT_FMT_YUV422 3 +#define SUN6I_CSI_INPUT_FMT_YUV420 4 + +/* Note that Allwinner manuals and code invert frame/field definitions. */ + +/* RAW */ +#define SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_8 0 +#define SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_10 1 +#define SUN6I_CSI_OUTPUT_FMT_FRAME_RAW_12 2 +#define SUN6I_CSI_OUTPUT_FMT_FRAME_RGB565 4 +#define SUN6I_CSI_OUTPUT_FMT_FRAME_RGB888 5 +#define SUN6I_CSI_OUTPUT_FMT_FRAME_PRGB888 6 +#define SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_8 8 +#define SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_10 9 +#define SUN6I_CSI_OUTPUT_FMT_FIELD_RAW_12 10 +#define SUN6I_CSI_OUTPUT_FMT_FIELD_RGB565 12 +#define SUN6I_CSI_OUTPUT_FMT_FIELD_RGB888 13 +#define SUN6I_CSI_OUTPUT_FMT_FIELD_PRGB888 14 + +/* YUV */ +#define SUN6I_CSI_OUTPUT_FMT_FRAME_YUV422P 0 +#define SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420P 1 +#define SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420P 2 +#define SUN6I_CSI_OUTPUT_FMT_FIELD_YUV422P 3 +#define SUN6I_CSI_OUTPUT_FMT_FRAME_YUV422SP 4 +#define SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420SP 5 +#define SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420SP 6 +#define SUN6I_CSI_OUTPUT_FMT_FIELD_YUV422SP 7 +#define SUN6I_CSI_OUTPUT_FMT_FRAME_YUV422MB 8 +#define SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420MB 9 +#define SUN6I_CSI_OUTPUT_FMT_FIELD_YUV420MB 10 +#define SUN6I_CSI_OUTPUT_FMT_FIELD_YUV422MB 11 +#define SUN6I_CSI_OUTPUT_FMT_FRAME_YUV422SP_10 12 +#define SUN6I_CSI_OUTPUT_FMT_FRAME_YUV420SP_10 13 + +/* YUV Planar */ +#define SUN6I_CSI_INPUT_YUV_SEQ_YUYV 0 +#define SUN6I_CSI_INPUT_YUV_SEQ_YVYU 1 +#define SUN6I_CSI_INPUT_YUV_SEQ_UYVY 2 +#define SUN6I_CSI_INPUT_YUV_SEQ_VYUY 3 + +/* YUV Semi-planar */ +#define SUN6I_CSI_INPUT_YUV_SEQ_UV 0 +#define SUN6I_CSI_INPUT_YUV_SEQ_VU 1 + +#define SUN6I_CSI_CH_SCALE_REG 0x4c +#define SUN6I_CSI_CH_SCALE_QUART_EN BIT(0) + +#define SUN6I_CSI_CH_FIFO0_ADDR_REG 0x50 +#define SUN6I_CSI_CH_FIFO1_ADDR_REG 0x58 +#define SUN6I_CSI_CH_FIFO2_ADDR_REG 0x60 + +#define SUN6I_CSI_CH_STA_REG 0x6c +#define SUN6I_CSI_CH_STA_FIELD BIT(2) +#define SUN6I_CSI_CH_STA_VCAP BIT(1) +#define SUN6I_CSI_CH_STA_SCAP BIT(0) + +#define SUN6I_CSI_CH_INT_EN_REG 0x70 +#define SUN6I_CSI_CH_INT_EN_VS BIT(7) +#define SUN6I_CSI_CH_INT_EN_HB_OF BIT(6) +#define SUN6I_CSI_CH_INT_EN_MUL_ERR BIT(5) +#define SUN6I_CSI_CH_INT_EN_FIFO2_OF BIT(4) +#define SUN6I_CSI_CH_INT_EN_FIFO1_OF BIT(3) +#define SUN6I_CSI_CH_INT_EN_FIFO0_OF BIT(2) +#define SUN6I_CSI_CH_INT_EN_FD BIT(1) +#define SUN6I_CSI_CH_INT_EN_CD BIT(0) + +#define SUN6I_CSI_CH_INT_STA_REG 0x74 +#define SUN6I_CSI_CH_INT_STA_CLEAR 0xff +#define SUN6I_CSI_CH_INT_STA_VS BIT(7) +#define SUN6I_CSI_CH_INT_STA_HB_OF BIT(6) +#define SUN6I_CSI_CH_INT_STA_MUL_ERR BIT(5) +#define SUN6I_CSI_CH_INT_STA_FIFO2_OF BIT(4) +#define SUN6I_CSI_CH_INT_STA_FIFO1_OF BIT(3) +#define SUN6I_CSI_CH_INT_STA_FIFO0_OF BIT(2) +#define SUN6I_CSI_CH_INT_STA_FD BIT(1) +#define SUN6I_CSI_CH_INT_STA_CD BIT(0) + +#define SUN6I_CSI_CH_FLD1_VSIZE_REG 0x78 +#define SUN6I_CSI_CH_FLD1_VSIZE_VER_LEN(v) (((v) << 16) & GENMASK(28, 16)) +#define SUN6I_CSI_CH_FLD1_VSIZE_VER_START(v) ((v) & GENMASK(12, 0)) + +#define SUN6I_CSI_CH_HSIZE_REG 0x80 +#define SUN6I_CSI_CH_HSIZE_LEN(v) (((v) << 16) & GENMASK(28, 16)) +#define SUN6I_CSI_CH_HSIZE_START(v) ((v) & GENMASK(12, 0)) + +#define SUN6I_CSI_CH_VSIZE_REG 0x84 +#define SUN6I_CSI_CH_VSIZE_LEN(v) (((v) << 16) & GENMASK(28, 16)) +#define SUN6I_CSI_CH_VSIZE_START(v) ((v) & GENMASK(12, 0)) + +#define SUN6I_CSI_CH_BUF_LEN_REG 0x88 +#define SUN6I_CSI_CH_BUF_LEN_CHROMA_LINE(v) (((v) << 16) & GENMASK(29, 16)) +#define SUN6I_CSI_CH_BUF_LEN_LUMA_LINE(v) ((v) & GENMASK(13, 0)) + +#define SUN6I_CSI_CH_FLIP_SIZE_REG 0x8c +#define SUN6I_CSI_CH_FLIP_SIZE_VER_LEN(v) (((v) << 16) & GENMASK(28, 16)) +#define SUN6I_CSI_CH_FLIP_SIZE_VALID_LEN(v) ((v) & GENMASK(12, 0)) + +#define SUN6I_CSI_CH_FRM_CLK_CNT_REG 0x90 +#define SUN6I_CSI_CH_ACC_ITNL_CLK_CNT_REG 0x94 +#define SUN6I_CSI_CH_FIFO_STAT_REG 0x98 +#define SUN6I_CSI_CH_PCLK_STAT_REG 0x9c + +#endif diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c deleted file mode 100644 index 791583d23a65..000000000000 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c +++ /dev/null @@ -1,733 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 2011-2018 Magewell Electronics Co., Ltd. (Nanjing) - * All rights reserved. - * Author: Yong Deng <[email protected]> - */ - -#include <linux/of.h> - -#include <media/v4l2-device.h> -#include <media/v4l2-event.h> -#include <media/v4l2-ioctl.h> -#include <media/v4l2-mc.h> -#include <media/videobuf2-dma-contig.h> -#include <media/videobuf2-v4l2.h> - -#include "sun6i_csi.h" -#include "sun6i_video.h" - -/* This is got from BSP sources. */ -#define MIN_WIDTH (32) -#define MIN_HEIGHT (32) -#define MAX_WIDTH (4800) -#define MAX_HEIGHT (4800) - -/* Helpers */ - -static struct v4l2_subdev * -sun6i_video_remote_subdev(struct sun6i_video *video, u32 *pad) -{ - struct media_pad *remote; - - remote = media_pad_remote_pad_first(&video->pad); - - if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) - return NULL; - - if (pad) - *pad = remote->index; - - return media_entity_to_v4l2_subdev(remote->entity); -} - -/* Format */ - -static const u32 sun6i_video_formats[] = { - V4L2_PIX_FMT_SBGGR8, - V4L2_PIX_FMT_SGBRG8, - V4L2_PIX_FMT_SGRBG8, - V4L2_PIX_FMT_SRGGB8, - V4L2_PIX_FMT_SBGGR10, - V4L2_PIX_FMT_SGBRG10, - V4L2_PIX_FMT_SGRBG10, - V4L2_PIX_FMT_SRGGB10, - V4L2_PIX_FMT_SBGGR12, - V4L2_PIX_FMT_SGBRG12, - V4L2_PIX_FMT_SGRBG12, - V4L2_PIX_FMT_SRGGB12, - V4L2_PIX_FMT_YUYV, - V4L2_PIX_FMT_YVYU, - V4L2_PIX_FMT_UYVY, - V4L2_PIX_FMT_VYUY, - V4L2_PIX_FMT_NV12_16L16, - V4L2_PIX_FMT_NV12, - V4L2_PIX_FMT_NV21, - V4L2_PIX_FMT_YUV420, - V4L2_PIX_FMT_YVU420, - V4L2_PIX_FMT_NV16, - V4L2_PIX_FMT_NV61, - V4L2_PIX_FMT_YUV422P, - V4L2_PIX_FMT_RGB565, - V4L2_PIX_FMT_RGB565X, - V4L2_PIX_FMT_JPEG, -}; - -static bool sun6i_video_format_check(u32 format) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(sun6i_video_formats); i++) - if (sun6i_video_formats[i] == format) - return true; - - return false; -} - -/* Video */ - -static void sun6i_video_buffer_configure(struct sun6i_csi_device *csi_dev, - struct sun6i_csi_buffer *csi_buffer) -{ - csi_buffer->queued_to_csi = true; - sun6i_csi_update_buf_addr(csi_dev, csi_buffer->dma_addr); -} - -static void sun6i_video_configure(struct sun6i_csi_device *csi_dev) -{ - struct sun6i_video *video = &csi_dev->video; - struct sun6i_csi_config config = { 0 }; - - config.pixelformat = video->format.fmt.pix.pixelformat; - config.code = video->mbus_code; - config.field = video->format.fmt.pix.field; - config.width = video->format.fmt.pix.width; - config.height = video->format.fmt.pix.height; - - sun6i_csi_update_config(csi_dev, &config); -} - -/* Queue */ - -static int sun6i_video_queue_setup(struct vb2_queue *queue, - unsigned int *buffers_count, - unsigned int *planes_count, - unsigned int sizes[], - struct device *alloc_devs[]) -{ - struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(queue); - struct sun6i_video *video = &csi_dev->video; - unsigned int size = video->format.fmt.pix.sizeimage; - - if (*planes_count) - return sizes[0] < size ? -EINVAL : 0; - - *planes_count = 1; - sizes[0] = size; - - return 0; -} - -static int sun6i_video_buffer_prepare(struct vb2_buffer *buffer) -{ - struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(buffer->vb2_queue); - struct sun6i_video *video = &csi_dev->video; - struct v4l2_device *v4l2_dev = &csi_dev->v4l2.v4l2_dev; - struct vb2_v4l2_buffer *v4l2_buffer = to_vb2_v4l2_buffer(buffer); - struct sun6i_csi_buffer *csi_buffer = - container_of(v4l2_buffer, struct sun6i_csi_buffer, v4l2_buffer); - unsigned long size = video->format.fmt.pix.sizeimage; - - if (vb2_plane_size(buffer, 0) < size) { - v4l2_err(v4l2_dev, "buffer too small (%lu < %lu)\n", - vb2_plane_size(buffer, 0), size); - return -EINVAL; - } - - vb2_set_plane_payload(buffer, 0, size); - - csi_buffer->dma_addr = vb2_dma_contig_plane_dma_addr(buffer, 0); - v4l2_buffer->field = video->format.fmt.pix.field; - - return 0; -} - -static void sun6i_video_buffer_queue(struct vb2_buffer *buffer) -{ - struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(buffer->vb2_queue); - struct sun6i_video *video = &csi_dev->video; - struct vb2_v4l2_buffer *v4l2_buffer = to_vb2_v4l2_buffer(buffer); - struct sun6i_csi_buffer *csi_buffer = - container_of(v4l2_buffer, struct sun6i_csi_buffer, v4l2_buffer); - unsigned long flags; - - spin_lock_irqsave(&video->dma_queue_lock, flags); - csi_buffer->queued_to_csi = false; - list_add_tail(&csi_buffer->list, &video->dma_queue); - spin_unlock_irqrestore(&video->dma_queue_lock, flags); -} - -static int sun6i_video_start_streaming(struct vb2_queue *queue, - unsigned int count) -{ - struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(queue); - struct sun6i_video *video = &csi_dev->video; - struct video_device *video_dev = &video->video_dev; - struct sun6i_csi_buffer *buf; - struct sun6i_csi_buffer *next_buf; - struct v4l2_subdev *subdev; - unsigned long flags; - int ret; - - video->sequence = 0; - - ret = video_device_pipeline_alloc_start(video_dev); - if (ret < 0) - goto error_dma_queue_flush; - - if (video->mbus_code == 0) { - ret = -EINVAL; - goto error_media_pipeline; - } - - subdev = sun6i_video_remote_subdev(video, NULL); - if (!subdev) { - ret = -EINVAL; - goto error_media_pipeline; - } - - sun6i_video_configure(csi_dev); - - spin_lock_irqsave(&video->dma_queue_lock, flags); - - buf = list_first_entry(&video->dma_queue, - struct sun6i_csi_buffer, list); - sun6i_video_buffer_configure(csi_dev, buf); - - sun6i_csi_set_stream(csi_dev, true); - - /* - * CSI will lookup the next dma buffer for next frame before the - * current frame done IRQ triggered. This is not documented - * but reported by OndÅ™ej Jirman. - * The BSP code has workaround for this too. It skip to mark the - * first buffer as frame done for VB2 and pass the second buffer - * to CSI in the first frame done ISR call. Then in second frame - * done ISR call, it mark the first buffer as frame done for VB2 - * and pass the third buffer to CSI. And so on. The bad thing is - * that the first buffer will be written twice and the first frame - * is dropped even the queued buffer is sufficient. - * So, I make some improvement here. Pass the next buffer to CSI - * just follow starting the CSI. In this case, the first frame - * will be stored in first buffer, second frame in second buffer. - * This method is used to avoid dropping the first frame, it - * would also drop frame when lacking of queued buffer. - */ - next_buf = list_next_entry(buf, list); - sun6i_video_buffer_configure(csi_dev, next_buf); - - spin_unlock_irqrestore(&video->dma_queue_lock, flags); - - ret = v4l2_subdev_call(subdev, video, s_stream, 1); - if (ret && ret != -ENOIOCTLCMD) - goto error_stream; - - return 0; - -error_stream: - sun6i_csi_set_stream(csi_dev, false); - -error_media_pipeline: - video_device_pipeline_stop(video_dev); - -error_dma_queue_flush: - spin_lock_irqsave(&video->dma_queue_lock, flags); - list_for_each_entry(buf, &video->dma_queue, list) - vb2_buffer_done(&buf->v4l2_buffer.vb2_buf, - VB2_BUF_STATE_QUEUED); - INIT_LIST_HEAD(&video->dma_queue); - spin_unlock_irqrestore(&video->dma_queue_lock, flags); - - return ret; -} - -static void sun6i_video_stop_streaming(struct vb2_queue *queue) -{ - struct sun6i_csi_device *csi_dev = vb2_get_drv_priv(queue); - struct sun6i_video *video = &csi_dev->video; - struct v4l2_subdev *subdev; - unsigned long flags; - struct sun6i_csi_buffer *buf; - - subdev = sun6i_video_remote_subdev(video, NULL); - if (subdev) - v4l2_subdev_call(subdev, video, s_stream, 0); - - sun6i_csi_set_stream(csi_dev, false); - - video_device_pipeline_stop(&video->video_dev); - - /* Release all active buffers */ - spin_lock_irqsave(&video->dma_queue_lock, flags); - list_for_each_entry(buf, &video->dma_queue, list) - vb2_buffer_done(&buf->v4l2_buffer.vb2_buf, VB2_BUF_STATE_ERROR); - INIT_LIST_HEAD(&video->dma_queue); - spin_unlock_irqrestore(&video->dma_queue_lock, flags); -} - -void sun6i_video_frame_done(struct sun6i_csi_device *csi_dev) -{ - struct sun6i_video *video = &csi_dev->video; - struct sun6i_csi_buffer *buf; - struct sun6i_csi_buffer *next_buf; - struct vb2_v4l2_buffer *v4l2_buffer; - - spin_lock(&video->dma_queue_lock); - - buf = list_first_entry(&video->dma_queue, - struct sun6i_csi_buffer, list); - if (list_is_last(&buf->list, &video->dma_queue)) { - dev_dbg(csi_dev->dev, "Frame dropped!\n"); - goto complete; - } - - next_buf = list_next_entry(buf, list); - /* If a new buffer (#next_buf) had not been queued to CSI, the old - * buffer (#buf) is still holding by CSI for storing the next - * frame. So, we queue a new buffer (#next_buf) to CSI then wait - * for next ISR call. - */ - if (!next_buf->queued_to_csi) { - sun6i_video_buffer_configure(csi_dev, next_buf); - dev_dbg(csi_dev->dev, "Frame dropped!\n"); - goto complete; - } - - list_del(&buf->list); - v4l2_buffer = &buf->v4l2_buffer; - v4l2_buffer->vb2_buf.timestamp = ktime_get_ns(); - v4l2_buffer->sequence = video->sequence; - vb2_buffer_done(&v4l2_buffer->vb2_buf, VB2_BUF_STATE_DONE); - - /* Prepare buffer for next frame but one. */ - if (!list_is_last(&next_buf->list, &video->dma_queue)) { - next_buf = list_next_entry(next_buf, list); - sun6i_video_buffer_configure(csi_dev, next_buf); - } else { - dev_dbg(csi_dev->dev, "Next frame will be dropped!\n"); - } - -complete: - video->sequence++; - spin_unlock(&video->dma_queue_lock); -} - -static const struct vb2_ops sun6i_video_queue_ops = { - .queue_setup = sun6i_video_queue_setup, - .buf_prepare = sun6i_video_buffer_prepare, - .buf_queue = sun6i_video_buffer_queue, - .start_streaming = sun6i_video_start_streaming, - .stop_streaming = sun6i_video_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -/* V4L2 Device */ - -static int sun6i_video_querycap(struct file *file, void *private, - struct v4l2_capability *capability) -{ - struct sun6i_csi_device *csi_dev = video_drvdata(file); - struct video_device *video_dev = &csi_dev->video.video_dev; - - strscpy(capability->driver, SUN6I_CSI_NAME, sizeof(capability->driver)); - strscpy(capability->card, video_dev->name, sizeof(capability->card)); - snprintf(capability->bus_info, sizeof(capability->bus_info), - "platform:%s", dev_name(csi_dev->dev)); - - return 0; -} - -static int sun6i_video_enum_fmt(struct file *file, void *private, - struct v4l2_fmtdesc *fmtdesc) -{ - u32 index = fmtdesc->index; - - if (index >= ARRAY_SIZE(sun6i_video_formats)) - return -EINVAL; - - fmtdesc->pixelformat = sun6i_video_formats[index]; - - return 0; -} - -static int sun6i_video_g_fmt(struct file *file, void *private, - struct v4l2_format *format) -{ - struct sun6i_csi_device *csi_dev = video_drvdata(file); - struct sun6i_video *video = &csi_dev->video; - - *format = video->format; - - return 0; -} - -static int sun6i_video_format_try(struct sun6i_video *video, - struct v4l2_format *format) -{ - struct v4l2_pix_format *pix_format = &format->fmt.pix; - int bpp; - - if (!sun6i_video_format_check(pix_format->pixelformat)) - pix_format->pixelformat = sun6i_video_formats[0]; - - v4l_bound_align_image(&pix_format->width, MIN_WIDTH, MAX_WIDTH, 1, - &pix_format->height, MIN_HEIGHT, MAX_WIDTH, 1, 1); - - bpp = sun6i_csi_get_bpp(pix_format->pixelformat); - pix_format->bytesperline = (pix_format->width * bpp) >> 3; - pix_format->sizeimage = pix_format->bytesperline * pix_format->height; - - if (pix_format->field == V4L2_FIELD_ANY) - pix_format->field = V4L2_FIELD_NONE; - - if (pix_format->pixelformat == V4L2_PIX_FMT_JPEG) - pix_format->colorspace = V4L2_COLORSPACE_JPEG; - else - pix_format->colorspace = V4L2_COLORSPACE_SRGB; - - pix_format->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; - pix_format->quantization = V4L2_QUANTIZATION_DEFAULT; - pix_format->xfer_func = V4L2_XFER_FUNC_DEFAULT; - - return 0; -} - -static int sun6i_video_format_set(struct sun6i_video *video, - struct v4l2_format *format) -{ - int ret; - - ret = sun6i_video_format_try(video, format); - if (ret) - return ret; - - video->format = *format; - - return 0; -} - -static int sun6i_video_s_fmt(struct file *file, void *private, - struct v4l2_format *format) -{ - struct sun6i_csi_device *csi_dev = video_drvdata(file); - struct sun6i_video *video = &csi_dev->video; - - if (vb2_is_busy(&video->queue)) - return -EBUSY; - - return sun6i_video_format_set(video, format); -} - -static int sun6i_video_try_fmt(struct file *file, void *private, - struct v4l2_format *format) -{ - struct sun6i_csi_device *csi_dev = video_drvdata(file); - struct sun6i_video *video = &csi_dev->video; - - return sun6i_video_format_try(video, format); -} - -static int sun6i_video_enum_input(struct file *file, void *private, - struct v4l2_input *input) -{ - if (input->index != 0) - return -EINVAL; - - input->type = V4L2_INPUT_TYPE_CAMERA; - strscpy(input->name, "Camera", sizeof(input->name)); - - return 0; -} - -static int sun6i_video_g_input(struct file *file, void *private, - unsigned int *index) -{ - *index = 0; - - return 0; -} - -static int sun6i_video_s_input(struct file *file, void *private, - unsigned int index) -{ - if (index != 0) - return -EINVAL; - - return 0; -} - -static const struct v4l2_ioctl_ops sun6i_video_ioctl_ops = { - .vidioc_querycap = sun6i_video_querycap, - - .vidioc_enum_fmt_vid_cap = sun6i_video_enum_fmt, - .vidioc_g_fmt_vid_cap = sun6i_video_g_fmt, - .vidioc_s_fmt_vid_cap = sun6i_video_s_fmt, - .vidioc_try_fmt_vid_cap = sun6i_video_try_fmt, - - .vidioc_enum_input = sun6i_video_enum_input, - .vidioc_g_input = sun6i_video_g_input, - .vidioc_s_input = sun6i_video_s_input, - - .vidioc_create_bufs = vb2_ioctl_create_bufs, - .vidioc_prepare_buf = vb2_ioctl_prepare_buf, - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_expbuf = vb2_ioctl_expbuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, -}; - -/* V4L2 File */ - -static int sun6i_video_open(struct file *file) -{ - struct sun6i_csi_device *csi_dev = video_drvdata(file); - struct sun6i_video *video = &csi_dev->video; - int ret = 0; - - if (mutex_lock_interruptible(&video->lock)) - return -ERESTARTSYS; - - ret = v4l2_fh_open(file); - if (ret < 0) - goto error_lock; - - ret = v4l2_pipeline_pm_get(&video->video_dev.entity); - if (ret < 0) - goto error_v4l2_fh; - - /* Power on at first open. */ - if (v4l2_fh_is_singular_file(file)) { - ret = sun6i_csi_set_power(csi_dev, true); - if (ret < 0) - goto error_v4l2_fh; - } - - mutex_unlock(&video->lock); - - return 0; - -error_v4l2_fh: - v4l2_fh_release(file); - -error_lock: - mutex_unlock(&video->lock); - - return ret; -} - -static int sun6i_video_close(struct file *file) -{ - struct sun6i_csi_device *csi_dev = video_drvdata(file); - struct sun6i_video *video = &csi_dev->video; - bool last_close; - - mutex_lock(&video->lock); - - last_close = v4l2_fh_is_singular_file(file); - - _vb2_fop_release(file, NULL); - v4l2_pipeline_pm_put(&video->video_dev.entity); - - /* Power off at last close. */ - if (last_close) - sun6i_csi_set_power(csi_dev, false); - - mutex_unlock(&video->lock); - - return 0; -} - -static const struct v4l2_file_operations sun6i_video_fops = { - .owner = THIS_MODULE, - .open = sun6i_video_open, - .release = sun6i_video_close, - .unlocked_ioctl = video_ioctl2, - .mmap = vb2_fop_mmap, - .poll = vb2_fop_poll -}; - -/* Media Entity */ - -static int sun6i_video_link_validate_get_format(struct media_pad *pad, - struct v4l2_subdev_format *fmt) -{ - if (is_media_entity_v4l2_subdev(pad->entity)) { - struct v4l2_subdev *sd = - media_entity_to_v4l2_subdev(pad->entity); - - fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; - fmt->pad = pad->index; - return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt); - } - - return -EINVAL; -} - -static int sun6i_video_link_validate(struct media_link *link) -{ - struct video_device *vdev = container_of(link->sink->entity, - struct video_device, entity); - struct sun6i_csi_device *csi_dev = video_get_drvdata(vdev); - struct sun6i_video *video = &csi_dev->video; - struct v4l2_subdev_format source_fmt; - int ret; - - video->mbus_code = 0; - - if (!media_pad_remote_pad_first(link->sink->entity->pads)) { - dev_info(csi_dev->dev, "video node %s pad not connected\n", - vdev->name); - return -ENOLINK; - } - - ret = sun6i_video_link_validate_get_format(link->source, &source_fmt); - if (ret < 0) - return ret; - - if (!sun6i_csi_is_format_supported(csi_dev, - video->format.fmt.pix.pixelformat, - source_fmt.format.code)) { - dev_err(csi_dev->dev, - "Unsupported pixformat: 0x%x with mbus code: 0x%x!\n", - video->format.fmt.pix.pixelformat, - source_fmt.format.code); - return -EPIPE; - } - - if (source_fmt.format.width != video->format.fmt.pix.width || - source_fmt.format.height != video->format.fmt.pix.height) { - dev_err(csi_dev->dev, - "Wrong width or height %ux%u (%ux%u expected)\n", - video->format.fmt.pix.width, video->format.fmt.pix.height, - source_fmt.format.width, source_fmt.format.height); - return -EPIPE; - } - - video->mbus_code = source_fmt.format.code; - - return 0; -} - -static const struct media_entity_operations sun6i_video_media_ops = { - .link_validate = sun6i_video_link_validate -}; - -/* Video */ - -int sun6i_video_setup(struct sun6i_csi_device *csi_dev) -{ - struct sun6i_video *video = &csi_dev->video; - struct v4l2_device *v4l2_dev = &csi_dev->v4l2.v4l2_dev; - struct video_device *video_dev = &video->video_dev; - struct vb2_queue *queue = &video->queue; - struct media_pad *pad = &video->pad; - struct v4l2_format format = { 0 }; - struct v4l2_pix_format *pix_format = &format.fmt.pix; - int ret; - - /* Media Entity */ - - video_dev->entity.ops = &sun6i_video_media_ops; - - /* Media Pad */ - - pad->flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; - - ret = media_entity_pads_init(&video_dev->entity, 1, pad); - if (ret < 0) - return ret; - - /* DMA queue */ - - INIT_LIST_HEAD(&video->dma_queue); - spin_lock_init(&video->dma_queue_lock); - - video->sequence = 0; - - /* Queue */ - - mutex_init(&video->lock); - - queue->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - queue->io_modes = VB2_MMAP | VB2_DMABUF; - queue->buf_struct_size = sizeof(struct sun6i_csi_buffer); - queue->ops = &sun6i_video_queue_ops; - queue->mem_ops = &vb2_dma_contig_memops; - queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - queue->lock = &video->lock; - queue->dev = csi_dev->dev; - queue->drv_priv = csi_dev; - - /* Make sure non-dropped frame. */ - queue->min_buffers_needed = 3; - - ret = vb2_queue_init(queue); - if (ret) { - v4l2_err(v4l2_dev, "failed to initialize vb2 queue: %d\n", ret); - goto error_media_entity; - } - - /* V4L2 Format */ - - format.type = queue->type; - pix_format->pixelformat = sun6i_video_formats[0]; - pix_format->width = 1280; - pix_format->height = 720; - pix_format->field = V4L2_FIELD_NONE; - - sun6i_video_format_set(video, &format); - - /* Video Device */ - - strscpy(video_dev->name, SUN6I_CSI_NAME, sizeof(video_dev->name)); - video_dev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - video_dev->vfl_dir = VFL_DIR_RX; - video_dev->release = video_device_release_empty; - video_dev->fops = &sun6i_video_fops; - video_dev->ioctl_ops = &sun6i_video_ioctl_ops; - video_dev->v4l2_dev = v4l2_dev; - video_dev->queue = queue; - video_dev->lock = &video->lock; - - video_set_drvdata(video_dev, csi_dev); - - ret = video_register_device(video_dev, VFL_TYPE_VIDEO, -1); - if (ret < 0) { - v4l2_err(v4l2_dev, "failed to register video device: %d\n", - ret); - goto error_media_entity; - } - - return 0; - -error_media_entity: - media_entity_cleanup(&video_dev->entity); - - mutex_destroy(&video->lock); - - return ret; -} - -void sun6i_video_cleanup(struct sun6i_csi_device *csi_dev) -{ - struct sun6i_video *video = &csi_dev->video; - struct video_device *video_dev = &video->video_dev; - - vb2_video_unregister_device(video_dev); - media_entity_cleanup(&video_dev->entity); - mutex_destroy(&video->lock); -} diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.h deleted file mode 100644 index a917d2da6deb..000000000000 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.h +++ /dev/null @@ -1,35 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (c) 2011-2018 Magewell Electronics Co., Ltd. (Nanjing) - * All rights reserved. - * Author: Yong Deng <[email protected]> - */ - -#ifndef __SUN6I_VIDEO_H__ -#define __SUN6I_VIDEO_H__ - -#include <media/v4l2-dev.h> -#include <media/videobuf2-core.h> - -struct sun6i_csi_device; - -struct sun6i_video { - struct video_device video_dev; - struct vb2_queue queue; - struct mutex lock; /* Queue lock. */ - struct media_pad pad; - - struct list_head dma_queue; - spinlock_t dma_queue_lock; /* DMA queue lock. */ - - struct v4l2_format format; - u32 mbus_code; - unsigned int sequence; -}; - -int sun6i_video_setup(struct sun6i_csi_device *csi_dev); -void sun6i_video_cleanup(struct sun6i_csi_device *csi_dev); - -void sun6i_video_frame_done(struct sun6i_csi_device *csi_dev); - -#endif /* __SUN6I_VIDEO_H__ */ diff --git a/drivers/media/radio/radio-tea5764.c b/drivers/media/radio/radio-tea5764.c index abda40e81612..2cb74afba49c 100644 --- a/drivers/media/radio/radio-tea5764.c +++ b/drivers/media/radio/radio-tea5764.c @@ -411,8 +411,7 @@ static const struct video_device tea5764_radio_template = { }; /* I2C probe: check if the device exists and register with v4l if it is */ -static int tea5764_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tea5764_i2c_probe(struct i2c_client *client) { struct tea5764_device *radio; struct v4l2_device *v4l2_dev; @@ -512,7 +511,7 @@ static struct i2c_driver tea5764_i2c_driver = { .driver = { .name = "radio-tea5764", }, - .probe = tea5764_i2c_probe, + .probe_new = tea5764_i2c_probe, .remove = tea5764_i2c_remove, .id_table = tea5764_id, }; diff --git a/drivers/media/radio/radio-terratec.c b/drivers/media/radio/radio-terratec.c index 8b8ce2b46a55..621bb8523271 100644 --- a/drivers/media/radio/radio-terratec.c +++ b/drivers/media/radio/radio-terratec.c @@ -82,7 +82,6 @@ static int terratec_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol static int terratec_s_frequency(struct radio_isa_card *isa, u32 freq) { int i; - int p; int temp; long rest; unsigned char buffer[25]; /* we have to bit shift 25 registers */ @@ -93,7 +92,6 @@ static int terratec_s_frequency(struct radio_isa_card *isa, u32 freq) rest = freq * 10 + 10700; /* I once had understood what is going on here */ /* maybe some wise guy (friedhelm?) can comment this stuff */ i = 13; - p = 10; temp = 102400; while (rest != 0) { if (rest % temp == rest) @@ -103,7 +101,6 @@ static int terratec_s_frequency(struct radio_isa_card *isa, u32 freq) rest = rest - temp; } i--; - p--; temp = temp / 2; } diff --git a/drivers/media/radio/saa7706h.c b/drivers/media/radio/saa7706h.c index f9e990a9c3ef..3c758a983344 100644 --- a/drivers/media/radio/saa7706h.c +++ b/drivers/media/radio/saa7706h.c @@ -331,8 +331,7 @@ static const struct v4l2_subdev_ops empty_ops = {}; * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' */ -static int saa7706h_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int saa7706h_probe(struct i2c_client *client) { struct saa7706h_state *state; struct v4l2_subdev *sd; @@ -406,7 +405,7 @@ static struct i2c_driver saa7706h_driver = { .driver = { .name = DRIVER_NAME, }, - .probe = saa7706h_probe, + .probe_new = saa7706h_probe, .remove = saa7706h_remove, .id_table = saa7706h_id, }; diff --git a/drivers/media/radio/tef6862.c b/drivers/media/radio/tef6862.c index 7b0870a9785b..d14c97d79e83 100644 --- a/drivers/media/radio/tef6862.c +++ b/drivers/media/radio/tef6862.c @@ -141,8 +141,7 @@ static const struct v4l2_subdev_ops tef6862_ops = { * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' */ -static int tef6862_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tef6862_probe(struct i2c_client *client) { struct tef6862_state *state; struct v4l2_subdev *sd; @@ -184,7 +183,7 @@ static struct i2c_driver tef6862_driver = { .driver = { .name = DRIVER_NAME, }, - .probe = tef6862_probe, + .probe_new = tef6862_probe, .remove = tef6862_remove, .id_table = tef6862_id, }; diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index 5edfd8a9e849..74546f7e3469 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -646,15 +646,14 @@ static int send_packet(struct imon_context *ictx) pr_err_ratelimited("error submitting urb(%d)\n", retval); } else { /* Wait for transmission to complete (or abort) */ - mutex_unlock(&ictx->lock); retval = wait_for_completion_interruptible( &ictx->tx.finished); if (retval) { usb_kill_urb(ictx->tx_urb); pr_err_ratelimited("task interrupted\n"); } - mutex_lock(&ictx->lock); + ictx->tx.busy = false; retval = ictx->tx.status; if (retval) pr_err_ratelimited("packet tx failed (%d)\n", retval); @@ -953,7 +952,8 @@ static ssize_t vfd_write(struct file *file, const char __user *buf, if (ictx->disconnected) return -ENODEV; - mutex_lock(&ictx->lock); + if (mutex_lock_interruptible(&ictx->lock)) + return -ERESTARTSYS; if (!ictx->dev_present_intf0) { pr_err_ratelimited("no iMON device present\n"); diff --git a/drivers/media/rc/ir-spi.c b/drivers/media/rc/ir-spi.c index 51aa55a84bb5..bbc81bed4f90 100644 --- a/drivers/media/rc/ir-spi.c +++ b/drivers/media/rc/ir-spi.c @@ -158,8 +158,15 @@ static const struct of_device_id ir_spi_of_match[] = { }; MODULE_DEVICE_TABLE(of, ir_spi_of_match); +static const struct spi_device_id ir_spi_ids[] = { + { "ir-spi-led" }, + {}, +}; +MODULE_DEVICE_TABLE(spi, ir_spi_ids); + static struct spi_driver ir_spi_driver = { .probe = ir_spi_probe, + .id_table = ir_spi_ids, .driver = { .name = IR_SPI_DRIVER_NAME, .of_match_table = ir_spi_of_match, diff --git a/drivers/media/test-drivers/vidtv/vidtv_demod.c b/drivers/media/test-drivers/vidtv/vidtv_demod.c index e7959ab1add8..c2300b30407b 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_demod.c +++ b/drivers/media/test-drivers/vidtv/vidtv_demod.c @@ -412,8 +412,7 @@ static const struct i2c_device_id vidtv_demod_i2c_id_table[] = { }; MODULE_DEVICE_TABLE(i2c, vidtv_demod_i2c_id_table); -static int vidtv_demod_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int vidtv_demod_i2c_probe(struct i2c_client *client) { struct vidtv_tuner_config *config = client->dev.platform_data; struct vidtv_demod_state *state; @@ -450,7 +449,7 @@ static struct i2c_driver vidtv_demod_i2c_driver = { .name = "dvb_vidtv_demod", .suppress_bind_attrs = true, }, - .probe = vidtv_demod_i2c_probe, + .probe_new = vidtv_demod_i2c_probe, .remove = vidtv_demod_i2c_remove, .id_table = vidtv_demod_i2c_id_table, }; diff --git a/drivers/media/test-drivers/vidtv/vidtv_tuner.c b/drivers/media/test-drivers/vidtv/vidtv_tuner.c index aabc97ed736b..55a4387f3854 100644 --- a/drivers/media/test-drivers/vidtv/vidtv_tuner.c +++ b/drivers/media/test-drivers/vidtv/vidtv_tuner.c @@ -390,8 +390,7 @@ static const struct i2c_device_id vidtv_tuner_i2c_id_table[] = { }; MODULE_DEVICE_TABLE(i2c, vidtv_tuner_i2c_id_table); -static int vidtv_tuner_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int vidtv_tuner_i2c_probe(struct i2c_client *client) { struct vidtv_tuner_config *config = client->dev.platform_data; struct dvb_frontend *fe = config->fe; @@ -426,7 +425,7 @@ static struct i2c_driver vidtv_tuner_i2c_driver = { .name = "dvb_vidtv_tuner", .suppress_bind_attrs = true, }, - .probe = vidtv_tuner_i2c_probe, + .probe_new = vidtv_tuner_i2c_probe, .remove = vidtv_tuner_i2c_remove, .id_table = vidtv_tuner_i2c_id_table, }; diff --git a/drivers/media/test-drivers/vimc/vimc-core.c b/drivers/media/test-drivers/vimc/vimc-core.c index 2ae7a0f11ebf..e82cfa5ffbf4 100644 --- a/drivers/media/test-drivers/vimc/vimc-core.c +++ b/drivers/media/test-drivers/vimc/vimc-core.c @@ -433,7 +433,7 @@ static int __init vimc_init(void) if (ret) { dev_err(&vimc_pdev.dev, "platform driver registration failed (err=%d)\n", ret); - platform_driver_unregister(&vimc_pdrv); + platform_device_unregister(&vimc_pdev); return ret; } diff --git a/drivers/media/test-drivers/vivid/vivid-ctrls.c b/drivers/media/test-drivers/vivid/vivid-ctrls.c index 92b1a7598470..f2b20e25a7a4 100644 --- a/drivers/media/test-drivers/vivid/vivid-ctrls.c +++ b/drivers/media/test-drivers/vivid/vivid-ctrls.c @@ -36,6 +36,8 @@ #define VIVID_CID_RO_INTEGER (VIVID_CID_CUSTOM_BASE + 12) #define VIVID_CID_U32_DYN_ARRAY (VIVID_CID_CUSTOM_BASE + 13) #define VIVID_CID_U8_PIXEL_ARRAY (VIVID_CID_CUSTOM_BASE + 14) +#define VIVID_CID_S32_ARRAY (VIVID_CID_CUSTOM_BASE + 15) +#define VIVID_CID_S64_ARRAY (VIVID_CID_CUSTOM_BASE + 16) #define VIVID_CID_VIVID_BASE (0x00f00000 | 0xf000) #define VIVID_CID_VIVID_CLASS (0x00f00000 | 1) @@ -241,6 +243,30 @@ static const struct v4l2_ctrl_config vivid_ctrl_u8_pixel_array = { .dims = { 640 / PIXEL_ARRAY_DIV, 360 / PIXEL_ARRAY_DIV }, }; +static const struct v4l2_ctrl_config vivid_ctrl_s32_array = { + .ops = &vivid_user_gen_ctrl_ops, + .id = VIVID_CID_S32_ARRAY, + .name = "S32 2 Element Array", + .type = V4L2_CTRL_TYPE_INTEGER, + .def = 2, + .min = -10, + .max = 10, + .step = 1, + .dims = { 2 }, +}; + +static const struct v4l2_ctrl_config vivid_ctrl_s64_array = { + .ops = &vivid_user_gen_ctrl_ops, + .id = VIVID_CID_S64_ARRAY, + .name = "S64 5 Element Array", + .type = V4L2_CTRL_TYPE_INTEGER64, + .def = 4, + .min = -10, + .max = 10, + .step = 1, + .dims = { 5 }, +}; + static const char * const vivid_ctrl_menu_strings[] = { "Menu Item 0 (Skipped)", "Menu Item 1", @@ -1656,6 +1682,8 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_u16_matrix, NULL); v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_u8_4d_array, NULL); dev->pixel_array = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_u8_pixel_array, NULL); + v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_s32_array, NULL); + v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_s64_array, NULL); if (dev->has_vid_cap) { /* Image Processing Controls */ diff --git a/drivers/media/test-drivers/vivid/vivid-vbi-gen.c b/drivers/media/test-drivers/vivid/vivid-vbi-gen.c index a141369a7a63..70a4024d461e 100644 --- a/drivers/media/test-drivers/vivid/vivid-vbi-gen.c +++ b/drivers/media/test-drivers/vivid/vivid-vbi-gen.c @@ -194,7 +194,6 @@ static void vivid_vbi_gen_set_time_of_day(u8 *packet) for (checksum = i = 0; i <= 8; i++) checksum += packet[i] & 0x7f; packet[9] = calc_parity(0x100 - checksum); - checksum = 0; packet[10] = calc_parity(0x07); packet[11] = calc_parity(0x04); if (sys_tz.tz_minuteswest >= 0) diff --git a/drivers/media/tuners/e4000.c b/drivers/media/tuners/e4000.c index 61ae884ea59a..7c269f3159ef 100644 --- a/drivers/media/tuners/e4000.c +++ b/drivers/media/tuners/e4000.c @@ -609,8 +609,7 @@ static const struct dvb_tuner_ops e4000_dvb_tuner_ops = { .get_if_frequency = e4000_dvb_get_if_frequency, }; -static int e4000_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int e4000_probe(struct i2c_client *client) { struct e4000_dev *dev; struct e4000_config *cfg = client->dev.platform_data; @@ -730,7 +729,7 @@ static struct i2c_driver e4000_driver = { .name = "e4000", .suppress_bind_attrs = true, }, - .probe = e4000_probe, + .probe_new = e4000_probe, .remove = e4000_remove, .id_table = e4000_id_table, }; diff --git a/drivers/media/tuners/fc2580.c b/drivers/media/tuners/fc2580.c index f30932e1a0f3..3cd8279f4f2e 100644 --- a/drivers/media/tuners/fc2580.c +++ b/drivers/media/tuners/fc2580.c @@ -506,8 +506,7 @@ static struct v4l2_subdev *fc2580_get_v4l2_subdev(struct i2c_client *client) return NULL; } -static int fc2580_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int fc2580_probe(struct i2c_client *client) { struct fc2580_dev *dev; struct fc2580_platform_data *pdata = client->dev.platform_data; @@ -611,7 +610,7 @@ static struct i2c_driver fc2580_driver = { .name = "fc2580", .suppress_bind_attrs = true, }, - .probe = fc2580_probe, + .probe_new = fc2580_probe, .remove = fc2580_remove, .id_table = fc2580_id_table, }; diff --git a/drivers/media/tuners/m88rs6000t.c b/drivers/media/tuners/m88rs6000t.c index e32e3e9daa15..7d172a5a66d9 100644 --- a/drivers/media/tuners/m88rs6000t.c +++ b/drivers/media/tuners/m88rs6000t.c @@ -573,8 +573,7 @@ static const struct dvb_tuner_ops m88rs6000t_tuner_ops = { .get_rf_strength = m88rs6000t_get_rf_strength, }; -static int m88rs6000t_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int m88rs6000t_probe(struct i2c_client *client) { struct m88rs6000t_config *cfg = client->dev.platform_data; struct dvb_frontend *fe = cfg->fe; @@ -719,7 +718,7 @@ static struct i2c_driver m88rs6000t_driver = { .driver = { .name = "m88rs6000t", }, - .probe = m88rs6000t_probe, + .probe_new = m88rs6000t_probe, .remove = m88rs6000t_remove, .id_table = m88rs6000t_id, }; diff --git a/drivers/media/tuners/mt2060.c b/drivers/media/tuners/mt2060.c index 322c806228a5..e5d86874adb3 100644 --- a/drivers/media/tuners/mt2060.c +++ b/drivers/media/tuners/mt2060.c @@ -442,8 +442,7 @@ struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter } EXPORT_SYMBOL(mt2060_attach); -static int mt2060_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int mt2060_probe(struct i2c_client *client) { struct mt2060_platform_data *pdata = client->dev.platform_data; struct dvb_frontend *fe; @@ -525,7 +524,7 @@ static struct i2c_driver mt2060_driver = { .name = "mt2060", .suppress_bind_attrs = true, }, - .probe = mt2060_probe, + .probe_new = mt2060_probe, .remove = mt2060_remove, .id_table = mt2060_id_table, }; diff --git a/drivers/media/tuners/mxl301rf.c b/drivers/media/tuners/mxl301rf.c index 6422056185a9..c35442a77ae5 100644 --- a/drivers/media/tuners/mxl301rf.c +++ b/drivers/media/tuners/mxl301rf.c @@ -283,8 +283,7 @@ static const struct dvb_tuner_ops mxl301rf_ops = { }; -static int mxl301rf_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int mxl301rf_probe(struct i2c_client *client) { struct mxl301rf_state *state; struct mxl301rf_config *cfg; @@ -327,7 +326,7 @@ static struct i2c_driver mxl301rf_driver = { .driver = { .name = "mxl301rf", }, - .probe = mxl301rf_probe, + .probe_new = mxl301rf_probe, .remove = mxl301rf_remove, .id_table = mxl301rf_id, }; diff --git a/drivers/media/tuners/mxl5005s.c b/drivers/media/tuners/mxl5005s.c index ab4c43df9d18..3a509038c8df 100644 --- a/drivers/media/tuners/mxl5005s.c +++ b/drivers/media/tuners/mxl5005s.c @@ -3637,7 +3637,7 @@ static u16 MXL_GetCHRegister_ZeroIF(struct dvb_frontend *fe, u8 *RegNum, u16 status = 0; int i; - u8 RegAddr[] = {43, 136}; + static const u8 RegAddr[] = {43, 136}; *count = ARRAY_SIZE(RegAddr); diff --git a/drivers/media/tuners/qm1d1b0004.c b/drivers/media/tuners/qm1d1b0004.c index 9cba0893207c..0b6f750c54ad 100644 --- a/drivers/media/tuners/qm1d1b0004.c +++ b/drivers/media/tuners/qm1d1b0004.c @@ -197,7 +197,7 @@ static const struct dvb_tuner_ops qm1d1b0004_ops = { }; static int -qm1d1b0004_probe(struct i2c_client *client, const struct i2c_device_id *id) +qm1d1b0004_probe(struct i2c_client *client) { struct dvb_frontend *fe; struct qm1d1b0004_config *cfg; @@ -253,7 +253,7 @@ static struct i2c_driver qm1d1b0004_driver = { .driver = { .name = "qm1d1b0004", }, - .probe = qm1d1b0004_probe, + .probe_new = qm1d1b0004_probe, .remove = qm1d1b0004_remove, .id_table = qm1d1b0004_id, }; diff --git a/drivers/media/tuners/qm1d1c0042.c b/drivers/media/tuners/qm1d1c0042.c index 2d60bf501fb5..f9be7a721d2c 100644 --- a/drivers/media/tuners/qm1d1c0042.c +++ b/drivers/media/tuners/qm1d1c0042.c @@ -401,8 +401,7 @@ static const struct dvb_tuner_ops qm1d1c0042_ops = { }; -static int qm1d1c0042_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int qm1d1c0042_probe(struct i2c_client *client) { struct qm1d1c0042_state *state; struct qm1d1c0042_config *cfg; @@ -444,7 +443,7 @@ static struct i2c_driver qm1d1c0042_driver = { .driver = { .name = "qm1d1c0042", }, - .probe = qm1d1c0042_probe, + .probe_new = qm1d1c0042_probe, .remove = qm1d1c0042_remove, .id_table = qm1d1c0042_id, }; diff --git a/drivers/media/tuners/tda18212.c b/drivers/media/tuners/tda18212.c index eb97711c9c68..5fdf05a97415 100644 --- a/drivers/media/tuners/tda18212.c +++ b/drivers/media/tuners/tda18212.c @@ -173,8 +173,7 @@ static const struct dvb_tuner_ops tda18212_tuner_ops = { .get_if_frequency = tda18212_get_if_frequency, }; -static int tda18212_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tda18212_probe(struct i2c_client *client) { struct tda18212_config *cfg = client->dev.platform_data; struct dvb_frontend *fe = cfg->fe; @@ -264,7 +263,7 @@ static struct i2c_driver tda18212_driver = { .driver = { .name = "tda18212", }, - .probe = tda18212_probe, + .probe_new = tda18212_probe, .remove = tda18212_remove, .id_table = tda18212_id, }; diff --git a/drivers/media/tuners/tda18250.c b/drivers/media/tuners/tda18250.c index e404a5afad4c..66ff2d035de7 100644 --- a/drivers/media/tuners/tda18250.c +++ b/drivers/media/tuners/tda18250.c @@ -741,8 +741,7 @@ static const struct dvb_tuner_ops tda18250_ops = { .sleep = tda18250_sleep, }; -static int tda18250_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tda18250_probe(struct i2c_client *client) { struct tda18250_config *cfg = client->dev.platform_data; struct dvb_frontend *fe = cfg->fe; @@ -878,7 +877,7 @@ static struct i2c_driver tda18250_driver = { .driver = { .name = "tda18250", }, - .probe = tda18250_probe, + .probe_new = tda18250_probe, .remove = tda18250_remove, .id_table = tda18250_id_table, }; diff --git a/drivers/media/tuners/tua9001.c b/drivers/media/tuners/tua9001.c index d141d000b819..ac38afd3441a 100644 --- a/drivers/media/tuners/tua9001.c +++ b/drivers/media/tuners/tua9001.c @@ -167,8 +167,7 @@ static const struct dvb_tuner_ops tua9001_tuner_ops = { .get_if_frequency = tua9001_get_if_frequency, }; -static int tua9001_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tua9001_probe(struct i2c_client *client) { struct tua9001_dev *dev; struct tua9001_platform_data *pdata = client->dev.platform_data; @@ -256,7 +255,7 @@ static struct i2c_driver tua9001_driver = { .name = "tua9001", .suppress_bind_attrs = true, }, - .probe = tua9001_probe, + .probe_new = tua9001_probe, .remove = tua9001_remove, .id_table = tua9001_id_table, }; diff --git a/drivers/media/usb/go7007/s2250-board.c b/drivers/media/usb/go7007/s2250-board.c index 2f45188bf9d4..29dfcc6d0b0a 100644 --- a/drivers/media/usb/go7007/s2250-board.c +++ b/drivers/media/usb/go7007/s2250-board.c @@ -494,8 +494,7 @@ static const struct v4l2_subdev_ops s2250_ops = { /* --------------------------------------------------------------------------*/ -static int s2250_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int s2250_probe(struct i2c_client *client) { struct i2c_client *audio; struct i2c_adapter *adapter = client->adapter; @@ -621,7 +620,7 @@ static struct i2c_driver s2250_driver = { .driver = { .name = "s2250", }, - .probe = s2250_probe, + .probe_new = s2250_probe, .remove = s2250_remove, .id_table = s2250_id, }; diff --git a/drivers/media/v4l2-core/tuner-core.c b/drivers/media/v4l2-core/tuner-core.c index 33162dc1daf6..1c0d23c52203 100644 --- a/drivers/media/v4l2-core/tuner-core.c +++ b/drivers/media/v4l2-core/tuner-core.c @@ -614,7 +614,6 @@ static void tuner_lookup(struct i2c_adapter *adap, *tuner_probe - Probes the existing tuners on an I2C bus * * @client: i2c_client descriptor - * @id: not used * * This routine probes for tuners at the expected I2C addresses. On most * cases, if a device answers to a given I2C address, it assumes that the @@ -625,8 +624,7 @@ static void tuner_lookup(struct i2c_adapter *adap, * During client attach, set_type is called by adapter's attach_inform callback. * set_type must then be completed by tuner_probe. */ -static int tuner_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tuner_probe(struct i2c_client *client) { struct tuner *t; struct tuner *radio; @@ -1413,7 +1411,7 @@ static struct i2c_driver tuner_driver = { .name = "tuner", .pm = &tuner_pm_ops, }, - .probe = tuner_probe, + .probe_new = tuner_probe, .remove = tuner_remove, .command = tuner_command, .id_table = tuner_id, diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c index 0dab1d7b90f0..29169170880a 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c @@ -1827,7 +1827,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, else if (type == V4L2_CTRL_TYPE_INTEGER_MENU) qmenu_int = v4l2_ctrl_get_int_menu(id, &qmenu_int_len); - if ((!qmenu && !qmenu_int) || (qmenu_int && max > qmenu_int_len)) { + if ((!qmenu && !qmenu_int) || (qmenu_int && max >= qmenu_int_len)) { handler_set_err(hdl, -EINVAL); return NULL; } diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 8cb4b976064e..de9efa94fec9 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1347,23 +1347,23 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_PIX_FMT_YUV420: descr = "Planar YUV 4:2:0"; break; case V4L2_PIX_FMT_HI240: descr = "8-bit Dithered RGB (BTTV)"; break; case V4L2_PIX_FMT_M420: descr = "YUV 4:2:0 (M420)"; break; - case V4L2_PIX_FMT_NV12: descr = "Y/CbCr 4:2:0"; break; - case V4L2_PIX_FMT_NV21: descr = "Y/CrCb 4:2:0"; break; - case V4L2_PIX_FMT_NV16: descr = "Y/CbCr 4:2:2"; break; - case V4L2_PIX_FMT_NV61: descr = "Y/CrCb 4:2:2"; break; - case V4L2_PIX_FMT_NV24: descr = "Y/CbCr 4:4:4"; break; - case V4L2_PIX_FMT_NV42: descr = "Y/CrCb 4:4:4"; break; - case V4L2_PIX_FMT_P010: descr = "10-bit Y/CbCr 4:2:0"; break; - case V4L2_PIX_FMT_NV12_4L4: descr = "Y/CbCr 4:2:0 (4x4 Linear)"; break; - case V4L2_PIX_FMT_NV12_16L16: descr = "Y/CbCr 4:2:0 (16x16 Linear)"; break; - case V4L2_PIX_FMT_NV12_32L32: descr = "Y/CbCr 4:2:0 (32x32 Linear)"; break; - case V4L2_PIX_FMT_P010_4L4: descr = "10-bit Y/CbCr 4:2:0 (4x4 Linear)"; break; - case V4L2_PIX_FMT_NV12M: descr = "Y/CbCr 4:2:0 (N-C)"; break; - case V4L2_PIX_FMT_NV21M: descr = "Y/CrCb 4:2:0 (N-C)"; break; - case V4L2_PIX_FMT_NV16M: descr = "Y/CbCr 4:2:2 (N-C)"; break; - case V4L2_PIX_FMT_NV61M: descr = "Y/CrCb 4:2:2 (N-C)"; break; - case V4L2_PIX_FMT_NV12MT: descr = "Y/CbCr 4:2:0 (64x32 MB, N-C)"; break; - case V4L2_PIX_FMT_NV12MT_16X16: descr = "Y/CbCr 4:2:0 (16x16 MB, N-C)"; break; + case V4L2_PIX_FMT_NV12: descr = "Y/UV 4:2:0"; break; + case V4L2_PIX_FMT_NV21: descr = "Y/VU 4:2:0"; break; + case V4L2_PIX_FMT_NV16: descr = "Y/UV 4:2:2"; break; + case V4L2_PIX_FMT_NV61: descr = "Y/VU 4:2:2"; break; + case V4L2_PIX_FMT_NV24: descr = "Y/UV 4:4:4"; break; + case V4L2_PIX_FMT_NV42: descr = "Y/VU 4:4:4"; break; + case V4L2_PIX_FMT_P010: descr = "10-bit Y/UV 4:2:0"; break; + case V4L2_PIX_FMT_NV12_4L4: descr = "Y/UV 4:2:0 (4x4 Linear)"; break; + case V4L2_PIX_FMT_NV12_16L16: descr = "Y/UV 4:2:0 (16x16 Linear)"; break; + case V4L2_PIX_FMT_NV12_32L32: descr = "Y/UV 4:2:0 (32x32 Linear)"; break; + case V4L2_PIX_FMT_P010_4L4: descr = "10-bit Y/UV 4:2:0 (4x4 Linear)"; break; + case V4L2_PIX_FMT_NV12M: descr = "Y/UV 4:2:0 (N-C)"; break; + case V4L2_PIX_FMT_NV21M: descr = "Y/VU 4:2:0 (N-C)"; break; + case V4L2_PIX_FMT_NV16M: descr = "Y/UV 4:2:2 (N-C)"; break; + case V4L2_PIX_FMT_NV61M: descr = "Y/VU 4:2:2 (N-C)"; break; + case V4L2_PIX_FMT_NV12MT: descr = "Y/UV 4:2:0 (64x32 MB, N-C)"; break; + case V4L2_PIX_FMT_NV12MT_16X16: descr = "Y/UV 4:2:0 (16x16 MB, N-C)"; break; case V4L2_PIX_FMT_YUV420M: descr = "Planar YUV 4:2:0 (N-C)"; break; case V4L2_PIX_FMT_YVU420M: descr = "Planar YVU 4:2:0 (N-C)"; break; case V4L2_PIX_FMT_YUV422M: descr = "Planar YUV 4:2:2 (N-C)"; break; diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 8a4ca2bd1584..4988a25bd8f4 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -860,7 +860,7 @@ int v4l2_subdev_get_fwnode_pad_1_to_1(struct media_entity *entity, fwnode = fwnode_graph_get_port_parent(endpoint->local_fwnode); fwnode_handle_put(fwnode); - if (dev_fwnode(sd->dev) == fwnode) + if (device_match_fwnode(sd->dev, fwnode)) return endpoint->port; return -ENXIO; diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c index 783f1b88ebf2..87a634bf9ff5 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c @@ -786,8 +786,6 @@ static int gpio_ctrl(struct v4l2_subdev *sd, bool flag) return ret; } -static int power_down(struct v4l2_subdev *sd); - static int power_up(struct v4l2_subdev *sd) { struct gc0310_device *dev = to_gc0310_sensor(sd); @@ -800,6 +798,9 @@ static int power_up(struct v4l2_subdev *sd) return -ENODEV; } + if (dev->power_on) + return 0; /* Already on */ + /* power control */ ret = power_ctrl(sd, 1); if (ret) @@ -820,6 +821,7 @@ static int power_up(struct v4l2_subdev *sd) msleep(100); + dev->power_on = true; return 0; fail_gpio: @@ -844,6 +846,9 @@ static int power_down(struct v4l2_subdev *sd) return -ENODEV; } + if (!dev->power_on) + return 0; /* Already off */ + /* gpio ctrl */ ret = gpio_ctrl(sd, 0); if (ret) { @@ -861,6 +866,7 @@ static int power_down(struct v4l2_subdev *sd) if (ret) dev_err(&client->dev, "vprog failed.\n"); + dev->power_on = false; return ret; } @@ -935,6 +941,9 @@ static int gc0310_set_fmt(struct v4l2_subdev *sd, return 0; } + /* s_power has not been called yet for std v4l2 clients (camorama) */ + power_up(sd); + dev_dbg(&client->dev, "%s: before gc0310_write_reg_array %s\n", __func__, dev->res->desc); ret = startup(sd); @@ -1073,6 +1082,7 @@ static int gc0310_s_config(struct v4l2_subdev *sd, * as first power on by board may not fulfill the * power on sequqence needed by the module */ + dev->power_on = true; /* force power_down() to run */ ret = power_down(sd); if (ret) { dev_err(&client->dev, "gc0310 power-off err.\n"); diff --git a/drivers/staging/media/atomisp/i2c/gc0310.h b/drivers/staging/media/atomisp/i2c/gc0310.h index db643ebc3909..4b9ce681bd93 100644 --- a/drivers/staging/media/atomisp/i2c/gc0310.h +++ b/drivers/staging/media/atomisp/i2c/gc0310.h @@ -152,6 +152,7 @@ struct gc0310_device { int vt_pix_clk_freq_mhz; struct gc0310_resolution *res; u8 type; + bool power_on; }; enum gc0310_tok_type { diff --git a/drivers/staging/media/atomisp/i2c/ov2680.h b/drivers/staging/media/atomisp/i2c/ov2680.h index 4e351196fe34..7ab337b859ad 100644 --- a/drivers/staging/media/atomisp/i2c/ov2680.h +++ b/drivers/staging/media/atomisp/i2c/ov2680.h @@ -485,19 +485,19 @@ static struct ov2680_reg const ov2680_720x592_30fps[] = { static struct ov2680_reg const ov2680_800x600_30fps[] = { {0x3086, 0x01}, {0x370a, 0x23}, - {0x3801, 0x00}, + {0x3801, 0x00}, /* hstart 0 */ {0x3802, 0x00}, - {0x3803, 0x00}, + {0x3803, 0x00}, /* vstart 0 */ {0x3804, 0x06}, - {0x3805, 0x4f}, + {0x3805, 0x4f}, /* hend 1615 */ {0x3806, 0x04}, - {0x3807, 0xbf}, + {0x3807, 0xbf}, /* vend 1215 */ {0x3808, 0x03}, - {0x3809, 0x20}, + {0x3809, 0x20}, /* hsize 800 */ {0x380a, 0x02}, - {0x380b, 0x58}, + {0x380b, 0x58}, /* vsize 600 */ {0x380c, 0x06}, - {0x380d, 0xac}, + {0x380d, 0xac}, /* htotal 1708 */ {0x3810, 0x00}, {0x3811, 0x00}, {0x3812, 0x00}, @@ -524,19 +524,19 @@ static struct ov2680_reg const ov2680_800x600_30fps[] = { static struct ov2680_reg const ov2680_720p_30fps[] = { {0x3086, 0x00}, {0x370a, 0x21}, - {0x3801, 0xa0}, + {0x3801, 0xa0}, /* hstart 160 */ {0x3802, 0x00}, - {0x3803, 0xf2}, + {0x3803, 0xf2}, /* vstart 242 */ {0x3804, 0x05}, - {0x3805, 0xbf}, + {0x3805, 0xbf}, /* hend 1471 */ {0x3806, 0x03}, - {0x3807, 0xdd}, + {0x3807, 0xdd}, /* vend 989 */ {0x3808, 0x05}, - {0x3809, 0x10}, + {0x3809, 0x10}, /* hsize 1296 */ {0x380a, 0x02}, - {0x380b, 0xe0}, + {0x380b, 0xe0}, /* vsize 736 */ {0x380c, 0x06}, - {0x380d, 0xa8}, + {0x380d, 0xa8}, /* htotal 1704 */ {0x3810, 0x00}, {0x3811, 0x08}, {0x3812, 0x00}, @@ -563,19 +563,19 @@ static struct ov2680_reg const ov2680_720p_30fps[] = { static struct ov2680_reg const ov2680_1296x976_30fps[] = { {0x3086, 0x00}, {0x370a, 0x21}, - {0x3801, 0xa0}, + {0x3801, 0xa0}, /* hstart 160 */ {0x3802, 0x00}, - {0x3803, 0x78}, + {0x3803, 0x78}, /* vstart 120 */ {0x3804, 0x05}, - {0x3805, 0xbf}, + {0x3805, 0xbf}, /* hend 1471 */ {0x3806, 0x04}, - {0x3807, 0x57}, + {0x3807, 0x57}, /* vend 1111 */ {0x3808, 0x05}, - {0x3809, 0x10}, + {0x3809, 0x10}, /* hsize 1296 */ {0x380a, 0x03}, - {0x380b, 0xd0}, + {0x380b, 0xd0}, /* vsize 976 */ {0x380c, 0x06}, - {0x380d, 0xa8}, + {0x380d, 0xa8}, /* htotal 1704 */ {0x3810, 0x00}, {0x3811, 0x08}, {0x3812, 0x00}, @@ -820,8 +820,8 @@ static struct ov2680_resolution ov2680_res_preview[] = { .regs = ov2680_1296x976_30fps, }, { - .width = 1280, - .height = 720, + .width = 1296, + .height = 736, .fps = 60, .pix_clk_freq = 66, .pixels_per_line = 1698,//1704, diff --git a/drivers/staging/media/atomisp/include/hmm/hmm.h b/drivers/staging/media/atomisp/include/hmm/hmm.h index c0384bb0a762..2bc323b34f89 100644 --- a/drivers/staging/media/atomisp/include/hmm/hmm.h +++ b/drivers/staging/media/atomisp/include/hmm/hmm.h @@ -37,7 +37,8 @@ int hmm_init(void); void hmm_cleanup(void); ia_css_ptr hmm_alloc(size_t bytes); -ia_css_ptr hmm_create_from_userdata(size_t bytes, const void __user *userptr); +ia_css_ptr hmm_create_from_vmalloc_buf(size_t bytes, void *vmalloc_addr); + void hmm_free(ia_css_ptr ptr); int hmm_load(ia_css_ptr virt, void *data, unsigned int bytes); int hmm_store(ia_css_ptr virt, const void *data, unsigned int bytes); diff --git a/drivers/staging/media/atomisp/include/hmm/hmm_bo.h b/drivers/staging/media/atomisp/include/hmm/hmm_bo.h index c5cbae1d9cf9..b4c03e0ca9c0 100644 --- a/drivers/staging/media/atomisp/include/hmm/hmm_bo.h +++ b/drivers/staging/media/atomisp/include/hmm/hmm_bo.h @@ -73,7 +73,7 @@ enum hmm_bo_type { HMM_BO_PRIVATE, - HMM_BO_USER, + HMM_BO_VMALLOC, HMM_BO_LAST, }; @@ -207,7 +207,7 @@ int hmm_bo_allocated(struct hmm_buffer_object *bo); */ int hmm_bo_alloc_pages(struct hmm_buffer_object *bo, enum hmm_bo_type type, - const void __user *userptr); + void *vmalloc_addr); void hmm_bo_free_pages(struct hmm_buffer_object *bo); int hmm_bo_page_allocated(struct hmm_buffer_object *bo); diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c index c72d0e344671..d8c7e7367386 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c +++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c @@ -30,7 +30,6 @@ #include <asm/iosf_mbi.h> #include <media/v4l2-event.h> -#include <media/videobuf-vmalloc.h> #define CREATE_TRACE_POINTS #include "atomisp_trace_event.h" @@ -208,11 +207,6 @@ int atomisp_freq_scaling(struct atomisp_device *isp, int i, ret; unsigned short fps = 0; - if (isp->sw_contex.power_state != ATOM_ISP_POWER_UP) { - dev_err(isp->dev, "DFS cannot proceed due to no power.\n"); - return -EINVAL; - } - if ((pdev->device & ATOMISP_PCI_DEVICE_SOC_MASK) == ATOMISP_PCI_DEVICE_SOC_CHT && ATOMISP_USE_YUVPP(asd)) isp->dfs = &dfs_config_cht_soc; @@ -308,24 +302,16 @@ int atomisp_reset(struct atomisp_device *isp) int ret = 0; dev_dbg(isp->dev, "%s\n", __func__); - atomisp_css_suspend(isp); - ret = atomisp_runtime_suspend(isp->dev); + + ret = atomisp_power_off(isp->dev); if (ret < 0) - dev_err(isp->dev, "atomisp_runtime_suspend failed, %d\n", ret); - ret = atomisp_mrfld_power_down(isp); + dev_err(isp->dev, "atomisp_power_off failed, %d\n", ret); + + ret = atomisp_power_on(isp->dev); if (ret < 0) { - dev_err(isp->dev, "can not disable ISP power\n"); - } else { - ret = atomisp_mrfld_power_up(isp); - if (ret < 0) - dev_err(isp->dev, "can not enable ISP power\n"); - ret = atomisp_runtime_resume(isp->dev); - if (ret < 0) - dev_err(isp->dev, "atomisp_runtime_resume failed, %d\n", ret); - } - ret = atomisp_css_resume(isp); - if (ret) + dev_err(isp->dev, "atomisp_power_on failed, %d\n", ret); isp->isp_fatal_error = true; + } return ret; } @@ -518,8 +504,8 @@ irqreturn_t atomisp_isr(int irq, void *dev) int err; spin_lock_irqsave(&isp->lock, flags); - if (isp->sw_contex.power_state != ATOM_ISP_POWER_UP || - !isp->css_initialized) { + + if (!isp->css_initialized) { spin_unlock_irqrestore(&isp->lock, flags); return IRQ_HANDLED; } @@ -634,25 +620,6 @@ void atomisp_clear_css_buffer_counters(struct atomisp_sub_device *asd) memset(asd->metadata_bufs_in_css[i], 0, sizeof(asd->metadata_bufs_in_css[i])); asd->dis_bufs_in_css = 0; - asd->video_out_capture.buffers_in_css = 0; - asd->video_out_vf.buffers_in_css = 0; - asd->video_out_preview.buffers_in_css = 0; - asd->video_out_video_capture.buffers_in_css = 0; -} - -/* ISP2400 */ -bool atomisp_buffers_queued(struct atomisp_sub_device *asd) -{ - return asd->video_out_capture.buffers_in_css || - asd->video_out_vf.buffers_in_css || - asd->video_out_preview.buffers_in_css || - asd->video_out_video_capture.buffers_in_css; -} - -/* ISP2401 */ -bool atomisp_buffers_queued_pipe(struct atomisp_video_pipe *pipe) -{ - return pipe->buffers_in_css ? true : false; } /* 0x100000 is the start of dmem inside SP */ @@ -681,57 +648,68 @@ void dump_sp_dmem(struct atomisp_device *isp, unsigned int addr, } while (--size32); } -static struct videobuf_buffer *atomisp_css_frame_to_vbuf( - struct atomisp_video_pipe *pipe, struct ia_css_frame *frame) +int atomisp_buffers_in_css(struct atomisp_video_pipe *pipe) { - struct videobuf_vmalloc_memory *vm_mem; - struct ia_css_frame *handle; - int i; + unsigned long irqflags; + struct list_head *pos; + int buffers_in_css = 0; - for (i = 0; pipe->capq.bufs[i]; i++) { - vm_mem = pipe->capq.bufs[i]->priv; - handle = vm_mem->vaddr; - if (handle && handle->data == frame->data) - return pipe->capq.bufs[i]; - } + spin_lock_irqsave(&pipe->irq_lock, irqflags); - return NULL; + list_for_each(pos, &pipe->buffers_in_css) + buffers_in_css++; + + spin_unlock_irqrestore(&pipe->irq_lock, irqflags); + + return buffers_in_css; } -static void atomisp_flush_video_pipe(struct atomisp_sub_device *asd, - struct atomisp_video_pipe *pipe) +void atomisp_buffer_done(struct ia_css_frame *frame, enum vb2_buffer_state state) { + struct atomisp_video_pipe *pipe = vb_to_pipe(&frame->vb.vb2_buf); + + lockdep_assert_held(&pipe->irq_lock); + + frame->vb.vb2_buf.timestamp = ktime_get_ns(); + frame->vb.field = pipe->pix.field; + frame->vb.sequence = atomic_read(&pipe->asd->sequence); + list_del(&frame->queue); + if (state == VB2_BUF_STATE_DONE) + vb2_set_plane_payload(&frame->vb.vb2_buf, 0, pipe->pix.sizeimage); + vb2_buffer_done(&frame->vb.vb2_buf, state); +} + +void atomisp_flush_video_pipe(struct atomisp_video_pipe *pipe, bool warn_on_css_frames) +{ + struct ia_css_frame *frame, *_frame; unsigned long irqflags; - int i; - if (!pipe->users) - return; + spin_lock_irqsave(&pipe->irq_lock, irqflags); - for (i = 0; pipe->capq.bufs[i]; i++) { - spin_lock_irqsave(&pipe->irq_lock, irqflags); - if (pipe->capq.bufs[i]->state == VIDEOBUF_ACTIVE || - pipe->capq.bufs[i]->state == VIDEOBUF_QUEUED) { - pipe->capq.bufs[i]->ts = ktime_get_ns(); - pipe->capq.bufs[i]->field_count = - atomic_read(&asd->sequence) << 1; - dev_dbg(asd->isp->dev, "release buffers on device %s\n", - pipe->vdev.name); - if (pipe->capq.bufs[i]->state == VIDEOBUF_QUEUED) - list_del_init(&pipe->capq.bufs[i]->queue); - pipe->capq.bufs[i]->state = VIDEOBUF_ERROR; - wake_up(&pipe->capq.bufs[i]->done); - } - spin_unlock_irqrestore(&pipe->irq_lock, irqflags); + list_for_each_entry_safe(frame, _frame, &pipe->buffers_in_css, queue) { + if (warn_on_css_frames) + dev_warn(pipe->isp->dev, "Warning: CSS frames queued on flush\n"); + atomisp_buffer_done(frame, VB2_BUF_STATE_ERROR); + } + + list_for_each_entry_safe(frame, _frame, &pipe->activeq, queue) + atomisp_buffer_done(frame, VB2_BUF_STATE_ERROR); + + list_for_each_entry_safe(frame, _frame, &pipe->buffers_waiting_for_param, queue) { + pipe->frame_request_config_id[frame->vb.vb2_buf.index] = 0; + atomisp_buffer_done(frame, VB2_BUF_STATE_ERROR); } + + spin_unlock_irqrestore(&pipe->irq_lock, irqflags); } /* Returns queued buffers back to video-core */ void atomisp_flush_bufs_and_wakeup(struct atomisp_sub_device *asd) { - atomisp_flush_video_pipe(asd, &asd->video_out_capture); - atomisp_flush_video_pipe(asd, &asd->video_out_vf); - atomisp_flush_video_pipe(asd, &asd->video_out_preview); - atomisp_flush_video_pipe(asd, &asd->video_out_video_capture); + atomisp_flush_video_pipe(&asd->video_out_capture, false); + atomisp_flush_video_pipe(&asd->video_out_vf, false); + atomisp_flush_video_pipe(&asd->video_out_preview, false); + atomisp_flush_video_pipe(&asd->video_out_video_capture, false); } /* clean out the parameters that did not apply */ @@ -763,93 +741,6 @@ static void atomisp_recover_params_queue(struct atomisp_video_pipe *pipe) atomisp_handle_parameter_and_buffer(pipe); } -/* find atomisp_video_pipe with css pipe id, buffer type and atomisp run_mode */ -static struct atomisp_video_pipe *__atomisp_get_pipe( - struct atomisp_sub_device *asd, - enum atomisp_input_stream_id stream_id, - enum ia_css_pipe_id css_pipe_id, - enum ia_css_buffer_type buf_type) -{ - /* video is same in online as in continuouscapture mode */ - if (asd->vfpp->val == ATOMISP_VFPP_DISABLE_LOWLAT) { - /* - * Disable vf_pp and run CSS in still capture mode. In this - * mode, CSS does not cause extra latency with buffering, but - * scaling is not available. - */ - return &asd->video_out_capture; - } else if (asd->vfpp->val == ATOMISP_VFPP_DISABLE_SCALER) { - /* - * Disable vf_pp and run CSS in video mode. This allows using - * ISP scaling but it has one frame delay due to CSS internal - * buffering. - */ - return &asd->video_out_video_capture; - } else if (css_pipe_id == IA_CSS_PIPE_ID_YUVPP) { - /* - * to SOC camera, yuvpp pipe is run for capture/video/SDV/ZSL. - */ - if (asd->continuous_mode->val) { - if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO) { - /* SDV case */ - switch (buf_type) { - case IA_CSS_BUFFER_TYPE_SEC_OUTPUT_FRAME: - return &asd->video_out_video_capture; - case IA_CSS_BUFFER_TYPE_SEC_VF_OUTPUT_FRAME: - return &asd->video_out_preview; - case IA_CSS_BUFFER_TYPE_OUTPUT_FRAME: - return &asd->video_out_capture; - default: - return &asd->video_out_vf; - } - } else if (asd->run_mode->val == ATOMISP_RUN_MODE_PREVIEW) { - /* ZSL case */ - switch (buf_type) { - case IA_CSS_BUFFER_TYPE_SEC_OUTPUT_FRAME: - return &asd->video_out_preview; - case IA_CSS_BUFFER_TYPE_OUTPUT_FRAME: - return &asd->video_out_capture; - default: - return &asd->video_out_vf; - } - } - } else if (buf_type == IA_CSS_BUFFER_TYPE_OUTPUT_FRAME) { - switch (asd->run_mode->val) { - case ATOMISP_RUN_MODE_VIDEO: - return &asd->video_out_video_capture; - case ATOMISP_RUN_MODE_PREVIEW: - return &asd->video_out_preview; - default: - return &asd->video_out_capture; - } - } else if (buf_type == IA_CSS_BUFFER_TYPE_VF_OUTPUT_FRAME) { - if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO) - return &asd->video_out_preview; - else - return &asd->video_out_vf; - } - } else if (asd->run_mode->val == ATOMISP_RUN_MODE_VIDEO) { - /* For online video or SDV video pipe. */ - if (css_pipe_id == IA_CSS_PIPE_ID_VIDEO || - css_pipe_id == IA_CSS_PIPE_ID_COPY || - css_pipe_id == IA_CSS_PIPE_ID_YUVPP) { - if (buf_type == IA_CSS_BUFFER_TYPE_OUTPUT_FRAME) - return &asd->video_out_video_capture; - return &asd->video_out_preview; - } - } else if (asd->run_mode->val == ATOMISP_RUN_MODE_PREVIEW) { - /* For online preview or ZSL preview pipe. */ - if (css_pipe_id == IA_CSS_PIPE_ID_PREVIEW || - css_pipe_id == IA_CSS_PIPE_ID_COPY || - css_pipe_id == IA_CSS_PIPE_ID_YUVPP) - return &asd->video_out_preview; - } - /* For capture pipe. */ - if (buf_type == IA_CSS_BUFFER_TYPE_VF_OUTPUT_FRAME) - return &asd->video_out_vf; - return &asd->video_out_capture; -} - enum atomisp_metadata_type atomisp_get_metadata_type(struct atomisp_sub_device *asd, enum ia_css_pipe_id pipe_id) @@ -868,11 +759,9 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error, enum ia_css_pipe_id css_pipe_id, bool q_buffers, enum atomisp_input_stream_id stream_id) { - struct videobuf_buffer *vb = NULL; struct atomisp_video_pipe *pipe = NULL; struct atomisp_css_buffer buffer; bool requeue = false; - int err; unsigned long irqflags; struct ia_css_frame *frame = NULL; struct atomisp_s3a_buf *s3a_buf = NULL, *_s3a_buf_tmp, *s3a_iter; @@ -881,6 +770,7 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error, enum atomisp_metadata_type md_type; struct atomisp_device *isp = asd->isp; struct v4l2_control ctrl; + int i, err; lockdep_assert_held(&isp->mutex); @@ -908,13 +798,6 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error, return; } - /* need to know the atomisp pipe for frame buffers */ - pipe = __atomisp_get_pipe(asd, stream_id, css_pipe_id, buf_type); - if (!pipe) { - dev_err(isp->dev, "error getting atomisp pipe\n"); - return; - } - switch (buf_type) { case IA_CSS_BUFFER_TYPE_3A_STATISTICS: list_for_each_entry_safe(s3a_iter, _s3a_buf_tmp, @@ -989,7 +872,6 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error, break; case IA_CSS_BUFFER_TYPE_VF_OUTPUT_FRAME: case IA_CSS_BUFFER_TYPE_SEC_VF_OUTPUT_FRAME: - pipe->buffers_in_css--; frame = buffer.css_buffer.data.frame; if (!frame) { WARN_ON(1); @@ -998,6 +880,8 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error, if (!frame->valid) error = true; + pipe = vb_to_pipe(&frame->vb.vb2_buf); + /* FIXME: * YUVPP doesn't set postview exp_id correctlly in SDV mode. * This is a WORKAROUND to set exp_id. see HSDES-1503911606. @@ -1022,10 +906,7 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error, dev_dbg(isp->dev, "%s thumb no flash in this frame\n", __func__); } - vb = atomisp_css_frame_to_vbuf(pipe, frame); - WARN_ON(!vb); - if (vb) - pipe->frame_config_id[vb->i] = frame->isp_config_id; + pipe->frame_config_id[frame->vb.vb2_buf.index] = frame->isp_config_id; if (css_pipe_id == IA_CSS_PIPE_ID_CAPTURE && asd->pending_capture_request > 0) { err = atomisp_css_offline_capture_configure(asd, @@ -1041,7 +922,6 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error, break; case IA_CSS_BUFFER_TYPE_OUTPUT_FRAME: case IA_CSS_BUFFER_TYPE_SEC_OUTPUT_FRAME: - pipe->buffers_in_css--; frame = buffer.css_buffer.data.frame; if (!frame) { WARN_ON(1); @@ -1051,6 +931,8 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error, if (!frame->valid) error = true; + pipe = vb_to_pipe(&frame->vb.vb2_buf); + /* FIXME: * YUVPP doesn't set preview exp_id correctlly in ZSL mode. * This is a WORKAROUND to set exp_id. see HSDES-1503911606. @@ -1062,72 +944,53 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error, dev_dbg(isp->dev, "%s: main frame with exp_id %d is ready\n", __func__, frame->exp_id); - vb = atomisp_css_frame_to_vbuf(pipe, frame); - if (!vb) { - WARN_ON(1); - break; - } + + i = frame->vb.vb2_buf.index; /* free the parameters */ - if (pipe->frame_params[vb->i]) { - if (asd->params.dvs_6axis == - pipe->frame_params[vb->i]->params.dvs_6axis) + if (pipe->frame_params[i]) { + if (asd->params.dvs_6axis == pipe->frame_params[i]->params.dvs_6axis) asd->params.dvs_6axis = NULL; - atomisp_free_css_parameters( - &pipe->frame_params[vb->i]->params); - kvfree(pipe->frame_params[vb->i]); - pipe->frame_params[vb->i] = NULL; + atomisp_free_css_parameters(&pipe->frame_params[i]->params); + kvfree(pipe->frame_params[i]); + pipe->frame_params[i] = NULL; } - pipe->frame_config_id[vb->i] = frame->isp_config_id; + pipe->frame_config_id[i] = frame->isp_config_id; ctrl.id = V4L2_CID_FLASH_MODE; if (asd->params.flash_state == ATOMISP_FLASH_ONGOING) { - if (frame->flash_state - == IA_CSS_FRAME_FLASH_STATE_PARTIAL) { - asd->frame_status[vb->i] = - ATOMISP_FRAME_STATUS_FLASH_PARTIAL; - dev_dbg(isp->dev, "%s partially flashed\n", - __func__); - } else if (frame->flash_state - == IA_CSS_FRAME_FLASH_STATE_FULL) { - asd->frame_status[vb->i] = - ATOMISP_FRAME_STATUS_FLASH_EXPOSED; + if (frame->flash_state == IA_CSS_FRAME_FLASH_STATE_PARTIAL) { + asd->frame_status[i] = ATOMISP_FRAME_STATUS_FLASH_PARTIAL; + dev_dbg(isp->dev, "%s partially flashed\n", __func__); + } else if (frame->flash_state == IA_CSS_FRAME_FLASH_STATE_FULL) { + asd->frame_status[i] = ATOMISP_FRAME_STATUS_FLASH_EXPOSED; asd->params.num_flash_frames--; - dev_dbg(isp->dev, "%s completely flashed\n", - __func__); + dev_dbg(isp->dev, "%s completely flashed\n", __func__); } else { - asd->frame_status[vb->i] = - ATOMISP_FRAME_STATUS_OK; - dev_dbg(isp->dev, - "%s no flash in this frame\n", - __func__); + asd->frame_status[i] = ATOMISP_FRAME_STATUS_OK; + dev_dbg(isp->dev, "%s no flash in this frame\n", __func__); } /* Check if flashing sequence is done */ - if (asd->frame_status[vb->i] == - ATOMISP_FRAME_STATUS_FLASH_EXPOSED) + if (asd->frame_status[i] == ATOMISP_FRAME_STATUS_FLASH_EXPOSED) asd->params.flash_state = ATOMISP_FLASH_DONE; } else if (isp->flash) { - if (v4l2_g_ctrl(isp->flash->ctrl_handler, &ctrl) == - 0 && ctrl.value == ATOMISP_FLASH_MODE_TORCH) { + if (v4l2_g_ctrl(isp->flash->ctrl_handler, &ctrl) == 0 && + ctrl.value == ATOMISP_FLASH_MODE_TORCH) { ctrl.id = V4L2_CID_FLASH_TORCH_INTENSITY; - if (v4l2_g_ctrl(isp->flash->ctrl_handler, &ctrl) - == 0 && ctrl.value > 0) { - asd->frame_status[vb->i] = - ATOMISP_FRAME_STATUS_FLASH_EXPOSED; - } else { - asd->frame_status[vb->i] = - ATOMISP_FRAME_STATUS_OK; - } + if (v4l2_g_ctrl(isp->flash->ctrl_handler, &ctrl) == 0 && + ctrl.value > 0) + asd->frame_status[i] = ATOMISP_FRAME_STATUS_FLASH_EXPOSED; + else + asd->frame_status[i] = ATOMISP_FRAME_STATUS_OK; } else { - asd->frame_status[vb->i] = - ATOMISP_FRAME_STATUS_OK; + asd->frame_status[i] = ATOMISP_FRAME_STATUS_OK; } } else { - asd->frame_status[vb->i] = ATOMISP_FRAME_STATUS_OK; + asd->frame_status[i] = ATOMISP_FRAME_STATUS_OK; } - asd->params.last_frame_status = asd->frame_status[vb->i]; + asd->params.last_frame_status = asd->frame_status[i]; if (asd->continuous_mode->val) { if (css_pipe_id == IA_CSS_PIPE_ID_PREVIEW || @@ -1193,20 +1056,10 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error, default: break; } - if (vb) { - vb->ts = ktime_get_ns(); - vb->field_count = atomic_read(&asd->sequence) << 1; - /*mark videobuffer done for dequeue*/ + if (frame) { spin_lock_irqsave(&pipe->irq_lock, irqflags); - vb->state = !error ? VIDEOBUF_DONE : VIDEOBUF_ERROR; + atomisp_buffer_done(frame, error ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); spin_unlock_irqrestore(&pipe->irq_lock, irqflags); - - /* - * Frame capture done, wake up any process block on - * current active buffer - * possibly hold by videobuf_dqbuf() - */ - wake_up(&vb->done); } /* @@ -3288,7 +3141,7 @@ int atomisp_css_cp_dvs2_coefs(struct atomisp_sub_device *asd, if (!IS_ISP2401) { if (sizeof(*cur) != sizeof(coefs->grid) || memcmp(&coefs->grid, cur, sizeof(coefs->grid))) { - dev_err(asd->isp->dev, "dvs grid mis-match!\n"); + dev_err(asd->isp->dev, "dvs grid mismatch!\n"); /* If the grid info in the argument differs from the current grid info, we tell the caller to reset the grid size and try again. */ @@ -3344,7 +3197,7 @@ int atomisp_css_cp_dvs2_coefs(struct atomisp_sub_device *asd, if (sizeof(*cur) != sizeof(dvs2_coefs.grid) || memcmp(&dvs2_coefs.grid, cur, sizeof(dvs2_coefs.grid))) { - dev_err(asd->isp->dev, "dvs grid mis-match!\n"); + dev_err(asd->isp->dev, "dvs grid mismatch!\n"); /* If the grid info in the argument differs from the current grid info, we tell the caller to reset the grid size and try again. */ @@ -3676,6 +3529,18 @@ void atomisp_free_css_parameters(struct atomisp_css_params *css_param) } } +static void atomisp_move_frame_to_activeq(struct ia_css_frame *frame, + struct atomisp_css_params_with_list *param) +{ + struct atomisp_video_pipe *pipe = vb_to_pipe(&frame->vb.vb2_buf); + unsigned long irqflags; + + pipe->frame_params[frame->vb.vb2_buf.index] = param; + spin_lock_irqsave(&pipe->irq_lock, irqflags); + list_move_tail(&frame->queue, &pipe->activeq); + spin_unlock_irqrestore(&pipe->irq_lock, irqflags); +} + /* * Check parameter queue list and buffer queue list to find out if matched items * and then set parameter to CSS and enqueue buffer to CSS. @@ -3686,13 +3551,10 @@ void atomisp_free_css_parameters(struct atomisp_css_params *css_param) void atomisp_handle_parameter_and_buffer(struct atomisp_video_pipe *pipe) { struct atomisp_sub_device *asd = pipe->asd; - struct videobuf_buffer *vb = NULL, *vb_tmp; + struct ia_css_frame *frame = NULL, *frame_tmp; struct atomisp_css_params_with_list *param = NULL, *param_tmp; - struct videobuf_vmalloc_memory *vm_mem = NULL; - unsigned long irqflags; bool need_to_enqueue_buffer = false; - - lockdep_assert_held(&asd->isp->mutex); + int i; if (!asd) { dev_err(pipe->isp->dev, "%s(): asd is NULL, device is %s\n", @@ -3700,6 +3562,8 @@ void atomisp_handle_parameter_and_buffer(struct atomisp_video_pipe *pipe) return; } + lockdep_assert_held(&asd->isp->mutex); + if (atomisp_is_vf_pipe(pipe)) return; @@ -3714,44 +3578,32 @@ void atomisp_handle_parameter_and_buffer(struct atomisp_video_pipe *pipe) list_empty(&pipe->buffers_waiting_for_param)) return; - list_for_each_entry_safe(vb, vb_tmp, + list_for_each_entry_safe(frame, frame_tmp, &pipe->buffers_waiting_for_param, queue) { - if (pipe->frame_request_config_id[vb->i]) { + i = frame->vb.vb2_buf.index; + if (pipe->frame_request_config_id[i]) { list_for_each_entry_safe(param, param_tmp, &pipe->per_frame_params, list) { - if (pipe->frame_request_config_id[vb->i] != - param->params.isp_config_id) + if (pipe->frame_request_config_id[i] != param->params.isp_config_id) continue; list_del(¶m->list); - list_del(&vb->queue); + /* * clear the request config id as the buffer * will be handled and enqueued into CSS soon */ - pipe->frame_request_config_id[vb->i] = 0; - pipe->frame_params[vb->i] = param; - vm_mem = vb->priv; - BUG_ON(!vm_mem); + pipe->frame_request_config_id[i] = 0; + atomisp_move_frame_to_activeq(frame, param); + need_to_enqueue_buffer = true; break; } - if (vm_mem) { - spin_lock_irqsave(&pipe->irq_lock, irqflags); - list_add_tail(&vb->queue, &pipe->activeq); - spin_unlock_irqrestore(&pipe->irq_lock, irqflags); - vm_mem = NULL; - need_to_enqueue_buffer = true; - } else { - /* The is the end, stop further loop */ + /* If this is the end, stop further loop */ + if (list_entry_is_head(param, &pipe->per_frame_params, list)) break; - } } else { - list_del(&vb->queue); - pipe->frame_params[vb->i] = NULL; - spin_lock_irqsave(&pipe->irq_lock, irqflags); - list_add_tail(&vb->queue, &pipe->activeq); - spin_unlock_irqrestore(&pipe->irq_lock, irqflags); + atomisp_move_frame_to_activeq(frame, NULL); need_to_enqueue_buffer = true; } } @@ -3774,14 +3626,14 @@ int atomisp_set_parameters(struct video_device *vdev, struct atomisp_css_params *css_param = &asd->params.css_param; int ret; - lockdep_assert_held(&asd->isp->mutex); - if (!asd) { dev_err(pipe->isp->dev, "%s(): asd is NULL, device is %s\n", __func__, vdev->name); return -EINVAL; } + lockdep_assert_held(&asd->isp->mutex); + if (!asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream) { dev_err(asd->isp->dev, "%s: internal error!\n", __func__); return -EINVAL; @@ -5140,9 +4992,8 @@ static int atomisp_set_fmt_to_snr(struct video_device *vdev, return css_input_resolution_changed(asd, ffmt); } -int atomisp_set_fmt(struct file *file, void *unused, struct v4l2_format *f) +int atomisp_set_fmt(struct video_device *vdev, struct v4l2_format *f) { - struct video_device *vdev = video_devdata(file); struct atomisp_device *isp = video_get_drvdata(vdev); struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev); struct atomisp_sub_device *asd = pipe->asd; @@ -5558,8 +5409,6 @@ done: f->fmt.pix.priv = PAGE_ALIGN(pipe->pix.width * pipe->pix.height * 2); - pipe->capq.field = f->fmt.pix.field; - /* * If in video 480P case, no GFX throttle */ @@ -5643,51 +5492,6 @@ out: return ret; } -/*Turn off ISP dphy */ -int atomisp_ospm_dphy_down(struct atomisp_device *isp) -{ - struct pci_dev *pdev = to_pci_dev(isp->dev); - unsigned long flags; - u32 reg; - - dev_dbg(isp->dev, "%s\n", __func__); - - /* if ISP timeout, we can force powerdown */ - if (isp->isp_timeout) - goto done; - - if (!atomisp_dev_users(isp)) - goto done; - - spin_lock_irqsave(&isp->lock, flags); - isp->sw_contex.power_state = ATOM_ISP_POWER_DOWN; - spin_unlock_irqrestore(&isp->lock, flags); -done: - /* - * MRFLD IUNIT DPHY is located in an always-power-on island - * MRFLD HW design need all CSI ports are disabled before - * powering down the IUNIT. - */ - pci_read_config_dword(pdev, MRFLD_PCI_CSI_CONTROL, ®); - reg |= MRFLD_ALL_CSI_PORTS_OFF_MASK; - pci_write_config_dword(pdev, MRFLD_PCI_CSI_CONTROL, reg); - return 0; -} - -/*Turn on ISP dphy */ -int atomisp_ospm_dphy_up(struct atomisp_device *isp) -{ - unsigned long flags; - - dev_dbg(isp->dev, "%s\n", __func__); - - spin_lock_irqsave(&isp->lock, flags); - isp->sw_contex.power_state = ATOM_ISP_POWER_UP; - spin_unlock_irqrestore(&isp->lock, flags); - - return 0; -} - int atomisp_exif_makernote(struct atomisp_sub_device *asd, struct atomisp_makernote_info *config) { diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.h b/drivers/staging/media/atomisp/pci/atomisp_cmd.h index c9f92f1326b6..b8911491581a 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_cmd.h +++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.h @@ -55,12 +55,11 @@ void dump_sp_dmem(struct atomisp_device *isp, unsigned int addr, struct camera_mipi_info *atomisp_to_sensor_mipi_info(struct v4l2_subdev *sd); struct atomisp_video_pipe *atomisp_to_video_pipe(struct video_device *dev); int atomisp_reset(struct atomisp_device *isp); +int atomisp_buffers_in_css(struct atomisp_video_pipe *pipe); +void atomisp_buffer_done(struct ia_css_frame *frame, enum vb2_buffer_state state); +void atomisp_flush_video_pipe(struct atomisp_video_pipe *pipe, bool warn_on_css_frames); void atomisp_flush_bufs_and_wakeup(struct atomisp_sub_device *asd); void atomisp_clear_css_buffer_counters(struct atomisp_sub_device *asd); -/* ISP2400 */ -bool atomisp_buffers_queued(struct atomisp_sub_device *asd); -/* ISP2401 */ -bool atomisp_buffers_queued_pipe(struct atomisp_video_pipe *pipe); /* Interrupt functions */ void atomisp_msi_irq_init(struct atomisp_device *isp); @@ -266,7 +265,7 @@ int atomisp_get_sensor_mode_data(struct atomisp_sub_device *asd, int atomisp_try_fmt(struct video_device *vdev, struct v4l2_pix_format *f, bool *res_overflow); -int atomisp_set_fmt(struct file *file, void *fh, struct v4l2_format *f); +int atomisp_set_fmt(struct video_device *vdev, struct v4l2_format *f); int atomisp_set_shading_table(struct atomisp_sub_device *asd, struct atomisp_shading_table *shading_table); @@ -274,8 +273,6 @@ int atomisp_set_shading_table(struct atomisp_sub_device *asd, int atomisp_offline_capture_configure(struct atomisp_sub_device *asd, struct atomisp_cont_capture_conf *cvf_config); -int atomisp_ospm_dphy_down(struct atomisp_device *isp); -int atomisp_ospm_dphy_up(struct atomisp_device *isp); int atomisp_exif_makernote(struct atomisp_sub_device *asd, struct atomisp_makernote_info *config); @@ -342,8 +339,6 @@ int atomisp_inject_a_fake_event(struct atomisp_sub_device *asd, int *event); int atomisp_get_invalid_frame_num(struct video_device *vdev, int *invalid_frame_num); -int atomisp_mrfld_power_up(struct atomisp_device *isp); -int atomisp_mrfld_power_down(struct atomisp_device *isp); -int atomisp_runtime_suspend(struct device *dev); -int atomisp_runtime_resume(struct device *dev); +int atomisp_power_off(struct device *dev); +int atomisp_power_on(struct device *dev); #endif /* __ATOMISP_CMD_H__ */ diff --git a/drivers/staging/media/atomisp/pci/atomisp_common.h b/drivers/staging/media/atomisp/pci/atomisp_common.h index b29874f2bc0f..07c38e487a66 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_common.h +++ b/drivers/staging/media/atomisp/pci/atomisp_common.h @@ -25,7 +25,7 @@ #include <linux/v4l2-mediabus.h> -#include <media/videobuf-core.h> +#include <media/videobuf2-v4l2.h> #include "atomisp_compat.h" @@ -64,8 +64,4 @@ struct atomisp_fmt { u32 bayer_order; }; -struct atomisp_buffer { - struct videobuf_buffer vb; -}; - #endif diff --git a/drivers/staging/media/atomisp/pci/atomisp_compat.h b/drivers/staging/media/atomisp/pci/atomisp_compat.h index a6d85d0f9ae5..7316eb9f974a 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_compat.h +++ b/drivers/staging/media/atomisp/pci/atomisp_compat.h @@ -22,7 +22,6 @@ #include "atomisp_compat_css20.h" #include "../../include/linux/atomisp.h" -#include <media/videobuf-vmalloc.h> struct atomisp_device; struct atomisp_sub_device; @@ -42,10 +41,6 @@ int atomisp_css_init(struct atomisp_device *isp); void atomisp_css_uninit(struct atomisp_device *isp); -void atomisp_css_suspend(struct atomisp_device *isp); - -int atomisp_css_resume(struct atomisp_device *isp); - void atomisp_css_init_struct(struct atomisp_sub_device *asd); int atomisp_css_irq_translate(struct atomisp_device *isp, @@ -61,7 +56,7 @@ int atomisp_css_irq_enable(struct atomisp_device *isp, enum ia_css_irq_info info, bool enable); int atomisp_q_video_buffer_to_css(struct atomisp_sub_device *asd, - struct videobuf_vmalloc_memory *vm_mem, + struct ia_css_frame *frame, enum atomisp_input_stream_id stream_id, enum ia_css_buffer_type css_buf_type, enum ia_css_pipe_id css_pipe_id); @@ -258,13 +253,6 @@ int atomisp_css_yuvpp_configure_output(struct atomisp_sub_device *asd, unsigned int padded_width, enum ia_css_frame_format format); -int atomisp_css_yuvpp_configure_viewfinder( - struct atomisp_sub_device *asd, - unsigned int stream_index, - unsigned int width, unsigned int height, - unsigned int min_width, - enum ia_css_frame_format format); - int atomisp_css_yuvpp_get_output_frame_info( struct atomisp_sub_device *asd, unsigned int stream_index, diff --git a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c index fdc05548d972..61e2e63a0ef1 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c +++ b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c @@ -237,18 +237,6 @@ static void __dump_pipe_config(struct atomisp_sub_device *asd, "pipe_config.isp_pipe_version:%d.\n", p_config->isp_pipe_version); dev_dbg(isp->dev, - "pipe_config.acc_extension=%p.\n", - p_config->acc_extension); - dev_dbg(isp->dev, - "pipe_config.acc_stages=%p.\n", - p_config->acc_stages); - dev_dbg(isp->dev, - "pipe_config.num_acc_stages=%d.\n", - p_config->num_acc_stages); - dev_dbg(isp->dev, - "pipe_config.acc_num_execs=%d.\n", - p_config->acc_num_execs); - dev_dbg(isp->dev, "pipe_config.default_capture_config.capture_mode=%d.\n", p_config->default_capture_config.mode); dev_dbg(isp->dev, @@ -629,10 +617,6 @@ static void __apply_additional_pipe_config( else stream_env->pipe_configs[pipe_id].enable_dz = false; break; - case IA_CSS_PIPE_ID_ACC: - stream_env->pipe_configs[pipe_id].mode = IA_CSS_PIPE_MODE_ACC; - stream_env->pipe_configs[pipe_id].enable_dz = false; - break; default: break; } @@ -644,7 +628,7 @@ static bool is_pipe_valid_to_current_run_mode(struct atomisp_sub_device *asd, if (!asd) return false; - if (pipe_id == IA_CSS_PIPE_ID_ACC || pipe_id == IA_CSS_PIPE_ID_YUVPP) + if (pipe_id == IA_CSS_PIPE_ID_YUVPP) return true; if (asd->vfpp) { @@ -718,12 +702,7 @@ static int __create_pipe(struct atomisp_sub_device *asd, if (pipe_id >= IA_CSS_PIPE_ID_NUM) return -EINVAL; - if (pipe_id != IA_CSS_PIPE_ID_ACC && - !stream_env->pipe_configs[pipe_id].output_info[0].res.width) - return 0; - - if (pipe_id == IA_CSS_PIPE_ID_ACC && - !stream_env->pipe_configs[pipe_id].acc_extension) + if (!stream_env->pipe_configs[pipe_id].output_info[0].res.width) return 0; if (!is_pipe_valid_to_current_run_mode(asd, pipe_id)) @@ -885,48 +864,10 @@ int atomisp_css_load_firmware(struct atomisp_device *isp) void atomisp_css_uninit(struct atomisp_device *isp) { - struct atomisp_sub_device *asd; - unsigned int i; - - for (i = 0; i < isp->num_of_streams; i++) { - asd = &isp->asd[i]; - memset(&asd->params.config, 0, sizeof(asd->params.config)); - asd->params.css_update_params_needed = false; - } - - isp->css_initialized = false; - ia_css_uninit(); -} - -void atomisp_css_suspend(struct atomisp_device *isp) -{ isp->css_initialized = false; ia_css_uninit(); } -int atomisp_css_resume(struct atomisp_device *isp) -{ - unsigned int mmu_base_addr; - int ret; - - ret = hmm_get_mmu_base_addr(isp->dev, &mmu_base_addr); - if (ret) { - dev_err(isp->dev, "get base address error.\n"); - return -EINVAL; - } - - ret = ia_css_init(isp->dev, &isp->css_env.isp_css_env, NULL, - mmu_base_addr, IA_CSS_IRQ_TYPE_PULSE); - if (ret) { - dev_err(isp->dev, "re-init css failed.\n"); - return -EINVAL; - } - ia_css_enable_isys_event_queue(true); - - isp->css_initialized = true; - return 0; -} - int atomisp_css_irq_translate(struct atomisp_device *isp, unsigned int *infos) { @@ -996,7 +937,7 @@ void atomisp_css_init_struct(struct atomisp_sub_device *asd) } int atomisp_q_video_buffer_to_css(struct atomisp_sub_device *asd, - struct videobuf_vmalloc_memory *vm_mem, + struct ia_css_frame *frame, enum atomisp_input_stream_id stream_id, enum ia_css_buffer_type css_buf_type, enum ia_css_pipe_id css_pipe_id) @@ -1006,7 +947,7 @@ int atomisp_q_video_buffer_to_css(struct atomisp_sub_device *asd, int err; css_buf.type = css_buf_type; - css_buf.data.frame = vm_mem->vaddr; + css_buf.data.frame = frame; err = ia_css_pipe_enqueue_buffer( stream_env->pipes[css_pipe_id], &css_buf); @@ -2141,8 +2082,6 @@ static enum ia_css_pipe_mode __pipe_id_to_pipe_mode( return IA_CSS_PIPE_MODE_CAPTURE; case IA_CSS_PIPE_ID_VIDEO: return IA_CSS_PIPE_MODE_VIDEO; - case IA_CSS_PIPE_ID_ACC: - return IA_CSS_PIPE_MODE_ACC; case IA_CSS_PIPE_ID_YUVPP: return IA_CSS_PIPE_MODE_YUVPP; default: @@ -2688,7 +2627,7 @@ int atomisp_get_css_frame_info(struct atomisp_sub_device *asd, if (0 != ia_css_pipe_get_info(asd->stream_env[stream_index] .pipes[pipe_index], &info)) { - dev_err(isp->dev, "ia_css_pipe_get_info FAILED"); + dev_dbg(isp->dev, "ia_css_pipe_get_info FAILED"); return -EINVAL; } @@ -2765,29 +2704,6 @@ int atomisp_css_yuvpp_configure_output(struct atomisp_sub_device *asd, return 0; } -int atomisp_css_yuvpp_configure_viewfinder( - struct atomisp_sub_device *asd, - unsigned int stream_index, - unsigned int width, unsigned int height, - unsigned int min_width, - enum ia_css_frame_format format) -{ - struct atomisp_stream_env *stream_env = - &asd->stream_env[stream_index]; - enum ia_css_pipe_id pipe_id = IA_CSS_PIPE_ID_YUVPP; - - stream_env->pipe_configs[pipe_id].mode = - __pipe_id_to_pipe_mode(asd, pipe_id); - stream_env->update_pipe[pipe_id] = true; - - stream_env->pipe_configs[pipe_id].vf_output_info[0].res.width = width; - stream_env->pipe_configs[pipe_id].vf_output_info[0].res.height = height; - stream_env->pipe_configs[pipe_id].vf_output_info[0].format = format; - stream_env->pipe_configs[pipe_id].vf_output_info[0].padded_width = - min_width; - return 0; -} - int atomisp_css_yuvpp_get_output_frame_info( struct atomisp_sub_device *asd, unsigned int stream_index, @@ -3180,7 +3096,7 @@ static int atomisp_compare_dvs_grid(struct atomisp_sub_device *asd, } if (sizeof(*cur) != sizeof(*atomgrid)) { - dev_err(asd->isp->dev, "dvs grid mis-match!\n"); + dev_err(asd->isp->dev, "dvs grid mismatch!\n"); return -EINVAL; } diff --git a/drivers/staging/media/atomisp/pci/atomisp_fops.c b/drivers/staging/media/atomisp/pci/atomisp_fops.c index 84a84e0cdeef..acea7492847d 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_fops.c +++ b/drivers/staging/media/atomisp/pci/atomisp_fops.c @@ -22,7 +22,7 @@ #include <linux/pm_runtime.h> #include <media/v4l2-ioctl.h> -#include <media/videobuf-vmalloc.h> +#include <media/videobuf2-vmalloc.h> #include "atomisp_cmd.h" #include "atomisp_common.h" @@ -35,49 +35,74 @@ #include "atomisp-regs.h" #include "hmm/hmm.h" +#include "ia_css_frame.h" #include "type_support.h" #include "device_access/device_access.h" -#define ISP_LEFT_PAD 128 /* equal to 2*NWAY */ - /* - * input image data, and current frame resolution for test + * Videobuf2 ops */ -#define ISP_PARAM_MMAP_OFFSET 0xfffff000 +static int atomisp_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct atomisp_video_pipe *pipe = container_of(vq, struct atomisp_video_pipe, vb_queue); + u16 source_pad = atomisp_subdev_source_pad(&pipe->vdev); + int ret; -#define MAGIC_CHECK(is, should) \ - do { \ - if (unlikely((is) != (should))) { \ - pr_err("magic mismatch: %x (expected %x)\n", \ - is, should); \ - BUG(); \ - } \ - } while (0) + mutex_lock(&pipe->asd->isp->mutex); /* for get_css_frame_info() / set_fmt() */ -/* - * Videobuf ops - */ -static int atomisp_buf_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) -{ - struct atomisp_video_pipe *pipe = vq->priv_data; + /* + * When VIDIOC_S_FMT has not been called before VIDIOC_REQBUFS, then + * this will fail. Call atomisp_set_fmt() ourselves and try again. + */ + ret = atomisp_get_css_frame_info(pipe->asd, source_pad, &pipe->frame_info); + if (ret) { + struct v4l2_format f = { + .fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420, + .fmt.pix.width = 10000, + .fmt.pix.height = 10000, + }; + + ret = atomisp_set_fmt(&pipe->vdev, &f); + if (ret) + goto out; + + ret = atomisp_get_css_frame_info(pipe->asd, source_pad, &pipe->frame_info); + if (ret) + goto out; + } + + atomisp_alloc_css_stat_bufs(pipe->asd, ATOMISP_INPUT_STREAM_GENERAL); - *size = pipe->pix.sizeimage; + *nplanes = 1; + sizes[0] = PAGE_ALIGN(pipe->pix.sizeimage); +out: + mutex_unlock(&pipe->asd->isp->mutex); return 0; } -static int atomisp_buf_prepare(struct videobuf_queue *vq, - struct videobuf_buffer *vb, - enum v4l2_field field) +static int atomisp_buf_init(struct vb2_buffer *vb) { - struct atomisp_video_pipe *pipe = vq->priv_data; + struct atomisp_video_pipe *pipe = vb_to_pipe(vb); + struct ia_css_frame *frame = vb_to_frame(vb); + int ret; + + ret = ia_css_frame_init_from_info(frame, &pipe->frame_info); + if (ret) + return ret; - vb->size = pipe->pix.sizeimage; - vb->width = pipe->pix.width; - vb->height = pipe->pix.height; - vb->field = field; - vb->state = VIDEOBUF_PREPARED; + if (frame->data_bytes > vb2_plane_size(vb, 0)) { + dev_err(pipe->asd->isp->dev, "Internal error frame.data_bytes(%u) > vb.length(%lu)\n", + frame->data_bytes, vb2_plane_size(vb, 0)); + return -EIO; + } + + frame->data = hmm_create_from_vmalloc_buf(vb2_plane_size(vb, 0), + vb2_plane_vaddr(vb, 0)); + if (frame->data == mmgr_NULL) + return -ENOMEM; return 0; } @@ -156,8 +181,7 @@ static int atomisp_q_one_s3a_buffer(struct atomisp_sub_device *asd, } else { list_add_tail(&s3a_buf->list, &asd->s3a_stats_in_css); if (s3a_list == &asd->s3a_stats_ready) - dev_warn(asd->isp->dev, "%s: drop one s3a stat which has exp_id %d!\n", - __func__, exp_id); + dev_dbg(asd->isp->dev, "drop one s3a stat with exp_id %d\n", exp_id); } asd->s3a_bufs_in_css[css_pipe_id]++; @@ -206,43 +230,44 @@ static int atomisp_q_one_dis_buffer(struct atomisp_sub_device *asd, return 0; } -int atomisp_q_video_buffers_to_css(struct atomisp_sub_device *asd, - struct atomisp_video_pipe *pipe, - enum atomisp_input_stream_id stream_id, - enum ia_css_buffer_type css_buf_type, - enum ia_css_pipe_id css_pipe_id) +static int atomisp_q_video_buffers_to_css(struct atomisp_sub_device *asd, + struct atomisp_video_pipe *pipe, + enum atomisp_input_stream_id stream_id, + enum ia_css_buffer_type css_buf_type, + enum ia_css_pipe_id css_pipe_id) { - struct videobuf_vmalloc_memory *vm_mem; struct atomisp_css_params_with_list *param; struct ia_css_dvs_grid_info *dvs_grid = atomisp_css_get_dvs_grid_info(&asd->params.curr_grid_info); unsigned long irqflags; - int err = 0; + int space, err = 0; + + lockdep_assert_held(&asd->isp->mutex); if (WARN_ON(css_pipe_id >= IA_CSS_PIPE_ID_NUM)) return -EINVAL; - while (pipe->buffers_in_css < ATOMISP_CSS_Q_DEPTH) { - struct videobuf_buffer *vb; + if (pipe->stopping) + return -EINVAL; + + space = ATOMISP_CSS_Q_DEPTH - atomisp_buffers_in_css(pipe); + while (space--) { + struct ia_css_frame *frame; spin_lock_irqsave(&pipe->irq_lock, irqflags); - if (list_empty(&pipe->activeq)) { - spin_unlock_irqrestore(&pipe->irq_lock, irqflags); - return -EINVAL; - } - vb = list_entry(pipe->activeq.next, - struct videobuf_buffer, queue); - list_del_init(&vb->queue); - vb->state = VIDEOBUF_ACTIVE; + frame = list_first_entry_or_null(&pipe->activeq, struct ia_css_frame, queue); + if (frame) + list_move_tail(&frame->queue, &pipe->buffers_in_css); spin_unlock_irqrestore(&pipe->irq_lock, irqflags); + if (!frame) + return -EINVAL; + /* * If there is a per_frame setting to apply on the buffer, * do it before buffer en-queueing. */ - vm_mem = vb->priv; - - param = pipe->frame_params[vb->i]; + param = pipe->frame_params[frame->vb.vb2_buf.index]; if (param) { atomisp_makeup_css_parameters(asd, &asd->params.css_param.update_flag, @@ -256,8 +281,7 @@ int atomisp_q_video_buffers_to_css(struct atomisp_sub_device *asd, if (!err) asd->params.config.dz_config = ¶m->params.dz_config; } - atomisp_css_set_isp_config_applied_frame(asd, - vm_mem->vaddr); + atomisp_css_set_isp_config_applied_frame(asd, frame); atomisp_css_update_isp_params_on_pipe(asd, asd->stream_env[stream_id].pipes[css_pipe_id]); asd->params.dvs_6axis = (struct ia_css_dvs_6axis_config *) @@ -282,20 +306,19 @@ int atomisp_q_video_buffers_to_css(struct atomisp_sub_device *asd, &asd->params.css_param.dz_config; asd->params.css_update_params_needed = true; } + pipe->frame_params[frame->vb.vb2_buf.index] = NULL; } /* Enqueue buffer */ - err = atomisp_q_video_buffer_to_css(asd, vm_mem, stream_id, + err = atomisp_q_video_buffer_to_css(asd, frame, stream_id, css_buf_type, css_pipe_id); if (err) { spin_lock_irqsave(&pipe->irq_lock, irqflags); - list_add_tail(&vb->queue, &pipe->activeq); - vb->state = VIDEOBUF_QUEUED; + list_move_tail(&frame->queue, &pipe->activeq); spin_unlock_irqrestore(&pipe->irq_lock, irqflags); dev_err(asd->isp->dev, "%s, css q fails: %d\n", __func__, err); return -EINVAL; } - pipe->buffers_in_css++; /* enqueue 3A/DIS/metadata buffers */ if (asd->params.curr_grid_info.s3a_grid.enable && @@ -517,11 +540,32 @@ int atomisp_qbuffers_to_css(struct atomisp_sub_device *asd) return 0; } -static void atomisp_buf_queue(struct videobuf_queue *vq, - struct videobuf_buffer *vb) +static void atomisp_buf_queue(struct vb2_buffer *vb) { - struct atomisp_video_pipe *pipe = vq->priv_data; + struct atomisp_video_pipe *pipe = vb_to_pipe(vb); + struct ia_css_frame *frame = vb_to_frame(vb); + struct atomisp_sub_device *asd = pipe->asd; + u16 source_pad = atomisp_subdev_source_pad(&pipe->vdev); + unsigned long irqflags; + int ret; + + mutex_lock(&asd->isp->mutex); + + ret = atomisp_pipe_check(pipe, false); + if (ret || pipe->stopping) { + spin_lock_irqsave(&pipe->irq_lock, irqflags); + atomisp_buffer_done(frame, VB2_BUF_STATE_ERROR); + spin_unlock_irqrestore(&pipe->irq_lock, irqflags); + goto out_unlock; + } + + /* FIXME this ugliness comes from the original atomisp buffer handling */ + if (!(vb->skip_cache_sync_on_finish && vb->skip_cache_sync_on_prepare)) + wbinvd(); + pipe->frame_params[vb->index] = NULL; + + spin_lock_irqsave(&pipe->irq_lock, irqflags); /* * when a frame buffer meets following conditions, it should be put into * the waiting list: @@ -533,40 +577,83 @@ static void atomisp_buf_queue(struct videobuf_queue *vq, * get enqueued. */ if (!atomisp_is_vf_pipe(pipe) && - (pipe->frame_request_config_id[vb->i] || + (pipe->frame_request_config_id[vb->index] || !list_empty(&pipe->buffers_waiting_for_param))) - list_add_tail(&vb->queue, &pipe->buffers_waiting_for_param); + list_add_tail(&frame->queue, &pipe->buffers_waiting_for_param); else - list_add_tail(&vb->queue, &pipe->activeq); + list_add_tail(&frame->queue, &pipe->activeq); + + spin_unlock_irqrestore(&pipe->irq_lock, irqflags); + + /* TODO: do this better, not best way to queue to css */ + if (asd->streaming == ATOMISP_DEVICE_STREAMING_ENABLED) { + if (!list_empty(&pipe->buffers_waiting_for_param)) + atomisp_handle_parameter_and_buffer(pipe); + else + atomisp_qbuffers_to_css(asd); + } + + /* + * Workaround: Due to the design of HALv3, + * sometimes in ZSL or SDV mode HAL needs to + * capture multiple images within one streaming cycle. + * But the capture number cannot be determined by HAL. + * So HAL only sets the capture number to be 1 and queue multiple + * buffers. Atomisp driver needs to check this case and re-trigger + * CSS to do capture when new buffer is queued. + */ + if (asd->continuous_mode->val && source_pad == ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE && + !asd->enable_raw_buffer_lock->val && asd->params.offline_parm.num_captures == 1) { + asd->pending_capture_request++; + dev_dbg(asd->isp->dev, "Add one pending capture request.\n"); + } - vb->state = VIDEOBUF_QUEUED; +out_unlock: + mutex_unlock(&asd->isp->mutex); } -static void atomisp_buf_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) +static void atomisp_buf_cleanup(struct vb2_buffer *vb) { - vb->state = VIDEOBUF_NEEDS_INIT; - atomisp_videobuf_free_buf(vb); + struct atomisp_video_pipe *pipe = vb_to_pipe(vb); + struct ia_css_frame *frame = vb_to_frame(vb); + int index = frame->vb.vb2_buf.index; + + pipe->frame_request_config_id[index] = 0; + pipe->frame_params[index] = NULL; + + hmm_free(frame->data); } -static const struct videobuf_queue_ops videobuf_qops = { - .buf_setup = atomisp_buf_setup, - .buf_prepare = atomisp_buf_prepare, - .buf_queue = atomisp_buf_queue, - .buf_release = atomisp_buf_release, +static const struct vb2_ops atomisp_vb2_ops = { + .queue_setup = atomisp_queue_setup, + .buf_init = atomisp_buf_init, + .buf_cleanup = atomisp_buf_cleanup, + .buf_queue = atomisp_buf_queue, + .start_streaming = atomisp_start_streaming, + .stop_streaming = atomisp_stop_streaming, }; static int atomisp_init_pipe(struct atomisp_video_pipe *pipe) { + int ret; + /* init locks */ spin_lock_init(&pipe->irq_lock); + mutex_init(&pipe->vb_queue_mutex); + + /* Init videobuf2 queue structure */ + pipe->vb_queue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + pipe->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR; + pipe->vb_queue.buf_struct_size = sizeof(struct ia_css_frame); + pipe->vb_queue.ops = &atomisp_vb2_ops; + pipe->vb_queue.mem_ops = &vb2_vmalloc_memops; + pipe->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + ret = vb2_queue_init(&pipe->vb_queue); + if (ret) + return ret; - videobuf_queue_vmalloc_init(&pipe->capq, &videobuf_qops, NULL, - &pipe->irq_lock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_NONE, - sizeof(struct atomisp_buffer), pipe, - NULL); /* ext_lock: NULL */ + pipe->vdev.queue = &pipe->vb_queue; + pipe->vdev.queue->lock = &pipe->vb_queue_mutex; INIT_LIST_HEAD(&pipe->activeq); INIT_LIST_HEAD(&pipe->buffers_waiting_for_param); @@ -721,13 +808,6 @@ static int atomisp_open(struct file *file) goto error; } - /* Init ISP */ - if (atomisp_css_init(isp)) { - ret = -EINVAL; - /* Need to clean up CSS init if it fails. */ - goto css_error; - } - atomisp_dev_init_struct(isp); ret = v4l2_subdev_call(isp->flash, core, s_power, 1); @@ -752,7 +832,6 @@ done: return 0; css_error: - atomisp_css_uninit(isp); pm_runtime_put(vdev->v4l2_dev->dev); error: mutex_unlock(&isp->mutex); @@ -766,45 +845,26 @@ static int atomisp_release(struct file *file) struct atomisp_device *isp = video_get_drvdata(vdev); struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev); struct atomisp_sub_device *asd = pipe->asd; - struct v4l2_requestbuffers req; struct v4l2_subdev_fh fh; struct v4l2_rect clear_compose = {0}; unsigned long flags; - int ret = 0; + int ret; v4l2_fh_init(&fh.vfh, vdev); - req.count = 0; - if (!isp) - return -EBADF; - - mutex_lock(&isp->mutex); - dev_dbg(isp->dev, "release device %s\n", vdev->name); asd->subdev.devnode = vdev; - pipe->users--; - - if (pipe->capq.streaming) - dev_warn(isp->dev, - "%s: ISP still streaming while closing!", - __func__); + /* Note file must not be used after this! */ + vb2_fop_release(file); - if (pipe->capq.streaming && - atomisp_streamoff(file, NULL, V4L2_BUF_TYPE_VIDEO_CAPTURE)) { - dev_err(isp->dev, "atomisp_streamoff failed on release, driver bug"); - goto done; - } + mutex_lock(&isp->mutex); + pipe->users--; if (pipe->users) goto done; - if (atomisp_reqbufs(file, NULL, &req)) { - dev_err(isp->dev, "atomisp_reqbufs failed on release, driver bug"); - goto done; - } - /* * A little trick here: * file injection input resolution is recorded in the sink pad, @@ -840,7 +900,6 @@ static int atomisp_release(struct file *file) goto done; atomisp_destroy_pipes_stream_force(asd); - atomisp_css_uninit(isp); if (defer_fw_load) { ia_css_unload_firmware(); @@ -862,260 +921,15 @@ done: V4L2_SEL_TGT_COMPOSE, 0, &clear_compose); mutex_unlock(&isp->mutex); - - return v4l2_fh_release(file); -} - -/* - * Memory help functions for image frame and private parameters - */ -static int do_isp_mm_remap(struct atomisp_device *isp, - struct vm_area_struct *vma, - ia_css_ptr isp_virt, u32 host_virt, u32 pgnr) -{ - u32 pfn; - - while (pgnr) { - pfn = hmm_virt_to_phys(isp_virt) >> PAGE_SHIFT; - if (remap_pfn_range(vma, host_virt, pfn, - PAGE_SIZE, PAGE_SHARED)) { - dev_err(isp->dev, "remap_pfn_range err.\n"); - return -EAGAIN; - } - - isp_virt += PAGE_SIZE; - host_virt += PAGE_SIZE; - pgnr--; - } - - return 0; -} - -static int frame_mmap(struct atomisp_device *isp, - const struct ia_css_frame *frame, struct vm_area_struct *vma) -{ - ia_css_ptr isp_virt; - u32 host_virt; - u32 pgnr; - - if (!frame) { - dev_err(isp->dev, "%s: NULL frame pointer.\n", __func__); - return -EINVAL; - } - - host_virt = vma->vm_start; - isp_virt = frame->data; - pgnr = DIV_ROUND_UP(frame->data_bytes, PAGE_SIZE); - - if (do_isp_mm_remap(isp, vma, isp_virt, host_virt, pgnr)) - return -EAGAIN; - return 0; } -int atomisp_videobuf_mmap_mapper(struct videobuf_queue *q, - struct vm_area_struct *vma) -{ - u32 offset = vma->vm_pgoff << PAGE_SHIFT; - int ret = -EINVAL, i; - struct atomisp_device *isp = - ((struct atomisp_video_pipe *)(q->priv_data))->isp; - struct videobuf_vmalloc_memory *vm_mem; - struct videobuf_mapping *map; - - MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); - if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED)) { - dev_err(isp->dev, "map appl bug: PROT_WRITE and MAP_SHARED are required\n"); - return -EINVAL; - } - - mutex_lock(&q->vb_lock); - for (i = 0; i < VIDEO_MAX_FRAME; i++) { - struct videobuf_buffer *buf = q->bufs[i]; - - if (!buf) - continue; - - map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL); - if (!map) { - mutex_unlock(&q->vb_lock); - return -ENOMEM; - } - - buf->map = map; - map->q = q; - - buf->baddr = vma->vm_start; - - if (buf && buf->memory == V4L2_MEMORY_MMAP && - buf->boff == offset) { - vm_mem = buf->priv; - ret = frame_mmap(isp, vm_mem->vaddr, vma); - vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP; - break; - } - } - mutex_unlock(&q->vb_lock); - - return ret; -} - -/* The input frame contains left and right padding that need to be removed. - * There is always ISP_LEFT_PAD padding on the left side. - * There is also padding on the right (padded_width - width). - */ -static int remove_pad_from_frame(struct atomisp_device *isp, - struct ia_css_frame *in_frame, __u32 width, __u32 height) -{ - unsigned int i; - unsigned short *buffer; - int ret = 0; - ia_css_ptr load = in_frame->data; - ia_css_ptr store = load; - - buffer = kmalloc_array(width, sizeof(load), GFP_KERNEL); - if (!buffer) - return -ENOMEM; - - load += ISP_LEFT_PAD; - for (i = 0; i < height; i++) { - ret = hmm_load(load, buffer, width * sizeof(load)); - if (ret < 0) - goto remove_pad_error; - - ret = hmm_store(store, buffer, width * sizeof(store)); - if (ret < 0) - goto remove_pad_error; - - load += in_frame->info.padded_width; - store += width; - } - -remove_pad_error: - kfree(buffer); - return ret; -} - -static int atomisp_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct video_device *vdev = video_devdata(file); - struct atomisp_device *isp = video_get_drvdata(vdev); - struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev); - struct atomisp_sub_device *asd = pipe->asd; - struct ia_css_frame *raw_virt_addr; - u32 start = vma->vm_start; - u32 end = vma->vm_end; - u32 size = end - start; - u32 origin_size, new_size; - int ret; - - if (!asd) { - dev_err(isp->dev, "%s(): asd is NULL, device is %s\n", - __func__, vdev->name); - return -EINVAL; - } - - if (!(vma->vm_flags & (VM_WRITE | VM_READ))) - return -EACCES; - - mutex_lock(&isp->mutex); - - if (!(vma->vm_flags & VM_SHARED)) { - /* Map private buffer. - * Set VM_SHARED to the flags since we need - * to map the buffer page by page. - * Without VM_SHARED, remap_pfn_range() treats - * this kind of mapping as invalid. - */ - vma->vm_flags |= VM_SHARED; - ret = hmm_mmap(vma, vma->vm_pgoff << PAGE_SHIFT); - mutex_unlock(&isp->mutex); - return ret; - } - - /* mmap for ISP offline raw data */ - if (atomisp_subdev_source_pad(vdev) - == ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE && - vma->vm_pgoff == (ISP_PARAM_MMAP_OFFSET >> PAGE_SHIFT)) { - new_size = pipe->pix.width * pipe->pix.height * 2; - if (asd->params.online_process != 0) { - ret = -EINVAL; - goto error; - } - raw_virt_addr = asd->raw_output_frame; - if (!raw_virt_addr) { - dev_err(isp->dev, "Failed to request RAW frame\n"); - ret = -EINVAL; - goto error; - } - - ret = remove_pad_from_frame(isp, raw_virt_addr, - pipe->pix.width, pipe->pix.height); - if (ret < 0) { - dev_err(isp->dev, "remove pad failed.\n"); - goto error; - } - origin_size = raw_virt_addr->data_bytes; - raw_virt_addr->data_bytes = new_size; - - if (size != PAGE_ALIGN(new_size)) { - dev_err(isp->dev, "incorrect size for mmap ISP Raw Frame\n"); - ret = -EINVAL; - goto error; - } - - if (frame_mmap(isp, raw_virt_addr, vma)) { - dev_err(isp->dev, "frame_mmap failed.\n"); - raw_virt_addr->data_bytes = origin_size; - ret = -EAGAIN; - goto error; - } - raw_virt_addr->data_bytes = origin_size; - vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP; - mutex_unlock(&isp->mutex); - return 0; - } - - /* - * mmap for normal frames - */ - if (size != pipe->pix.sizeimage) { - dev_err(isp->dev, "incorrect size for mmap ISP frames\n"); - ret = -EINVAL; - goto error; - } - mutex_unlock(&isp->mutex); - - return atomisp_videobuf_mmap_mapper(&pipe->capq, vma); - -error: - mutex_unlock(&isp->mutex); - - return ret; -} - -static __poll_t atomisp_poll(struct file *file, - struct poll_table_struct *pt) -{ - struct video_device *vdev = video_devdata(file); - struct atomisp_device *isp = video_get_drvdata(vdev); - struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev); - - mutex_lock(&isp->mutex); - if (pipe->capq.streaming != 1) { - mutex_unlock(&isp->mutex); - return EPOLLERR; - } - mutex_unlock(&isp->mutex); - - return videobuf_poll_stream(file, &pipe->capq, pt); -} - const struct v4l2_file_operations atomisp_fops = { .owner = THIS_MODULE, .open = atomisp_open, .release = atomisp_release, - .mmap = atomisp_mmap, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll, .unlocked_ioctl = video_ioctl2, #ifdef CONFIG_COMPAT /* @@ -1124,5 +938,4 @@ const struct v4l2_file_operations atomisp_fops = { .compat_ioctl32 = atomisp_compat_ioctl32, */ #endif - .poll = atomisp_poll, }; diff --git a/drivers/staging/media/atomisp/pci/atomisp_fops.h b/drivers/staging/media/atomisp/pci/atomisp_fops.h index 3f1e442ba782..10e43126b693 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_fops.h +++ b/drivers/staging/media/atomisp/pci/atomisp_fops.h @@ -22,12 +22,6 @@ #define __ATOMISP_FOPS_H__ #include "atomisp_subdev.h" -int atomisp_q_video_buffers_to_css(struct atomisp_sub_device *asd, - struct atomisp_video_pipe *pipe, - enum atomisp_input_stream_id stream_id, - enum ia_css_buffer_type css_buf_type, - enum ia_css_pipe_id css_pipe_id); - unsigned int atomisp_dev_users(struct atomisp_device *isp); unsigned int atomisp_sub_dev_users(struct atomisp_sub_device *asd); @@ -35,13 +29,6 @@ unsigned int atomisp_sub_dev_users(struct atomisp_sub_device *asd); * Memory help functions for image frame and private parameters */ -int atomisp_videobuf_mmap_mapper(struct videobuf_queue *q, - struct vm_area_struct *vma); - -int atomisp_qbuf_to_css(struct atomisp_device *isp, - struct atomisp_video_pipe *pipe, - struct videobuf_buffer *vb); - int atomisp_qbuffers_to_css(struct atomisp_sub_device *asd); extern const struct v4l2_file_operations atomisp_fops; diff --git a/drivers/staging/media/atomisp/pci/atomisp_internal.h b/drivers/staging/media/atomisp/pci/atomisp_internal.h index d9d158cdf09e..653e6d74a966 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_internal.h +++ b/drivers/staging/media/atomisp/pci/atomisp_internal.h @@ -195,7 +195,6 @@ struct atomisp_regs { }; struct atomisp_sw_contex { - int power_state; int running_freq; }; diff --git a/drivers/staging/media/atomisp/pci/atomisp_ioctl.c b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c index 0ddb0ed42dd9..cb01ba65c88f 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_ioctl.c +++ b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c @@ -23,7 +23,6 @@ #include <media/v4l2-ioctl.h> #include <media/v4l2-event.h> -#include <media/videobuf-vmalloc.h> #include "atomisp_cmd.h" #include "atomisp_common.h" @@ -542,6 +541,11 @@ int atomisp_pipe_check(struct atomisp_video_pipe *pipe, bool settings_change) if (pipe->isp->isp_fatal_error) return -EIO; + if (settings_change && vb2_is_busy(&pipe->vb_queue)) { + dev_err(pipe->isp->dev, "Set fmt/input IOCTL while streaming\n"); + return -EBUSY; + } + switch (pipe->asd->streaming) { case ATOMISP_DEVICE_STREAMING_DISABLED: break; @@ -632,10 +636,10 @@ static int atomisp_enum_input(struct file *file, void *fh, static unsigned int atomisp_subdev_streaming_count(struct atomisp_sub_device *asd) { - return asd->video_out_preview.capq.streaming - + asd->video_out_capture.capq.streaming - + asd->video_out_video_capture.capq.streaming - + asd->video_out_vf.capq.streaming; + return asd->video_out_preview.vb_queue.start_streaming_called + + asd->video_out_capture.vb_queue.start_streaming_called + + asd->video_out_video_capture.vb_queue.start_streaming_called + + asd->video_out_vf.vb_queue.start_streaming_called; } unsigned int atomisp_streaming_count(struct atomisp_device *isp) @@ -661,6 +665,14 @@ static int atomisp_g_input(struct file *file, void *fh, unsigned int *input) return 0; } +static int atomisp_s_fmt_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct video_device *vdev = video_devdata(file); + + return atomisp_set_fmt(vdev, f); +} + /* * set input are used to set current primary/secondary camera */ @@ -866,29 +878,8 @@ static int atomisp_adjust_fmt(struct v4l2_format *f) u32 padded_width; format_bridge = atomisp_get_format_bridge(f->fmt.pix.pixelformat); - - padded_width = f->fmt.pix.width + pad_w; - - if (format_bridge->planar) { - f->fmt.pix.bytesperline = padded_width; - f->fmt.pix.sizeimage = PAGE_ALIGN(f->fmt.pix.height * - DIV_ROUND_UP(format_bridge->depth * - padded_width, 8)); - } else { - f->fmt.pix.bytesperline = DIV_ROUND_UP(format_bridge->depth * - padded_width, 8); - f->fmt.pix.sizeimage = PAGE_ALIGN(f->fmt.pix.height * f->fmt.pix.bytesperline); - } - - if (f->fmt.pix.field == V4L2_FIELD_ANY) - f->fmt.pix.field = V4L2_FIELD_NONE; - - format_bridge = atomisp_get_format_bridge(f->fmt.pix.pixelformat); - if (!format_bridge) - return -EINVAL; - /* Currently, raw formats are broken!!! */ - if (format_bridge->sh_fmt == IA_CSS_FRAME_FORMAT_RAW) { + if (!format_bridge || format_bridge->sh_fmt == IA_CSS_FRAME_FORMAT_RAW) { f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; format_bridge = atomisp_get_format_bridge(f->fmt.pix.pixelformat); @@ -931,6 +922,7 @@ static int atomisp_try_fmt_cap(struct file *file, void *fh, struct v4l2_format *f) { struct video_device *vdev = video_devdata(file); + u32 pixfmt = f->fmt.pix.pixelformat; int ret; /* @@ -944,6 +936,12 @@ static int atomisp_try_fmt_cap(struct file *file, void *fh, if (ret) return ret; + /* + * atomisp_try_fmt() replaces pixelformat with the sensors native + * format, restore the actual format requested by userspace. + */ + f->fmt.pix.pixelformat = pixfmt; + return atomisp_adjust_fmt(f); } @@ -961,44 +959,13 @@ static int atomisp_g_fmt_cap(struct file *file, void *fh, if (f->fmt.pix.sizeimage) return 0; - f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; f->fmt.pix.width = 10000; f->fmt.pix.height = 10000; return atomisp_try_fmt_cap(file, fh, f); } -/* - * Free videobuffer buffer priv data - */ -void atomisp_videobuf_free_buf(struct videobuf_buffer *vb) -{ - struct videobuf_vmalloc_memory *vm_mem; - - if (!vb) - return; - - vm_mem = vb->priv; - if (vm_mem && vm_mem->vaddr) { - ia_css_frame_free(vm_mem->vaddr); - vm_mem->vaddr = NULL; - } -} - -/* - * this function is used to free video buffer queue - */ -static void atomisp_videobuf_free_queue(struct videobuf_queue *q) -{ - int i; - - for (i = 0; i < VIDEO_MAX_FRAME; i++) { - atomisp_videobuf_free_buf(q->bufs[i]); - kfree(q->bufs[i]); - q->bufs[i] = NULL; - } -} - int atomisp_alloc_css_stat_bufs(struct atomisp_sub_device *asd, uint16_t stream_id) { @@ -1100,178 +1067,13 @@ error: return -ENOMEM; } -/* - * Initiate Memory Mapping or User Pointer I/O - */ -int atomisp_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *req) -{ - struct video_device *vdev = video_devdata(file); - struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev); - struct atomisp_sub_device *asd = pipe->asd; - struct ia_css_frame_info frame_info; - struct ia_css_frame *frame; - struct videobuf_vmalloc_memory *vm_mem; - u16 source_pad = atomisp_subdev_source_pad(vdev); - int ret = 0, i = 0; - - if (req->count == 0) { - mutex_lock(&pipe->capq.vb_lock); - if (!list_empty(&pipe->capq.stream)) - videobuf_queue_cancel(&pipe->capq); - - atomisp_videobuf_free_queue(&pipe->capq); - mutex_unlock(&pipe->capq.vb_lock); - /* clear request config id */ - memset(pipe->frame_request_config_id, 0, - VIDEO_MAX_FRAME * sizeof(unsigned int)); - memset(pipe->frame_params, 0, - VIDEO_MAX_FRAME * - sizeof(struct atomisp_css_params_with_list *)); - return 0; - } - - ret = videobuf_reqbufs(&pipe->capq, req); - if (ret) - return ret; - - atomisp_alloc_css_stat_bufs(asd, ATOMISP_INPUT_STREAM_GENERAL); - - /* - * for user pointer type, buffers are not really allocated here, - * buffers are setup in QBUF operation through v4l2_buffer structure - */ - if (req->memory == V4L2_MEMORY_USERPTR) - return 0; - - ret = atomisp_get_css_frame_info(asd, source_pad, &frame_info); - if (ret) - return ret; - - /* - * Allocate the real frame here for selected node using our - * memory management function - */ - for (i = 0; i < req->count; i++) { - if (ia_css_frame_allocate_from_info(&frame, &frame_info)) - goto error; - vm_mem = pipe->capq.bufs[i]->priv; - vm_mem->vaddr = frame; - } - - return ret; - -error: - while (i--) { - vm_mem = pipe->capq.bufs[i]->priv; - ia_css_frame_free(vm_mem->vaddr); - } - - if (asd->vf_frame) - ia_css_frame_free(asd->vf_frame); - - return -ENOMEM; -} - -/* application query the status of a buffer */ -static int atomisp_querybuf(struct file *file, void *fh, - struct v4l2_buffer *buf) -{ - struct video_device *vdev = video_devdata(file); - struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev); - - return videobuf_querybuf(&pipe->capq, buf); -} - -/* - * Applications call the VIDIOC_QBUF ioctl to enqueue an empty (capturing) or - * filled (output) buffer in the drivers incoming queue. - */ -static int atomisp_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) +static int atomisp_qbuf_wrapper(struct file *file, void *fh, struct v4l2_buffer *buf) { - static const int NOFLUSH_FLAGS = V4L2_BUF_FLAG_NO_CACHE_INVALIDATE | - V4L2_BUF_FLAG_NO_CACHE_CLEAN; struct video_device *vdev = video_devdata(file); struct atomisp_device *isp = video_get_drvdata(vdev); struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev); - struct atomisp_sub_device *asd = pipe->asd; - struct videobuf_buffer *vb; - struct videobuf_vmalloc_memory *vm_mem; - struct ia_css_frame_info frame_info; - struct ia_css_frame *handle = NULL; - u32 length; - u32 pgnr; - int ret; - - ret = atomisp_pipe_check(pipe, false); - if (ret) - return ret; - - if (!buf || buf->index >= VIDEO_MAX_FRAME || - !pipe->capq.bufs[buf->index]) { - dev_err(isp->dev, "Invalid index for qbuf.\n"); - return -EINVAL; - } - - /* - * For userptr type frame, we convert user space address to physic - * address and reprograme out page table properly - */ - if (buf->memory == V4L2_MEMORY_USERPTR) { - if (offset_in_page(buf->m.userptr)) { - dev_err(isp->dev, "Error userptr is not page aligned.\n"); - return -EINVAL; - } - - vb = pipe->capq.bufs[buf->index]; - vm_mem = vb->priv; - if (!vm_mem) - return -EINVAL; - - length = vb->bsize; - pgnr = (length + (PAGE_SIZE - 1)) >> PAGE_SHIFT; - - if (vb->baddr == buf->m.userptr && vm_mem->vaddr) - goto done; - - if (atomisp_get_css_frame_info(asd, - atomisp_subdev_source_pad(vdev), &frame_info)) - return -EIO; - - ret = ia_css_frame_map(&handle, &frame_info, - (void __user *)buf->m.userptr, - pgnr); - if (ret) { - dev_err(isp->dev, "Failed to map user buffer\n"); - return ret; - } - - if (vm_mem->vaddr) { - mutex_lock(&pipe->capq.vb_lock); - ia_css_frame_free(vm_mem->vaddr); - vm_mem->vaddr = NULL; - vb->state = VIDEOBUF_NEEDS_INIT; - mutex_unlock(&pipe->capq.vb_lock); - } - - vm_mem->vaddr = handle; - - buf->flags &= ~V4L2_BUF_FLAG_MAPPED; - buf->flags |= V4L2_BUF_FLAG_QUEUED; - buf->flags &= ~V4L2_BUF_FLAG_DONE; - } else if (buf->memory == V4L2_MEMORY_MMAP) { - buf->flags |= V4L2_BUF_FLAG_MAPPED; - buf->flags |= V4L2_BUF_FLAG_QUEUED; - buf->flags &= ~V4L2_BUF_FLAG_DONE; - - /* - * For mmap, frames were allocated at request buffers - */ - } - -done: - if (!((buf->flags & NOFLUSH_FLAGS) == NOFLUSH_FLAGS)) - wbinvd(); + /* FIXME this abuse of buf->reserved2 comes from the original atomisp buffer handling */ if (!atomisp_is_vf_pipe(pipe) && (buf->reserved2 & ATOMISP_BUFFER_HAS_PER_FRAME_SETTING)) { /* this buffer will have a per-frame parameter */ @@ -1284,90 +1086,27 @@ done: pipe->frame_request_config_id[buf->index] = 0; } - pipe->frame_params[buf->index] = NULL; - - mutex_unlock(&isp->mutex); - ret = videobuf_qbuf(&pipe->capq, buf); - mutex_lock(&isp->mutex); - if (ret) - return ret; - - /* TODO: do this better, not best way to queue to css */ - if (asd->streaming == ATOMISP_DEVICE_STREAMING_ENABLED) { - if (!list_empty(&pipe->buffers_waiting_for_param)) { - atomisp_handle_parameter_and_buffer(pipe); - } else { - atomisp_qbuffers_to_css(asd); - } - } - - /* - * Workaround: Due to the design of HALv3, - * sometimes in ZSL or SDV mode HAL needs to - * capture multiple images within one streaming cycle. - * But the capture number cannot be determined by HAL. - * So HAL only sets the capture number to be 1 and queue multiple - * buffers. Atomisp driver needs to check this case and re-trigger - * CSS to do capture when new buffer is queued. - */ - if (asd->continuous_mode->val && - atomisp_subdev_source_pad(vdev) - == ATOMISP_SUBDEV_PAD_SOURCE_CAPTURE && - pipe->capq.streaming && - !asd->enable_raw_buffer_lock->val && - asd->params.offline_parm.num_captures == 1) { - asd->pending_capture_request++; - dev_dbg(isp->dev, "Add one pending capture request.\n"); - } - - dev_dbg(isp->dev, "qbuf buffer %d (%s) for asd%d\n", buf->index, - vdev->name, asd->index); - - return 0; + return vb2_ioctl_qbuf(file, fh, buf); } -static int __get_frame_exp_id(struct atomisp_video_pipe *pipe, - struct v4l2_buffer *buf) -{ - struct videobuf_vmalloc_memory *vm_mem; - struct ia_css_frame *handle; - int i; - - for (i = 0; pipe->capq.bufs[i]; i++) { - vm_mem = pipe->capq.bufs[i]->priv; - handle = vm_mem->vaddr; - if (buf->index == pipe->capq.bufs[i]->i && handle) - return handle->exp_id; - } - return -EINVAL; -} - -/* - * Applications call the VIDIOC_DQBUF ioctl to dequeue a filled (capturing) or - * displayed (output buffer)from the driver's outgoing queue - */ -static int atomisp_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) +static int atomisp_dqbuf_wrapper(struct file *file, void *fh, struct v4l2_buffer *buf) { struct video_device *vdev = video_devdata(file); struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev); struct atomisp_sub_device *asd = pipe->asd; struct atomisp_device *isp = video_get_drvdata(vdev); + struct ia_css_frame *frame; + struct vb2_buffer *vb; int ret; - ret = atomisp_pipe_check(pipe, false); + ret = vb2_ioctl_dqbuf(file, fh, buf); if (ret) return ret; - mutex_unlock(&isp->mutex); - ret = videobuf_dqbuf(&pipe->capq, buf, file->f_flags & O_NONBLOCK); - mutex_lock(&isp->mutex); - if (ret) { - if (ret != -EAGAIN) - dev_dbg(isp->dev, "<%s: %d\n", __func__, ret); - return ret; - } + vb = pipe->vb_queue.bufs[buf->index]; + frame = vb_to_frame(vb); - buf->bytesused = pipe->pix.sizeimage; + /* FIXME this abuse of buf->reserved* comes from the original atomisp buffer handling */ buf->reserved = asd->frame_status[buf->index]; /* @@ -1378,7 +1117,7 @@ static int atomisp_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) */ buf->reserved &= 0x0000ffff; if (!(buf->flags & V4L2_BUF_FLAG_ERROR)) - buf->reserved |= __get_frame_exp_id(pipe, buf) << 16; + buf->reserved |= frame->exp_id; buf->reserved2 = pipe->frame_config_id[buf->index]; dev_dbg(isp->dev, @@ -1506,36 +1245,26 @@ static void atomisp_dma_burst_len_cfg(struct atomisp_sub_device *asd) atomisp_css2_hw_store_32(DMA_BURST_SIZE_REG, 0x00); } -/* - * This ioctl start the capture during streaming I/O. - */ -static int atomisp_streamon(struct file *file, void *fh, - enum v4l2_buf_type type) +int atomisp_start_streaming(struct vb2_queue *vq, unsigned int count) { - struct video_device *vdev = video_devdata(file); - struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev); + struct atomisp_video_pipe *pipe = vq_to_pipe(vq); struct atomisp_sub_device *asd = pipe->asd; - struct atomisp_device *isp = video_get_drvdata(vdev); + struct video_device *vdev = &pipe->vdev; + struct atomisp_device *isp = asd->isp; struct pci_dev *pdev = to_pci_dev(isp->dev); enum ia_css_pipe_id css_pipe_id; unsigned int sensor_start_stream; unsigned long irqflags; int ret; + mutex_lock(&isp->mutex); + dev_dbg(isp->dev, "Start stream on pad %d for asd%d\n", atomisp_subdev_source_pad(vdev), asd->index); - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - dev_dbg(isp->dev, "unsupported v4l2 buf type\n"); - return -EINVAL; - } - ret = atomisp_pipe_check(pipe, false); if (ret) - return ret; - - if (pipe->capq.streaming) - return 0; + goto out_unlock; /* Input system HW workaround */ atomisp_dma_burst_len_cfg(asd); @@ -1546,18 +1275,6 @@ static int atomisp_streamon(struct file *file, void *fh, */ sensor_start_stream = atomisp_sensor_start_stream(asd); - spin_lock_irqsave(&pipe->irq_lock, irqflags); - if (list_empty(&pipe->capq.stream)) { - spin_unlock_irqrestore(&pipe->irq_lock, irqflags); - dev_dbg(isp->dev, "no buffer in the queue\n"); - return -EINVAL; - } - spin_unlock_irqrestore(&pipe->irq_lock, irqflags); - - ret = videobuf_streamon(&pipe->capq); - if (ret) - return ret; - /* Reset pending capture request count. */ asd->pending_capture_request = 0; @@ -1578,8 +1295,10 @@ static int atomisp_streamon(struct file *file, void *fh, mutex_unlock(&isp->mutex); ret = wait_for_completion_interruptible(&asd->init_done); mutex_lock(&isp->mutex); - if (ret != 0) - return -ERESTARTSYS; + if (ret) { + ret = -ERESTARTSYS; + goto out_unlock; + } } /* handle per_frame_setting parameter and buffers */ @@ -1601,12 +1320,15 @@ static int atomisp_streamon(struct file *file, void *fh, asd->params.offline_parm.num_captures, asd->params.offline_parm.skip_frames, asd->params.offline_parm.offset); - if (ret) - return -EINVAL; + if (ret) { + ret = -EINVAL; + goto out_unlock; + } } } atomisp_qbuffers_to_css(asd); - return 0; + ret = 0; + goto out_unlock; } if (asd->streaming == ATOMISP_DEVICE_STREAMING_ENABLED) { @@ -1631,8 +1353,10 @@ static int atomisp_streamon(struct file *file, void *fh, asd->params.dvs_6axis = NULL; ret = atomisp_css_start(asd, css_pipe_id, false); - if (ret) - return ret; + if (ret) { + atomisp_flush_video_pipe(pipe, true); + goto out_unlock; + } spin_lock_irqsave(&isp->lock, irqflags); asd->streaming = ATOMISP_DEVICE_STREAMING_ENABLED; @@ -1652,8 +1376,10 @@ static int atomisp_streamon(struct file *file, void *fh, atomisp_qbuffers_to_css(asd); /* Only start sensor when the last streaming instance started */ - if (atomisp_subdev_streaming_count(asd) < sensor_start_stream) - return 0; + if (atomisp_subdev_streaming_count(asd) < sensor_start_stream) { + ret = 0; + goto out_unlock; + } start_sensor: if (isp->flash) { @@ -1684,7 +1410,7 @@ start_sensor: ret = atomisp_stream_on_master_slave_sensor(isp, false); if (ret) { dev_err(isp->dev, "master slave sensor stream on failed!\n"); - return ret; + goto out_unlock; } goto start_delay_wq; } else if (asd->depth_mode->val && (atomisp_streaming_count(isp) < @@ -1706,7 +1432,8 @@ start_sensor: spin_lock_irqsave(&isp->lock, irqflags); asd->streaming = ATOMISP_DEVICE_STREAMING_DISABLED; spin_unlock_irqrestore(&isp->lock, irqflags); - return -EINVAL; + ret = -EINVAL; + goto out_unlock; } start_delay_wq: @@ -1722,35 +1449,44 @@ start_delay_wq: asd->delayed_init = ATOMISP_DELAYED_INIT_NOT_QUEUED; } - return 0; +out_unlock: + mutex_unlock(&isp->mutex); + return ret; } -int atomisp_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) +void atomisp_stop_streaming(struct vb2_queue *vq) { - struct video_device *vdev = video_devdata(file); - struct atomisp_device *isp = video_get_drvdata(vdev); - struct pci_dev *pdev = to_pci_dev(isp->dev); - struct atomisp_video_pipe *pipe = atomisp_to_video_pipe(vdev); + struct atomisp_video_pipe *pipe = vq_to_pipe(vq); struct atomisp_sub_device *asd = pipe->asd; - struct atomisp_video_pipe *capture_pipe = NULL; - struct atomisp_video_pipe *vf_pipe = NULL; - struct atomisp_video_pipe *preview_pipe = NULL; - struct atomisp_video_pipe *video_pipe = NULL; - struct videobuf_buffer *vb, *_vb; + struct video_device *vdev = &pipe->vdev; + struct atomisp_device *isp = asd->isp; + struct pci_dev *pdev = to_pci_dev(isp->dev); + bool recreate_streams[MAX_STREAM_NUM] = {0}; enum ia_css_pipe_id css_pipe_id; - int ret; - unsigned long flags; bool first_streamoff = false; + unsigned long flags; + int i, ret; + + mutex_lock(&isp->mutex); dev_dbg(isp->dev, "Stop stream on pad %d for asd%d\n", atomisp_subdev_source_pad(vdev), asd->index); - lockdep_assert_held(&isp->mutex); - - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - dev_dbg(isp->dev, "unsupported v4l2 buf type\n"); - return -EINVAL; - } + /* + * There is no guarantee that the buffers queued to / owned by the ISP + * will properly be returned to the queue when stopping. Set a flag to + * avoid new buffers getting queued and then wait for all the current + * buffers to finish. + */ + pipe->stopping = true; + mutex_unlock(&isp->mutex); + /* wait max 1 second */ + ret = wait_event_timeout(pipe->vb_queue.done_wq, + atomisp_buffers_in_css(pipe) == 0, HZ); + mutex_lock(&isp->mutex); + pipe->stopping = false; + if (ret == 0) + dev_warn(isp->dev, "Warning timeout waiting for CSS to return buffers\n"); /* * do only videobuf_streamoff for capture & vf pipes in @@ -1766,36 +1502,10 @@ int atomisp_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) 0, 0, 0); atomisp_freq_scaling(isp, ATOMISP_DFS_MODE_AUTO, false); } - /* - * Currently there is no way to flush buffers queued to css. - * When doing videobuf_streamoff, active buffers will be - * marked as VIDEOBUF_NEEDS_INIT. HAL will be able to use - * these buffers again, and these buffers might be queued to - * css more than once! Warn here, if HAL has not dequeued all - * buffers back before calling streamoff. - */ - if (pipe->buffers_in_css != 0) { - WARN(1, "%s: buffers of vdev %s still in CSS!\n", - __func__, pipe->vdev.name); - - /* - * Buffers remained in css maybe dequeued out in the - * next stream on, while this will causes serious - * issues as buffers already get invalid after - * previous stream off. - * - * No way to flush buffers but to reset the whole css - */ - dev_warn(isp->dev, "Reset CSS to clean up css buffers.\n"); - atomisp_css_flush(isp); - } - return videobuf_streamoff(&pipe->capq); + goto out_unlock; } - if (!pipe->capq.streaming) - return 0; - if (asd->streaming == ATOMISP_DEVICE_STREAMING_ENABLED) first_streamoff = true; @@ -1806,12 +1516,8 @@ int atomisp_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) asd->streaming = ATOMISP_DEVICE_STREAMING_STOPPING; spin_unlock_irqrestore(&isp->lock, flags); - if (!first_streamoff) { - ret = videobuf_streamoff(&pipe->capq); - if (ret) - return ret; + if (!first_streamoff) goto stopsensor; - } atomisp_clear_css_buffer_counters(asd); atomisp_css_irq_enable(isp, IA_CSS_IRQ_INFO_CSS_RECEIVER_SOF, false); @@ -1824,48 +1530,12 @@ int atomisp_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) css_pipe_id = atomisp_get_css_pipe_id(asd); atomisp_css_stop(asd, css_pipe_id, false); - /* cancel work queue*/ - if (asd->video_out_capture.users) { - capture_pipe = &asd->video_out_capture; - wake_up_interruptible(&capture_pipe->capq.wait); - } - if (asd->video_out_vf.users) { - vf_pipe = &asd->video_out_vf; - wake_up_interruptible(&vf_pipe->capq.wait); - } - if (asd->video_out_preview.users) { - preview_pipe = &asd->video_out_preview; - wake_up_interruptible(&preview_pipe->capq.wait); - } - if (asd->video_out_video_capture.users) { - video_pipe = &asd->video_out_video_capture; - wake_up_interruptible(&video_pipe->capq.wait); - } - ret = videobuf_streamoff(&pipe->capq); - if (ret) - return ret; - - /* cleanup css here */ - /* no need for this, as ISP will be reset anyway */ - /*atomisp_flush_bufs_in_css(isp);*/ - - spin_lock_irqsave(&pipe->irq_lock, flags); - list_for_each_entry_safe(vb, _vb, &pipe->activeq, queue) { - vb->state = VIDEOBUF_PREPARED; - list_del(&vb->queue); - } - list_for_each_entry_safe(vb, _vb, &pipe->buffers_waiting_for_param, queue) { - vb->state = VIDEOBUF_PREPARED; - list_del(&vb->queue); - pipe->frame_request_config_id[vb->i] = 0; - } - spin_unlock_irqrestore(&pipe->irq_lock, flags); + atomisp_flush_video_pipe(pipe, true); atomisp_subdev_cleanup_pending_events(asd); stopsensor: - if (atomisp_subdev_streaming_count(asd) + 1 - != atomisp_sensor_start_stream(asd)) - return 0; + if (atomisp_subdev_streaming_count(asd) != atomisp_sensor_start_stream(asd)) + goto out_unlock; ret = v4l2_subdev_call(isp->inputs[asd->input_curr].camera, video, s_stream, 0); @@ -1878,7 +1548,7 @@ stopsensor: /* if other streams are running, isp should not be powered off */ if (atomisp_streaming_count(isp)) { atomisp_css_flush(isp); - return 0; + goto out_unlock; } /* Disable the CSI interface on ANN B0/K0 */ @@ -1894,50 +1564,45 @@ stopsensor: * ISP work around, need to reset isp * Is it correct time to reset ISP when first node does streamoff? */ - if (isp->sw_contex.power_state == ATOM_ISP_POWER_UP) { - unsigned int i; - bool recreate_streams[MAX_STREAM_NUM] = {0}; - - if (isp->isp_timeout) - dev_err(isp->dev, "%s: Resetting with WA activated", - __func__); - /* - * It is possible that the other asd stream is in the stage - * that v4l2_setfmt is just get called on it, which will - * create css stream on that stream. But at this point, there - * is no way to destroy the css stream created on that stream. - * - * So force stream destroy here. - */ - for (i = 0; i < isp->num_of_streams; i++) { - if (isp->asd[i].stream_prepared) { - atomisp_destroy_pipes_stream_force(&isp-> - asd[i]); - recreate_streams[i] = true; - } + if (isp->isp_timeout) + dev_err(isp->dev, "%s: Resetting with WA activated", + __func__); + /* + * It is possible that the other asd stream is in the stage + * that v4l2_setfmt is just get called on it, which will + * create css stream on that stream. But at this point, there + * is no way to destroy the css stream created on that stream. + * + * So force stream destroy here. + */ + for (i = 0; i < isp->num_of_streams; i++) { + if (isp->asd[i].stream_prepared) { + atomisp_destroy_pipes_stream_force(&isp->asd[i]); + recreate_streams[i] = true; } + } - /* disable PUNIT/ISP acknowlede/handshake - SRSE=3 */ - pci_write_config_dword(pdev, PCI_I_CONTROL, - isp->saved_regs.i_control | MRFLD_PCI_I_CONTROL_SRSE_RESET_MASK); - dev_err(isp->dev, "atomisp_reset"); - atomisp_reset(isp); - for (i = 0; i < isp->num_of_streams; i++) { - if (recreate_streams[i]) { - int ret2; - - ret2 = atomisp_create_pipes_stream(&isp->asd[i]); - if (ret2) { - dev_err(isp->dev, "%s error re-creating streams: %d\n", - __func__, ret2); - if (!ret) - ret = ret2; - } + /* disable PUNIT/ISP acknowlede/handshake - SRSE=3 */ + pci_write_config_dword(pdev, PCI_I_CONTROL, + isp->saved_regs.i_control | MRFLD_PCI_I_CONTROL_SRSE_RESET_MASK); + dev_err(isp->dev, "atomisp_reset"); + atomisp_reset(isp); + for (i = 0; i < isp->num_of_streams; i++) { + if (recreate_streams[i]) { + int ret2; + + ret2 = atomisp_create_pipes_stream(&isp->asd[i]); + if (ret2) { + dev_err(isp->dev, "%s error re-creating streams: %d\n", + __func__, ret2); + if (!ret) + ret = ret2; } } - isp->isp_timeout = false; } - return ret; + isp->isp_timeout = false; +out_unlock: + mutex_unlock(&isp->mutex); } /* @@ -2725,13 +2390,13 @@ const struct v4l2_ioctl_ops atomisp_ioctl_ops = { .vidioc_enum_fmt_vid_cap = atomisp_enum_fmt_cap, .vidioc_try_fmt_vid_cap = atomisp_try_fmt_cap, .vidioc_g_fmt_vid_cap = atomisp_g_fmt_cap, - .vidioc_s_fmt_vid_cap = atomisp_set_fmt, - .vidioc_reqbufs = atomisp_reqbufs, - .vidioc_querybuf = atomisp_querybuf, - .vidioc_qbuf = atomisp_qbuf, - .vidioc_dqbuf = atomisp_dqbuf, - .vidioc_streamon = atomisp_streamon, - .vidioc_streamoff = atomisp_streamoff, + .vidioc_s_fmt_vid_cap = atomisp_s_fmt_cap, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = atomisp_qbuf_wrapper, + .vidioc_dqbuf = atomisp_dqbuf_wrapper, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_default = atomisp_vidioc_default, .vidioc_s_parm = atomisp_s_parm, .vidioc_g_parm = atomisp_g_parm, diff --git a/drivers/staging/media/atomisp/pci/atomisp_ioctl.h b/drivers/staging/media/atomisp/pci/atomisp_ioctl.h index c660f631d371..59e071f035f9 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_ioctl.h +++ b/drivers/staging/media/atomisp/pci/atomisp_ioctl.h @@ -39,14 +39,12 @@ int atomisp_pipe_check(struct atomisp_video_pipe *pipe, bool streaming_ok); int atomisp_alloc_css_stat_bufs(struct atomisp_sub_device *asd, uint16_t stream_id); -int atomisp_streamoff(struct file *file, void *fh, enum v4l2_buf_type type); -int atomisp_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *req); +int atomisp_start_streaming(struct vb2_queue *vq, unsigned int count); +void atomisp_stop_streaming(struct vb2_queue *vq); enum ia_css_pipe_id atomisp_get_css_pipe_id(struct atomisp_sub_device *asd); -void atomisp_videobuf_free_buf(struct videobuf_buffer *vb); - extern const struct v4l2_ioctl_ops atomisp_ioctl_ops; unsigned int atomisp_streaming_count(struct atomisp_device *isp); @@ -57,4 +55,8 @@ long atomisp_compat_ioctl32(struct file *file, int atomisp_stream_on_master_slave_sensor(struct atomisp_device *isp, bool isp_timeout); + +int atomisp_start_streaming(struct vb2_queue *vq, unsigned int count); +void atomisp_stop_streaming(struct vb2_queue *vq); + #endif /* __ATOMISP_IOCTL_H__ */ diff --git a/drivers/staging/media/atomisp/pci/atomisp_subdev.c b/drivers/staging/media/atomisp/pci/atomisp_subdev.c index 847dfee6ad78..cadc468b4c2f 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_subdev.c +++ b/drivers/staging/media/atomisp/pci/atomisp_subdev.c @@ -1064,6 +1064,8 @@ static void atomisp_init_subdev_pipe(struct atomisp_sub_device *asd, pipe->asd = asd; pipe->isp = asd->isp; spin_lock_init(&pipe->irq_lock); + mutex_init(&pipe->vb_queue_mutex); + INIT_LIST_HEAD(&pipe->buffers_in_css); INIT_LIST_HEAD(&pipe->activeq); INIT_LIST_HEAD(&pipe->buffers_waiting_for_param); INIT_LIST_HEAD(&pipe->per_frame_params); diff --git a/drivers/staging/media/atomisp/pci/atomisp_subdev.h b/drivers/staging/media/atomisp/pci/atomisp_subdev.h index a1f4da35235d..bd2872cbb50c 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_subdev.h +++ b/drivers/staging/media/atomisp/pci/atomisp_subdev.h @@ -21,8 +21,7 @@ #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-subdev.h> -#include <media/videobuf-core.h> - +#include <media/videobuf2-v4l2.h> #include "atomisp_common.h" #include "atomisp_compat.h" #include "atomisp_v4l2.h" @@ -69,7 +68,12 @@ struct atomisp_video_pipe { struct video_device vdev; enum v4l2_buf_type type; struct media_pad pad; - struct videobuf_queue capq; + struct vb2_queue vb_queue; + /* Lock for vb_queue, when also taking isp->mutex this must be taken first! */ + struct mutex vb_queue_mutex; + /* List of video-buffers handed over to the CSS */ + struct list_head buffers_in_css; + /* List of video-buffers handed over to the driver, but not yet to the CSS */ struct list_head activeq; /* * the buffers waiting for per-frame parameters, this is only valid @@ -79,10 +83,13 @@ struct atomisp_video_pipe { /* the link list to store per_frame parameters */ struct list_head per_frame_params; + /* Filled through atomisp_get_css_frame_info() on queue setup */ + struct ia_css_frame_info frame_info; + /* Store here the initial run mode */ unsigned int default_run_mode; - - unsigned int buffers_in_css; + /* Set from streamoff to disallow queuing further buffers in CSS */ + bool stopping; /* * irq_lock is used to protect video buffer state change operations and @@ -110,6 +117,11 @@ struct atomisp_video_pipe { struct atomisp_css_params_with_list *frame_params[VIDEO_MAX_FRAME]; }; +#define vq_to_pipe(queue) \ + container_of(queue, struct atomisp_video_pipe, vb_queue) + +#define vb_to_pipe(vb) vq_to_pipe((vb)->vb2_queue) + struct atomisp_pad_format { struct v4l2_mbus_framefmt fmt; struct v4l2_rect crop; diff --git a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c index d5bb9906ca6f..e786b81921da 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c +++ b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c @@ -573,11 +573,7 @@ static int atomisp_mrfld_pre_power_down(struct atomisp_device *isp) unsigned long flags; spin_lock_irqsave(&isp->lock, flags); - if (isp->sw_contex.power_state == ATOM_ISP_POWER_DOWN) { - spin_unlock_irqrestore(&isp->lock, flags); - dev_dbg(isp->dev, "<%s %d.\n", __func__, __LINE__); - return 0; - } + /* * MRFLD HAS requirement: cannot power off i-unit if * ISP has IRQ not serviced. @@ -724,62 +720,51 @@ static int atomisp_mrfld_power(struct atomisp_device *isp, bool enable) return -EBUSY; } -/* Workaround for pmu_nc_set_power_state not ready in MRFLD */ -int atomisp_mrfld_power_down(struct atomisp_device *isp) -{ - return atomisp_mrfld_power(isp, false); -} - -/* Workaround for pmu_nc_set_power_state not ready in MRFLD */ -int atomisp_mrfld_power_up(struct atomisp_device *isp) -{ - return atomisp_mrfld_power(isp, true); -} - -int atomisp_runtime_suspend(struct device *dev) +int atomisp_power_off(struct device *dev) { - struct atomisp_device *isp = (struct atomisp_device *) - dev_get_drvdata(dev); + struct atomisp_device *isp = dev_get_drvdata(dev); + struct pci_dev *pdev = to_pci_dev(dev); int ret; + u32 reg; + + atomisp_css_uninit(isp); ret = atomisp_mrfld_pre_power_down(isp); if (ret) return ret; - /*Turn off the ISP d-phy*/ - ret = atomisp_ospm_dphy_down(isp); - if (ret) - return ret; + /* + * MRFLD IUNIT DPHY is located in an always-power-on island + * MRFLD HW design need all CSI ports are disabled before + * powering down the IUNIT. + */ + pci_read_config_dword(pdev, MRFLD_PCI_CSI_CONTROL, ®); + reg |= MRFLD_ALL_CSI_PORTS_OFF_MASK; + pci_write_config_dword(pdev, MRFLD_PCI_CSI_CONTROL, reg); + cpu_latency_qos_update_request(&isp->pm_qos, PM_QOS_DEFAULT_VALUE); - return atomisp_mrfld_power_down(isp); + return atomisp_mrfld_power(isp, false); } -int atomisp_runtime_resume(struct device *dev) +int atomisp_power_on(struct device *dev) { struct atomisp_device *isp = (struct atomisp_device *) dev_get_drvdata(dev); int ret; - ret = atomisp_mrfld_power_up(isp); + ret = atomisp_mrfld_power(isp, true); if (ret) return ret; cpu_latency_qos_update_request(&isp->pm_qos, isp->max_isr_latency); - if (isp->sw_contex.power_state == ATOM_ISP_POWER_DOWN) { - /*Turn on ISP d-phy */ - ret = atomisp_ospm_dphy_up(isp); - if (ret) { - dev_err(isp->dev, "Failed to power up ISP!.\n"); - return -EINVAL; - } - } /*restore register values for iUnit and iUnitPHY registers*/ if (isp->saved_regs.pcicmdsts) atomisp_restore_iunit_reg(isp); atomisp_freq_scaling(isp, ATOMISP_DFS_MODE_LOW, true); - return 0; + + return atomisp_css_init(isp); } static int __maybe_unused atomisp_suspend(struct device *dev) @@ -789,7 +774,6 @@ static int __maybe_unused atomisp_suspend(struct device *dev) /* FIXME: only has one isp_subdev at present */ struct atomisp_sub_device *asd = &isp->asd[0]; unsigned long flags; - int ret; /* * FIXME: Suspend is not supported by sensors. Abort if any video @@ -806,45 +790,12 @@ static int __maybe_unused atomisp_suspend(struct device *dev) } spin_unlock_irqrestore(&isp->lock, flags); - ret = atomisp_mrfld_pre_power_down(isp); - if (ret) - return ret; - - /*Turn off the ISP d-phy */ - ret = atomisp_ospm_dphy_down(isp); - if (ret) { - dev_err(isp->dev, "fail to power off ISP\n"); - return ret; - } - cpu_latency_qos_update_request(&isp->pm_qos, PM_QOS_DEFAULT_VALUE); - return atomisp_mrfld_power_down(isp); + return atomisp_power_off(dev); } static int __maybe_unused atomisp_resume(struct device *dev) { - struct atomisp_device *isp = (struct atomisp_device *) - dev_get_drvdata(dev); - int ret; - - ret = atomisp_mrfld_power_up(isp); - if (ret) - return ret; - - cpu_latency_qos_update_request(&isp->pm_qos, isp->max_isr_latency); - - /*Turn on ISP d-phy */ - ret = atomisp_ospm_dphy_up(isp); - if (ret) { - dev_err(isp->dev, "Failed to power up ISP!.\n"); - return -EINVAL; - } - - /*restore register values for iUnit and iUnitPHY registers*/ - if (isp->saved_regs.pcicmdsts) - atomisp_restore_iunit_reg(isp); - - atomisp_freq_scaling(isp, ATOMISP_DFS_MODE_LOW, true); - return 0; + return atomisp_power_on(dev); } int atomisp_csi_lane_config(struct atomisp_device *isp) @@ -1459,7 +1410,6 @@ static int atomisp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i isp->dev = &pdev->dev; isp->base = pcim_iomap_table(pdev)[ATOM_ISP_PCI_BAR]; - isp->sw_contex.power_state = ATOM_ISP_POWER_UP; isp->saved_regs.ispmmadr = start; dev_dbg(&pdev->dev, "atomisp mmio base: %p\n", isp->base); @@ -1723,10 +1673,8 @@ load_fw_fail: atomisp_msi_irq_uninit(isp); - atomisp_ospm_dphy_down(isp); - /* Address later when we worry about the ...field chips */ - if (IS_ENABLED(CONFIG_PM) && atomisp_mrfld_power_down(isp)) + if (IS_ENABLED(CONFIG_PM) && atomisp_mrfld_power(isp, false)) dev_err(&pdev->dev, "Failed to switch off ISP\n"); atomisp_dev_alloc_fail: @@ -1774,8 +1722,8 @@ static const struct pci_device_id atomisp_pci_tbl[] = { MODULE_DEVICE_TABLE(pci, atomisp_pci_tbl); static const struct dev_pm_ops atomisp_pm_ops = { - .runtime_suspend = atomisp_runtime_suspend, - .runtime_resume = atomisp_runtime_resume, + .runtime_suspend = atomisp_power_off, + .runtime_resume = atomisp_power_on, .suspend = atomisp_suspend, .resume = atomisp_resume, }; diff --git a/drivers/staging/media/atomisp/pci/camera/pipe/interface/ia_css_pipe_binarydesc.h b/drivers/staging/media/atomisp/pci/camera/pipe/interface/ia_css_pipe_binarydesc.h index 965cfda50707..e42eeaeb3ee4 100644 --- a/drivers/staging/media/atomisp/pci/camera/pipe/interface/ia_css_pipe_binarydesc.h +++ b/drivers/staging/media/atomisp/pci/camera/pipe/interface/ia_css_pipe_binarydesc.h @@ -16,6 +16,8 @@ #ifndef __IA_CSS_PIPE_BINARYDESC_H__ #define __IA_CSS_PIPE_BINARYDESC_H__ +#include <linux/math.h> + #include <ia_css_types.h> /* ia_css_pipe */ #include <ia_css_frame_public.h> /* ia_css_frame_info */ #include <ia_css_binary.h> /* ia_css_binary_descr */ @@ -56,17 +58,12 @@ void ia_css_pipe_get_vfpp_binarydesc( * * @param[in] bds_factor: The bayer downscaling factor. * (= The bds_factor member in the sh_css_bds_factor structure.) - * @param[out] bds_factor_numerator: The numerator of the bayer downscaling factor. - * (= The numerator member in the sh_css_bds_factor structure.) - * @param[out] bds_factor_denominator: The denominator of the bayer downscaling factor. - * (= The denominator member in the sh_css_bds_factor structure.) + * @param[out] bds: The rational fraction of the bayer downscaling factor. + * (= The respective member in the sh_css_bds_factor structure.) * @return 0 or error code upon error. * */ -int sh_css_bds_factor_get_numerator_denominator( - unsigned int bds_factor, - unsigned int *bds_factor_numerator, - unsigned int *bds_factor_denominator); +int sh_css_bds_factor_get_fract(unsigned int bds_factor, struct u32_fract *bds); /* @brief Get a binary descriptor for preview stage. * diff --git a/drivers/staging/media/atomisp/pci/camera/pipe/interface/ia_css_pipe_stagedesc.h b/drivers/staging/media/atomisp/pci/camera/pipe/interface/ia_css_pipe_stagedesc.h index 40c8145a0797..7a0c988d89ee 100644 --- a/drivers/staging/media/atomisp/pci/camera/pipe/interface/ia_css_pipe_stagedesc.h +++ b/drivers/staging/media/atomisp/pci/camera/pipe/interface/ia_css_pipe_stagedesc.h @@ -38,11 +38,6 @@ void ia_css_pipe_get_firmwares_stage_desc( const struct ia_css_fw_info *fw, unsigned int mode); -void ia_css_pipe_get_acc_stage_desc( - struct ia_css_pipeline_stage_desc *stage_desc, - struct ia_css_binary *binary, - struct ia_css_fw_info *fw); - void ia_css_pipe_get_sp_func_stage_desc( struct ia_css_pipeline_stage_desc *stage_desc, struct ia_css_frame *out_frame, diff --git a/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_binarydesc.c b/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_binarydesc.c index 7dd0e4a53c8b..06664ce75b60 100644 --- a/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_binarydesc.c +++ b/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_binarydesc.c @@ -13,6 +13,9 @@ * more details. */ +#include <linux/kernel.h> +#include <linux/math.h> + #include "ia_css_pipe_binarydesc.h" #include "ia_css_frame_format.h" #include "ia_css_pipe.h" @@ -23,7 +26,6 @@ #include <assert_support.h> /* HRT_GDC_N */ #include "gdc_device.h" -#include <linux/kernel.h> /* This module provides a binary descriptions to used to find a binary. Since, * every stage is associated with a binary, it implicity helps stage @@ -126,40 +128,29 @@ void ia_css_pipe_get_vfpp_binarydesc( IA_CSS_LEAVE_PRIVATE(""); } -static struct sh_css_bds_factor bds_factors_list[] = { - {1, 1, SH_CSS_BDS_FACTOR_1_00}, - {5, 4, SH_CSS_BDS_FACTOR_1_25}, - {3, 2, SH_CSS_BDS_FACTOR_1_50}, - {2, 1, SH_CSS_BDS_FACTOR_2_00}, - {9, 4, SH_CSS_BDS_FACTOR_2_25}, - {5, 2, SH_CSS_BDS_FACTOR_2_50}, - {3, 1, SH_CSS_BDS_FACTOR_3_00}, - {4, 1, SH_CSS_BDS_FACTOR_4_00}, - {9, 2, SH_CSS_BDS_FACTOR_4_50}, - {5, 1, SH_CSS_BDS_FACTOR_5_00}, - {6, 1, SH_CSS_BDS_FACTOR_6_00}, - {8, 1, SH_CSS_BDS_FACTOR_8_00} +static struct u32_fract bds_factors_list[] = { + [SH_CSS_BDS_FACTOR_1_00] = {1, 1}, + [SH_CSS_BDS_FACTOR_1_25] = {5, 4}, + [SH_CSS_BDS_FACTOR_1_50] = {3, 2}, + [SH_CSS_BDS_FACTOR_2_00] = {2, 1}, + [SH_CSS_BDS_FACTOR_2_25] = {9, 4}, + [SH_CSS_BDS_FACTOR_2_50] = {5, 2}, + [SH_CSS_BDS_FACTOR_3_00] = {3, 1}, + [SH_CSS_BDS_FACTOR_4_00] = {4, 1}, + [SH_CSS_BDS_FACTOR_4_50] = {9, 2}, + [SH_CSS_BDS_FACTOR_5_00] = {5, 1}, + [SH_CSS_BDS_FACTOR_6_00] = {6, 1}, + [SH_CSS_BDS_FACTOR_8_00] = {8, 1}, }; -int sh_css_bds_factor_get_numerator_denominator( - unsigned int bds_factor, - unsigned int *bds_factor_numerator, - unsigned int *bds_factor_denominator) +int sh_css_bds_factor_get_fract(unsigned int bds_factor, struct u32_fract *bds) { - unsigned int i; - - /* Loop over all bds factors until a match is found */ - for (i = 0; i < ARRAY_SIZE(bds_factors_list); i++) { - if (bds_factors_list[i].bds_factor == bds_factor) { - *bds_factor_numerator = bds_factors_list[i].numerator; - *bds_factor_denominator = bds_factors_list[i].denominator; - return 0; - } - } + /* Throw an error since bds_factor cannot be found in bds_factors_list */ + if (bds_factor >= ARRAY_SIZE(bds_factors_list)) + return -EINVAL; - /* Throw an error since bds_factor cannot be found - in bds_factors_list */ - return -EINVAL; + *bds = bds_factors_list[bds_factor]; + return 0; } int binarydesc_calculate_bds_factor( @@ -194,7 +185,7 @@ int binarydesc_calculate_bds_factor( (out_h * num / den <= in_h); if (cond) { - *bds_factor = bds_factors_list[i].bds_factor; + *bds_factor = i; return 0; } } diff --git a/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_stagedesc.c b/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_stagedesc.c index 82a24aabe8ce..6c93fa1c683b 100644 --- a/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_stagedesc.c +++ b/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_stagedesc.c @@ -74,27 +74,6 @@ void ia_css_pipe_get_firmwares_stage_desc( stage_desc->vf_frame = vf_frame; } -void ia_css_pipe_get_acc_stage_desc( - struct ia_css_pipeline_stage_desc *stage_desc, - struct ia_css_binary *binary, - struct ia_css_fw_info *fw) -{ - unsigned int i; - - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, - "ia_css_pipe_get_acc_stage_desc() enter:\n"); - stage_desc->binary = binary; - stage_desc->firmware = fw; - stage_desc->sp_func = IA_CSS_PIPELINE_NO_FUNC; - stage_desc->max_input_width = 0; - stage_desc->mode = IA_CSS_BINARY_MODE_VF_PP; - stage_desc->in_frame = NULL; - for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) { - stage_desc->out_frame[i] = NULL; - } - stage_desc->vf_frame = NULL; -} - void ia_css_pipe_get_sp_func_stage_desc( struct ia_css_pipeline_stage_desc *stage_desc, struct ia_css_frame *out_frame, diff --git a/drivers/staging/media/atomisp/pci/css_2401_system/host/pixelgen_private.h b/drivers/staging/media/atomisp/pci/css_2401_system/host/pixelgen_private.h index 1c7938d8ccb5..8f79424bedb2 100644 --- a/drivers/staging/media/atomisp/pci/css_2401_system/host/pixelgen_private.h +++ b/drivers/staging/media/atomisp/pci/css_2401_system/host/pixelgen_private.h @@ -161,7 +161,7 @@ STORAGE_CLASS_PIXELGEN_C void pixelgen_ctrl_dump_state( state->syng_stat_fcnt); ia_css_print("Pixel Generator ID %d syng stat done 0x%x\n", ID, state->syng_stat_done); - ia_css_print("Pixel Generator ID %d tpg modee 0x%x\n", ID, state->tpg_mode); + ia_css_print("Pixel Generator ID %d tpg mode 0x%x\n", ID, state->tpg_mode); ia_css_print("Pixel Generator ID %d tpg hcnt mask 0x%x\n", ID, state->tpg_hcnt_mask); ia_css_print("Pixel Generator ID %d tpg hcnt mask 0x%x\n", ID, diff --git a/drivers/staging/media/atomisp/pci/hmm/hmm.c b/drivers/staging/media/atomisp/pci/hmm/hmm.c index fc6cfe9f7744..bb12644fd033 100644 --- a/drivers/staging/media/atomisp/pci/hmm/hmm.c +++ b/drivers/staging/media/atomisp/pci/hmm/hmm.c @@ -41,11 +41,9 @@ static bool hmm_initialized; /* * p: private - * s: shared - * u: user - * i: ion + * v: vmalloc */ -static const char hmm_bo_type_string[] = "psui"; +static const char hmm_bo_type_string[] = "pv"; static ssize_t bo_show(struct device *dev, struct device_attribute *attr, char *buf, struct list_head *bo_list, bool active) @@ -168,7 +166,8 @@ void hmm_cleanup(void) hmm_initialized = false; } -static ia_css_ptr __hmm_alloc(size_t bytes, enum hmm_bo_type type, const void __user *userptr) +static ia_css_ptr __hmm_alloc(size_t bytes, enum hmm_bo_type type, + void *vmalloc_addr) { unsigned int pgnr; struct hmm_buffer_object *bo; @@ -192,7 +191,7 @@ static ia_css_ptr __hmm_alloc(size_t bytes, enum hmm_bo_type type, const void __ } /* Allocate pages for memory */ - ret = hmm_bo_alloc_pages(bo, type, userptr); + ret = hmm_bo_alloc_pages(bo, type, vmalloc_addr); if (ret) { dev_err(atomisp_dev, "hmm_bo_alloc_pages failed.\n"); goto alloc_page_err; @@ -205,9 +204,8 @@ static ia_css_ptr __hmm_alloc(size_t bytes, enum hmm_bo_type type, const void __ goto bind_err; } - dev_dbg(atomisp_dev, - "%s: pages: 0x%08x (%zu bytes), type: %d, user ptr %p\n", - __func__, bo->start, bytes, type, userptr); + dev_dbg(atomisp_dev, "pages: 0x%08x (%zu bytes), type: %d, vmalloc %p\n", + bo->start, bytes, type, vmalloc); return bo->start; @@ -224,9 +222,9 @@ ia_css_ptr hmm_alloc(size_t bytes) return __hmm_alloc(bytes, HMM_BO_PRIVATE, NULL); } -ia_css_ptr hmm_create_from_userdata(size_t bytes, const void __user *userptr) +ia_css_ptr hmm_create_from_vmalloc_buf(size_t bytes, void *vmalloc_addr) { - return __hmm_alloc(bytes, HMM_BO_USER, userptr); + return __hmm_alloc(bytes, HMM_BO_VMALLOC, vmalloc_addr); } void hmm_free(ia_css_ptr virt) diff --git a/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c b/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c index a5fd6d38d3c4..5e53eed8ae95 100644 --- a/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c +++ b/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c @@ -638,6 +638,7 @@ static int alloc_private_pages(struct hmm_buffer_object *bo) ret = alloc_pages_bulk_array(gfp, bo->pgnr, bo->pages); if (ret != bo->pgnr) { free_pages_bulk_array(ret, bo->pages); + dev_err(atomisp_dev, "alloc_pages_bulk_array() failed\n"); return -ENOMEM; } @@ -651,61 +652,34 @@ static int alloc_private_pages(struct hmm_buffer_object *bo) return 0; } -static void free_user_pages(struct hmm_buffer_object *bo, - unsigned int page_nr) +static int alloc_vmalloc_pages(struct hmm_buffer_object *bo, void *vmalloc_addr) { + void *vaddr = vmalloc_addr; int i; - for (i = 0; i < page_nr; i++) - put_page(bo->pages[i]); -} - -/* - * Convert user space virtual address into pages list - */ -static int alloc_user_pages(struct hmm_buffer_object *bo, - const void __user *userptr) -{ - int page_nr; - - userptr = untagged_addr(userptr); - - /* Handle frame buffer allocated in user space */ - mutex_unlock(&bo->mutex); - page_nr = get_user_pages_fast((unsigned long)userptr, bo->pgnr, 1, bo->pages); - mutex_lock(&bo->mutex); - - /* can be written by caller, not forced */ - if (page_nr != bo->pgnr) { - dev_err(atomisp_dev, - "get_user_pages err: bo->pgnr = %d, pgnr actually pinned = %d.\n", - bo->pgnr, page_nr); - if (page_nr < 0) - page_nr = 0; - goto out_of_mem; + for (i = 0; i < bo->pgnr; i++) { + bo->pages[i] = vmalloc_to_page(vaddr); + if (!bo->pages[i]) { + dev_err(atomisp_dev, "Error could not get page %d of vmalloc buf\n", i); + return -ENOMEM; + } + vaddr += PAGE_SIZE; } return 0; - -out_of_mem: - - free_user_pages(bo, page_nr); - - return -ENOMEM; } /* * allocate/free physical pages for the bo. * * type indicate where are the pages from. currently we have 3 types - * of memory: HMM_BO_PRIVATE, HMM_BO_USER. + * of memory: HMM_BO_PRIVATE, HMM_BO_VMALLOC. * - * userptr is only valid when type is HMM_BO_USER, it indicates - * the start address from user space task. + * vmalloc_addr is only valid when type is HMM_BO_VMALLOC. */ int hmm_bo_alloc_pages(struct hmm_buffer_object *bo, enum hmm_bo_type type, - const void __user *userptr) + void *vmalloc_addr) { int ret = -EINVAL; @@ -720,14 +694,10 @@ int hmm_bo_alloc_pages(struct hmm_buffer_object *bo, goto alloc_err; } - /* - * TO DO: - * add HMM_BO_USER type - */ if (type == HMM_BO_PRIVATE) { ret = alloc_private_pages(bo); - } else if (type == HMM_BO_USER) { - ret = alloc_user_pages(bo, userptr); + } else if (type == HMM_BO_VMALLOC) { + ret = alloc_vmalloc_pages(bo, vmalloc_addr); } else { dev_err(atomisp_dev, "invalid buffer type.\n"); ret = -EINVAL; @@ -771,8 +741,8 @@ void hmm_bo_free_pages(struct hmm_buffer_object *bo) if (bo->type == HMM_BO_PRIVATE) free_private_bo_pages(bo); - else if (bo->type == HMM_BO_USER) - free_user_pages(bo, bo->pgnr); + else if (bo->type == HMM_BO_VMALLOC) + ; /* No-op, nothing to do */ else dev_err(atomisp_dev, "invalid buffer type.\n"); diff --git a/drivers/staging/media/atomisp/pci/ia_css_frame_public.h b/drivers/staging/media/atomisp/pci/ia_css_frame_public.h index 514d933f934d..7ba464abf447 100644 --- a/drivers/staging/media/atomisp/pci/ia_css_frame_public.h +++ b/drivers/staging/media/atomisp/pci/ia_css_frame_public.h @@ -20,6 +20,7 @@ * This file contains structs to describe various frame-formats supported by the ISP. */ +#include <media/videobuf2-v4l2.h> #include <type_support.h> #include "ia_css_err.h" #include "ia_css_types.h" @@ -146,7 +147,18 @@ enum ia_css_frame_flash_state { * This is the main structure used for all input and output images. */ struct ia_css_frame { - struct ia_css_frame_info info; /** info struct describing the frame */ + /* + * The videobuf2 core will allocate buffers including room for private + * data (the rest of struct ia_css_frame). The vb2_v4l2_buffer must + * be the first member for this to work! + * Note the atomisp code also uses ia_css_frame-s which are not used + * as v4l2-buffers in some places. In this case the vb2 member will + * be unused. + */ + struct vb2_v4l2_buffer vb; + /* List-head for linking into the activeq or buffers_waiting_for_param list */ + struct list_head queue; + struct ia_css_frame_info frame_info; /** info struct describing the frame */ ia_css_ptr data; /** pointer to start of image data */ unsigned int data_bytes; /** size of image data in bytes */ /* LA: move this to ia_css_buffer */ @@ -183,22 +195,16 @@ struct ia_css_frame { info.format */ }; +#define vb_to_frame(vb2) \ + container_of(to_vb2_v4l2_buffer(vb2), struct ia_css_frame, vb) + #define DEFAULT_FRAME { \ - .info = IA_CSS_BINARY_DEFAULT_FRAME_INFO, \ + .frame_info = IA_CSS_BINARY_DEFAULT_FRAME_INFO, \ .dynamic_queue_id = SH_CSS_INVALID_QUEUE_ID, \ .buf_type = IA_CSS_BUFFER_TYPE_INVALID, \ .flash_state = IA_CSS_FRAME_FLASH_STATE_NONE, \ } -/* @brief Fill a frame with zeros - * - * @param frame The frame. - * @return None - * - * Fill a frame with pixel values of zero - */ -void ia_css_frame_zero(struct ia_css_frame *frame); - /* @brief Allocate a CSS frame structure * * @param frame The allocated frame. @@ -220,6 +226,17 @@ ia_css_frame_allocate(struct ia_css_frame **frame, unsigned int stride, unsigned int raw_bit_depth); +/* @brief Initialize a CSS frame structure using a frame info structure. + * + * @param frame The allocated frame. + * @param[in] info The frame info structure. + * @return The error code. + * + * Initialize a frame using the resolution and format from a frame info struct. + */ +int ia_css_frame_init_from_info(struct ia_css_frame *frame, + const struct ia_css_frame_info *info); + /* @brief Allocate a CSS frame structure using a frame info structure. * * @param frame The allocated frame. @@ -244,69 +261,10 @@ ia_css_frame_allocate_from_info(struct ia_css_frame **frame, void ia_css_frame_free(struct ia_css_frame *frame); -/* @brief Allocate a CSS frame structure using a frame info structure. - * - * @param frame The allocated frame. - * @param[in] info The frame info structure. - * @return The error code. - * - * Allocate an empty CSS frame with no data buffer using the parameters - * in the frame info. - */ -int -ia_css_frame_create_from_info(struct ia_css_frame **frame, - const struct ia_css_frame_info *info); - -/* @brief Set a mapped data buffer to a CSS frame - * - * @param[in] frame Valid CSS frame pointer - * @param[in] mapped_data Mapped data buffer to be assigned to the CSS frame - * @param[in] data_size_bytes Size of the mapped_data in bytes - * @return The error code. - * - * Sets a mapped data buffer to this frame. This function can be called multiple - * times with different buffers or NULL to reset the data pointer. This API - * would not try free the mapped_data and its the callers responsiblity to - * free the mapped_data buffer. However if ia_css_frame_free() is called and - * the frame had a valid data buffer, it would be freed along with the frame. - */ -int -ia_css_frame_set_data(struct ia_css_frame *frame, - const ia_css_ptr mapped_data, - size_t data_size_bytes); - -/* @brief Map an existing frame data pointer to a CSS frame. - * - * @param frame Pointer to the frame to be initialized - * @param[in] info The frame info. - * @param[in] data Pointer to the allocated frame data. - * @param[in] attribute Attributes to be passed to mmgr_mmap. - * @param[in] context Pointer to the a context to be passed to mmgr_mmap. - * @return The allocated frame structure. - * - * This function maps a pre-allocated pointer into a CSS frame. This can be - * used when an upper software layer is responsible for allocating the frame - * data and it wants to share that frame pointer with the CSS code. - * This function will fill the CSS frame structure just like - * ia_css_frame_allocate() does, but instead of allocating the memory, it will - * map the pre-allocated memory into the CSS address space. - */ -int -ia_css_frame_map(struct ia_css_frame **frame, - const struct ia_css_frame_info *info, - const void __user *data, - unsigned int pgnr); - -/* @brief Unmap a CSS frame structure. - * - * @param[in] frame Pointer to the CSS frame. - * @return None - * - * This function unmaps the frame data pointer within a CSS frame and - * then frees the CSS frame structure. Use this for frame pointers created - * using ia_css_frame_map(). - */ -void -ia_css_frame_unmap(struct ia_css_frame *frame); +static inline const struct ia_css_frame_info * +ia_css_frame_get_info(const struct ia_css_frame *frame) +{ + return frame ? &frame->frame_info : NULL; +} #endif /* __IA_CSS_FRAME_PUBLIC_H */ diff --git a/drivers/staging/media/atomisp/pci/ia_css_pipe.h b/drivers/staging/media/atomisp/pci/ia_css_pipe.h index fb58535bff40..22522968b9e6 100644 --- a/drivers/staging/media/atomisp/pci/ia_css_pipe.h +++ b/drivers/staging/media/atomisp/pci/ia_css_pipe.h @@ -37,7 +37,6 @@ struct ia_css_preview_settings { struct ia_css_pipe *copy_pipe; struct ia_css_pipe *capture_pipe; - struct ia_css_pipe *acc_pipe; }; #define IA_CSS_DEFAULT_PREVIEW_SETTINGS { \ @@ -156,7 +155,7 @@ struct ia_css_pipe { #define IA_CSS_DEFAULT_PIPE { \ .config = DEFAULT_PIPE_CONFIG, \ .info = DEFAULT_PIPE_INFO, \ - .mode = IA_CSS_PIPE_ID_ACC, /* (pipe_id) */ \ + .mode = IA_CSS_PIPE_ID_VIDEO, /* (pipe_id) */ \ .pipeline = DEFAULT_PIPELINE, \ .output_info = {IA_CSS_BINARY_DEFAULT_FRAME_INFO}, \ .bds_output_info = IA_CSS_BINARY_DEFAULT_FRAME_INFO, \ diff --git a/drivers/staging/media/atomisp/pci/ia_css_pipe_public.h b/drivers/staging/media/atomisp/pci/ia_css_pipe_public.h index 7352cbf779fb..8ac1586dce4e 100644 --- a/drivers/staging/media/atomisp/pci/ia_css_pipe_public.h +++ b/drivers/staging/media/atomisp/pci/ia_css_pipe_public.h @@ -45,7 +45,6 @@ enum ia_css_pipe_mode { IA_CSS_PIPE_MODE_PREVIEW, /** Preview pipe */ IA_CSS_PIPE_MODE_VIDEO, /** Video pipe */ IA_CSS_PIPE_MODE_CAPTURE, /** Still capture pipe */ - IA_CSS_PIPE_MODE_ACC, /** Accelerated pipe */ IA_CSS_PIPE_MODE_COPY, /** Copy pipe, only used for embedded/image data copying */ IA_CSS_PIPE_MODE_YUVPP, /** YUV post processing pipe, used for all use cases with YUV input, for SoC sensor and external ISP */ @@ -95,21 +94,11 @@ struct ia_css_pipe_config { /** output of YUV scaling */ struct ia_css_frame_info vf_output_info[IA_CSS_PIPE_MAX_OUTPUT_STAGE]; /** output of VF YUV scaling */ - struct ia_css_fw_info *acc_extension; - /** Pipeline extension accelerator */ - struct ia_css_fw_info **acc_stages; - /** Standalone accelerator stages */ - u32 num_acc_stages; - /** Number of standalone accelerator stages */ struct ia_css_capture_config default_capture_config; /** Default capture config for initial capture pipe configuration. */ struct ia_css_resolution dvs_envelope; /** temporary */ enum ia_css_frame_delay dvs_frame_delay; /** indicates the DVS loop delay in frame periods */ - int acc_num_execs; - /** For acceleration pipes only: determine how many times the pipe - should be run. Setting this to -1 means it will run until - stopped. */ bool enable_dz; /** Disabling digital zoom for a pipeline, if this is set to false, then setting a zoom factor will have no effect. @@ -153,7 +142,6 @@ struct ia_css_pipe_config { .vf_output_info = {IA_CSS_BINARY_DEFAULT_FRAME_INFO}, \ .default_capture_config = DEFAULT_CAPTURE_CONFIG, \ .dvs_frame_delay = IA_CSS_FRAME_DELAY_1, \ - .acc_num_execs = -1, \ } /* Pipe info, this struct describes properties of a pipe after it's stream has @@ -224,9 +212,6 @@ struct ia_css_pipe_info { {{0, 0}, 0, 0, 0, 0}, // second_output_info {{0, 0}, 0, 0, 0, 0}, // vf_output_info {{0, 0}, 0, 0, 0, 0}, // second_vf_output_info - NULL, // acc_extension - NULL, // acc_stages - 0, // num_acc_stages { IA_CSS_CAPTURE_MODE_RAW, // mode false, // enable_xnr @@ -234,7 +219,6 @@ struct ia_css_pipe_info { }, // default_capture_config {0, 0}, // dvs_envelope 1, // dvs_frame_delay - -1, // acc_num_execs true, // enable_dz NULL, // p_isp_config }; @@ -426,59 +410,6 @@ int ia_css_pipe_dequeue_buffer(struct ia_css_pipe *pipe, struct ia_css_buffer *buffer); -/* @brief Set the state (Enable or Disable) of the Extension stage in the - * given pipe. - * @param[in] pipe Pipe handle. - * @param[in] fw_handle Extension firmware Handle (ia_css_fw_info.handle) - * @param[in] enable Enable Flag (1 to enable ; 0 to disable) - * - * @return - * 0 : Success - * -EINVAL : Invalid Parameters - * -EBUSY : Inactive QOS Pipe - * (No active stream with this pipe) - * - * This function will request state change (enable or disable) for the Extension - * stage (firmware handle) in the given pipe. - * - * Note: - * 1. Extension can be enabled/disabled only on QOS Extensions - * 2. Extension can be enabled/disabled only with an active QOS Pipe - * 3. Initial(Default) state of QOS Extensions is Disabled - * 4. State change cannot be guaranteed immediately OR on frame boundary - * - */ -int -ia_css_pipe_set_qos_ext_state(struct ia_css_pipe *pipe, - u32 fw_handle, - bool enable); - -/* @brief Get the state (Enable or Disable) of the Extension stage in the - * given pipe. - * @param[in] pipe Pipe handle. - * @param[in] fw_handle Extension firmware Handle (ia_css_fw_info.handle) - * @param[out] *enable Enable Flag - * - * @return - * 0 : Success - * -EINVAL : Invalid Parameters - * -EBUSY : Inactive QOS Pipe - * (No active stream with this pipe) - * - * This function will query the state of the Extension stage (firmware handle) - * in the given Pipe. - * - * Note: - * 1. Extension state can be queried only on QOS Extensions - * 2. Extension can be enabled/disabled only with an active QOS Pipe - * 3. Initial(Default) state of QOS Extensions is Disabled. - * - */ -int -ia_css_pipe_get_qos_ext_state(struct ia_css_pipe *pipe, - u32 fw_handle, - bool *enable); - /* @brief Get selected configuration settings * @param[in] pipe The pipe. * @param[out] config Configuration settings. diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/bayer_io_ls/ia_css_bayer_io.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/bayer_io_ls/ia_css_bayer_io.host.c index c7d88552dfde..0091e2a3da52 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/bayer_io_ls/ia_css_bayer_io.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/bayer_io_ls/ia_css_bayer_io.host.c @@ -28,9 +28,7 @@ int ia_css_bayer_io_config(const struct ia_css_binary *binary, const struct ia_css_frame *in_frame = args->in_frame; const struct ia_css_frame **out_frames = (const struct ia_css_frame **) &args->out_frame; - const struct ia_css_frame_info *in_frame_info = (in_frame) ? &in_frame->info : - &binary->in_frame_info; - + const struct ia_css_frame_info *in_frame_info = ia_css_frame_get_info(in_frame); const unsigned int ddr_bits_per_element = sizeof(short) * 8; const unsigned int ddr_elems_per_word = ceil_div(HIVE_ISP_DDR_WORD_BITS, ddr_bits_per_element); @@ -80,12 +78,12 @@ int ia_css_bayer_io_config(const struct ia_css_binary *binary, "ia_css_bayer_io_config() put part enter:\n"); #endif - ret = ia_css_dma_configure_from_info(&config, &out_frames[0]->info); + ret = ia_css_dma_configure_from_info(&config, &out_frames[0]->frame_info); if (ret) return ret; to->base_address = out_frames[0]->data; - to->width = out_frames[0]->info.res.width; - to->height = out_frames[0]->info.res.height; + to->width = out_frames[0]->frame_info.res.width; + to->height = out_frames[0]->frame_info.res.height; to->stride = config.stride; to->ddr_elems_per_word = ddr_elems_per_word; diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/yuv444_io_ls/ia_css_yuv444_io.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/yuv444_io_ls/ia_css_yuv444_io.host.c index 7d2ef6e26ee6..32c504a950ce 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/yuv444_io_ls/ia_css_yuv444_io.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/yuv444_io_ls/ia_css_yuv444_io.host.c @@ -28,9 +28,7 @@ int ia_css_yuv444_io_config(const struct ia_css_binary *binary, const struct ia_css_frame *in_frame = args->in_frame; const struct ia_css_frame **out_frames = (const struct ia_css_frame **) &args->out_frame; - const struct ia_css_frame_info *in_frame_info = (in_frame) ? &in_frame->info : - &binary->in_frame_info; - + const struct ia_css_frame_info *in_frame_info = ia_css_frame_get_info(in_frame); const unsigned int ddr_bits_per_element = sizeof(short) * 8; const unsigned int ddr_elems_per_word = ceil_div(HIVE_ISP_DDR_WORD_BITS, ddr_bits_per_element); @@ -81,13 +79,13 @@ int ia_css_yuv444_io_config(const struct ia_css_binary *binary, "ia_css_yuv444_io_config() put part enter:\n"); #endif - ret = ia_css_dma_configure_from_info(&config, &out_frames[0]->info); + ret = ia_css_dma_configure_from_info(&config, &out_frames[0]->frame_info); if (ret) return ret; to->base_address = out_frames[0]->data; - to->width = out_frames[0]->info.res.width; - to->height = out_frames[0]->info.res.height; + to->width = out_frames[0]->frame_info.res.width; + to->height = out_frames[0]->frame_info.res.height; to->stride = config.stride; to->ddr_elems_per_word = ddr_elems_per_word; diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/ref/ref_1.0/ia_css_ref.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/ref/ref_1.0/ia_css_ref.host.c index 08ed916a7eb8..9288a7a37b37 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/ref/ref_1.0/ia_css_ref.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/ref/ref_1.0/ia_css_ref.host.c @@ -30,7 +30,7 @@ int ia_css_ref_config(struct sh_css_isp_ref_isp_config *to, int ret; if (from->ref_frames[0]) { - ret = ia_css_dma_configure_from_info(&to->port_b, &from->ref_frames[0]->info); + ret = ia_css_dma_configure_from_info(&to->port_b, &from->ref_frames[0]->frame_info); if (ret) return ret; to->width_a_over_b = elems_a / to->port_b.elems; diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/tnr/tnr_1.0/ia_css_tnr.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/tnr/tnr_1.0/ia_css_tnr.host.c index 53050c0c49fc..a5fea753ec64 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/tnr/tnr_1.0/ia_css_tnr.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/tnr/tnr_1.0/ia_css_tnr.host.c @@ -79,11 +79,11 @@ int ia_css_tnr_config(struct sh_css_isp_tnr_isp_config *to, unsigned int i; int ret; - ret = ia_css_dma_configure_from_info(&to->port_b, &from->tnr_frames[0]->info); + ret = ia_css_dma_configure_from_info(&to->port_b, &from->tnr_frames[0]->frame_info); if (ret) return ret; to->width_a_over_b = elems_a / to->port_b.elems; - to->frame_height = from->tnr_frames[0]->info.res.height; + to->frame_height = from->tnr_frames[0]->frame_info.res.height; for (i = 0; i < NUM_VIDEO_TNR_FRAMES; i++) { to->tnr_frame_addr[i] = from->tnr_frames[i]->data + from->tnr_frames[i]->planes.yuyv.offset; diff --git a/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c b/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c index 406ed5fb4c6a..768da86b8c2c 100644 --- a/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c +++ b/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c @@ -13,6 +13,8 @@ * more details. */ +#include <linux/math.h> + #include <math_support.h> #include <gdc_device.h> /* HR_GDC_N */ @@ -128,16 +130,8 @@ ia_css_binary_compute_shading_table_bayer_origin( { int err; - /* Numerator and denominator of the fixed bayer downscaling factor. - (numerator >= denominator) */ - unsigned int bds_num, bds_den; - - /* Horizontal/Vertical ratio of bayer scaling - between input area and output area. */ - unsigned int bs_hor_ratio_in; - unsigned int bs_hor_ratio_out; - unsigned int bs_ver_ratio_in; - unsigned int bs_ver_ratio_out; + /* Rational fraction of the fixed bayer downscaling factor. */ + struct u32_fract bds; /* Left padding set by InputFormatter. */ unsigned int left_padding_bqs; /* in bqs */ @@ -158,19 +152,11 @@ ia_css_binary_compute_shading_table_bayer_origin( unsigned int bad_bqs_on_top_before_bs; /* in bqs */ unsigned int bad_bqs_on_top_after_bs; /* in bqs */ - /* Get the numerator and denominator of bayer downscaling factor. */ - err = sh_css_bds_factor_get_numerator_denominator - (required_bds_factor, &bds_num, &bds_den); + /* Get the rational fraction of bayer downscaling factor. */ + err = sh_css_bds_factor_get_fract(required_bds_factor, &bds); if (err) return err; - /* Set the horizontal/vertical ratio of bayer scaling - between input area and output area. */ - bs_hor_ratio_in = bds_num; - bs_hor_ratio_out = bds_den; - bs_ver_ratio_in = bds_num; - bs_ver_ratio_out = bds_den; - /* Set the left padding set by InputFormatter. (ifmtr.c) */ if (stream_config->left_padding == -1) left_padding_bqs = _ISP_BQS(binary->left_padding); @@ -228,18 +214,18 @@ ia_css_binary_compute_shading_table_bayer_origin( located on the shading table during the shading correction. */ res->sc_bayer_origin_x_bqs_on_shading_table = ((left_padding_adjusted_bqs + bad_bqs_on_left_before_bs) - * bs_hor_ratio_out + bs_hor_ratio_in / 2) / bs_hor_ratio_in + * bds.denominator + bds.numerator / 2) / bds.numerator + bad_bqs_on_left_after_bs; - /* "+ bs_hor_ratio_in/2": rounding for division by bs_hor_ratio_in */ + /* "+ bds.numerator / 2": rounding for division by bds.numerator */ res->sc_bayer_origin_y_bqs_on_shading_table = - (bad_bqs_on_top_before_bs * bs_ver_ratio_out + bs_ver_ratio_in / 2) / bs_ver_ratio_in + (bad_bqs_on_top_before_bs * bds.denominator + bds.numerator / 2) / bds.numerator + bad_bqs_on_top_after_bs; - /* "+ bs_ver_ratio_in/2": rounding for division by bs_ver_ratio_in */ + /* "+ bds.numerator / 2": rounding for division by bds.numerator */ - res->bayer_scale_hor_ratio_in = (uint32_t)bs_hor_ratio_in; - res->bayer_scale_hor_ratio_out = (uint32_t)bs_hor_ratio_out; - res->bayer_scale_ver_ratio_in = (uint32_t)bs_ver_ratio_in; - res->bayer_scale_ver_ratio_out = (uint32_t)bs_ver_ratio_out; + res->bayer_scale_hor_ratio_in = bds.numerator; + res->bayer_scale_hor_ratio_out = bds.denominator; + res->bayer_scale_ver_ratio_in = bds.numerator; + res->bayer_scale_ver_ratio_out = bds.denominator; return err; } diff --git a/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c b/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c index 3d269bd23207..bb6204cb42c5 100644 --- a/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c +++ b/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c @@ -133,7 +133,6 @@ static const char *const pipe_id_to_str[] = { /* [IA_CSS_PIPE_ID_VIDEO] =*/ "video", /* [IA_CSS_PIPE_ID_CAPTURE] =*/ "capture", /* [IA_CSS_PIPE_ID_YUVPP] =*/ "yuvpp", - /* [IA_CSS_PIPE_ID_ACC] =*/ "accelerator" }; static char dot_id_input_bin[SH_CSS_MAX_BINARY_NAME + 10]; @@ -1301,11 +1300,11 @@ void ia_css_debug_frame_print(const struct ia_css_frame *frame, data = (char *)HOST_ADDRESS(frame->data); ia_css_debug_dtrace(2, "frame %s (%p):\n", descr, frame); ia_css_debug_dtrace(2, " resolution = %dx%d\n", - frame->info.res.width, frame->info.res.height); + frame->frame_info.res.width, frame->frame_info.res.height); ia_css_debug_dtrace(2, " padded width = %d\n", - frame->info.padded_width); - ia_css_debug_dtrace(2, " format = %d\n", frame->info.format); - switch (frame->info.format) { + frame->frame_info.padded_width); + ia_css_debug_dtrace(2, " format = %d\n", frame->frame_info.format); + switch (frame->frame_info.format) { case IA_CSS_FRAME_FORMAT_NV12: case IA_CSS_FRAME_FORMAT_NV16: case IA_CSS_FRAME_FORMAT_NV21: @@ -2565,11 +2564,11 @@ ia_css_debug_pipe_graph_dump_frame( dtrace_dot( "node [shape = box, fixedsize=true, width=2, height=0.7]; \"%p\" [label = \"%s\\n%d(%d) x %d, %dbpp\\n%s\"];", frame, - debug_frame_format2str(frame->info.format), - frame->info.res.width, - frame->info.padded_width, - frame->info.res.height, - frame->info.raw_bit_depth, + debug_frame_format2str(frame->frame_info.format), + frame->frame_info.res.width, + frame->frame_info.padded_width, + frame->frame_info.res.height, + frame->frame_info.raw_bit_depth, bufinfo); if (in_frame) { @@ -2866,10 +2865,10 @@ ia_css_debug_pipe_graph_dump_sp_raw_copy( snprintf(ring_buffer, sizeof(ring_buffer), "node [shape = box, fixedsize=true, width=2, height=0.7]; \"%p\" [label = \"%s\\n%d(%d) x %d\\nRingbuffer\"];", out_frame, - debug_frame_format2str(out_frame->info.format), - out_frame->info.res.width, - out_frame->info.padded_width, - out_frame->info.res.height); + debug_frame_format2str(out_frame->frame_info.format), + out_frame->frame_info.res.width, + out_frame->frame_info.padded_width, + out_frame->frame_info.res.height); dtrace_dot(ring_buffer); @@ -2989,16 +2988,10 @@ ia_css_debug_dump_pipe_config( ia_css_debug_dump_frame_info(&config->vf_output_info[i], "vf_output_info"); } - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "acc_extension: %p\n", - config->acc_extension); - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "num_acc_stages: %d\n", - config->num_acc_stages); ia_css_debug_dump_capture_config(&config->default_capture_config); ia_css_debug_dump_resolution(&config->dvs_envelope, "dvs_envelope"); ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "dvs_frame_delay: %d\n", config->dvs_frame_delay); - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "acc_num_execs: %d\n", - config->acc_num_execs); ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "enable_dz: %d\n", config->enable_dz); IA_CSS_LEAVE_PRIVATE(""); diff --git a/drivers/staging/media/atomisp/pci/runtime/frame/src/frame.c b/drivers/staging/media/atomisp/pci/runtime/frame/src/frame.c index 5a7058320ee6..83bb42e05421 100644 --- a/drivers/staging/media/atomisp/pci/runtime/frame/src/frame.c +++ b/drivers/staging/media/atomisp/pci/runtime/frame/src/frame.c @@ -88,12 +88,6 @@ ia_css_elems_bytes_from_info( ** CSS API functions, exposed by ia_css.h **************************************************************************/ -void ia_css_frame_zero(struct ia_css_frame *frame) -{ - assert(frame); - hmm_set(frame->data, 0, frame->data_bytes); -} - int ia_css_frame_allocate_from_info(struct ia_css_frame **frame, const struct ia_css_frame_info *info) { @@ -143,121 +137,6 @@ int ia_css_frame_allocate(struct ia_css_frame **frame, return err; } -int ia_css_frame_map(struct ia_css_frame **frame, - const struct ia_css_frame_info *info, - const void __user *data, - unsigned int pgnr) -{ - int err = 0; - struct ia_css_frame *me; - - assert(frame); - - /* Create the frame structure */ - err = ia_css_frame_create_from_info(&me, info); - - if (err) - return err; - - if (pgnr < ((PAGE_ALIGN(me->data_bytes)) >> PAGE_SHIFT)) { - dev_err(atomisp_dev, - "user space memory size is less than the expected size..\n"); - err = -ENOMEM; - goto error; - } else if (pgnr > ((PAGE_ALIGN(me->data_bytes)) >> PAGE_SHIFT)) { - dev_err(atomisp_dev, - "user space memory size is large than the expected size..\n"); - err = -ENOMEM; - goto error; - } - - me->data = hmm_create_from_userdata(me->data_bytes, data); - if (me->data == mmgr_NULL) - err = -EINVAL; - -error: - if (err) { - kvfree(me); - me = NULL; - } - - *frame = me; - - return err; -} - -int ia_css_frame_create_from_info(struct ia_css_frame **frame, - const struct ia_css_frame_info *info) -{ - int err = 0; - struct ia_css_frame *me; - - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_frame_create_from_info() enter:\n"); - if (!frame || !info) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_frame_create_from_info() leave: invalid arguments\n"); - return -EINVAL; - } - - me = frame_create(info->res.width, - info->res.height, - info->format, - info->padded_width, - info->raw_bit_depth, - false); - if (!me) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_frame_create_from_info() leave: frame create failed\n"); - return -ENOMEM; - } - - err = ia_css_frame_init_planes(me); - - if (err) { - kvfree(me); - me = NULL; - } - - *frame = me; - - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_frame_create_from_info() leave:\n"); - - return err; -} - -int ia_css_frame_set_data(struct ia_css_frame *frame, - const ia_css_ptr mapped_data, - size_t data_bytes) -{ - int err = 0; - - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_frame_set_data() enter:\n"); - if (!frame) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_frame_set_data() leave: NULL frame\n"); - return -EINVAL; - } - - /* If we are setting a valid data. - * Make sure that there is enough - * room for the expected frame format - */ - if ((mapped_data != mmgr_NULL) && (frame->data_bytes > data_bytes)) { - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "ia_css_frame_set_data() leave: invalid arguments\n"); - return -EINVAL; - } - - frame->data = mapped_data; - - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "ia_css_frame_set_data() leave:\n"); - - return err; -} - void ia_css_frame_free(struct ia_css_frame *frame) { IA_CSS_ENTER_PRIVATE("frame = %p", frame); @@ -286,32 +165,32 @@ int ia_css_frame_init_planes(struct ia_css_frame *frame) { assert(frame); - switch (frame->info.format) { + switch (frame->frame_info.format) { case IA_CSS_FRAME_FORMAT_MIPI: dev_err(atomisp_dev, "%s: unexpected use of IA_CSS_FRAME_FORMAT_MIPI\n", __func__); return -EINVAL; case IA_CSS_FRAME_FORMAT_RAW_PACKED: frame_init_raw_single_plane(frame, &frame->planes.raw, - frame->info.res.height, - frame->info.padded_width, - frame->info.raw_bit_depth); + frame->frame_info.res.height, + frame->frame_info.padded_width, + frame->frame_info.raw_bit_depth); break; case IA_CSS_FRAME_FORMAT_RAW: frame_init_single_plane(frame, &frame->planes.raw, - frame->info.res.height, - frame->info.padded_width, - frame->info.raw_bit_depth <= 8 ? 1 : 2); + frame->frame_info.res.height, + frame->frame_info.padded_width, + frame->frame_info.raw_bit_depth <= 8 ? 1 : 2); break; case IA_CSS_FRAME_FORMAT_RGB565: frame_init_single_plane(frame, &frame->planes.rgb, - frame->info.res.height, - frame->info.padded_width, 2); + frame->frame_info.res.height, + frame->frame_info.padded_width, 2); break; case IA_CSS_FRAME_FORMAT_RGBA888: frame_init_single_plane(frame, &frame->planes.rgb, - frame->info.res.height, - frame->info.padded_width * 4, 1); + frame->frame_info.res.height, + frame->frame_info.padded_width * 4, 1); break; case IA_CSS_FRAME_FORMAT_PLANAR_RGB888: frame_init_rgb_planes(frame, 1); @@ -324,14 +203,14 @@ int ia_css_frame_init_planes(struct ia_css_frame *frame) case IA_CSS_FRAME_FORMAT_CSI_MIPI_YUV420_8: case IA_CSS_FRAME_FORMAT_CSI_MIPI_LEGACY_YUV420_8: frame_init_single_plane(frame, &frame->planes.yuyv, - frame->info.res.height, - frame->info.padded_width * 2, 1); + frame->frame_info.res.height, + frame->frame_info.padded_width * 2, 1); break; case IA_CSS_FRAME_FORMAT_YUV_LINE: /* Needs 3 extra lines to allow vf_pp prefetching */ frame_init_single_plane(frame, &frame->planes.yuyv, - frame->info.res.height * 3 / 2 + 3, - frame->info.padded_width, 1); + frame->frame_info.res.height * 3 / 2 + 3, + frame->frame_info.padded_width, 1); break; case IA_CSS_FRAME_FORMAT_NV11: frame_init_nv_planes(frame, 4, 1, 1); @@ -380,8 +259,8 @@ int ia_css_frame_init_planes(struct ia_css_frame *frame) break; case IA_CSS_FRAME_FORMAT_BINARY_8: frame_init_single_plane(frame, &frame->planes.binary.data, - frame->info.res.height, - frame->info.padded_width, 1); + frame->frame_info.res.height, + frame->frame_info.padded_width, 1); frame->planes.binary.size = 0; break; default: @@ -510,8 +389,8 @@ bool ia_css_frame_is_same_type(const struct ia_css_frame *frame_a, const struct ia_css_frame *frame_b) { bool is_equal = false; - const struct ia_css_frame_info *info_a = &frame_a->info, - *info_b = &frame_b->info; + const struct ia_css_frame_info *info_a = &frame_a->frame_info; + const struct ia_css_frame_info *info_b = &frame_b->frame_info; ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "ia_css_frame_is_same_type() enter:\n"); @@ -613,8 +492,8 @@ static void frame_init_nv_planes(struct ia_css_frame *frame, unsigned int vertical_decimation, unsigned int bytes_per_element) { - unsigned int y_width = frame->info.padded_width; - unsigned int y_height = frame->info.res.height; + unsigned int y_width = frame->frame_info.padded_width; + unsigned int y_height = frame->frame_info.res.height; unsigned int uv_width; unsigned int uv_height; unsigned int y_bytes; @@ -627,7 +506,7 @@ static void frame_init_nv_planes(struct ia_css_frame *frame, uv_width = 2 * (y_width / horizontal_decimation); uv_height = y_height / vertical_decimation; - if (frame->info.format == IA_CSS_FRAME_FORMAT_NV12_TILEY) { + if (frame->frame_info.format == IA_CSS_FRAME_FORMAT_NV12_TILEY) { y_width = CEIL_MUL(y_width, NV12_TILEY_TILE_WIDTH); uv_width = CEIL_MUL(uv_width, NV12_TILEY_TILE_WIDTH); y_height = CEIL_MUL(y_height, NV12_TILEY_TILE_HEIGHT); @@ -652,8 +531,8 @@ static void frame_init_yuv_planes(struct ia_css_frame *frame, bool swap_uv, unsigned int bytes_per_element) { - unsigned int y_width = frame->info.padded_width, - y_height = frame->info.res.height, + unsigned int y_width = frame->frame_info.padded_width, + y_height = frame->frame_info.res.height, uv_width = y_width / horizontal_decimation, uv_height = y_height / vertical_decimation, y_stride, y_bytes, uv_bytes, uv_stride; @@ -682,8 +561,8 @@ static void frame_init_yuv_planes(struct ia_css_frame *frame, static void frame_init_rgb_planes(struct ia_css_frame *frame, unsigned int bytes_per_element) { - unsigned int width = frame->info.res.width, - height = frame->info.res.height, stride, bytes; + unsigned int width = frame->frame_info.res.width, + height = frame->frame_info.res.height, stride, bytes; stride = width * bytes_per_element; bytes = stride * height; @@ -698,8 +577,8 @@ static void frame_init_rgb_planes(struct ia_css_frame *frame, static void frame_init_qplane6_planes(struct ia_css_frame *frame) { - unsigned int width = frame->info.padded_width / 2, - height = frame->info.res.height / 2, bytes, stride; + unsigned int width = frame->frame_info.padded_width / 2, + height = frame->frame_info.res.height / 2, bytes, stride; stride = width * 2; bytes = stride * height; @@ -781,11 +660,11 @@ static struct ia_css_frame *frame_create(unsigned int width, return NULL; memset(me, 0, sizeof(*me)); - me->info.res.width = width; - me->info.res.height = height; - me->info.format = format; - me->info.padded_width = padded_width; - me->info.raw_bit_depth = raw_bit_depth; + me->frame_info.res.width = width; + me->frame_info.res.height = height; + me->frame_info.format = format; + me->frame_info.padded_width = padded_width; + me->frame_info.raw_bit_depth = raw_bit_depth; me->valid = valid; me->data_bytes = 0; me->data = mmgr_NULL; @@ -847,3 +726,19 @@ void ia_css_resolution_to_sp_resolution( to->width = (uint16_t)from->width; to->height = (uint16_t)from->height; } + +int ia_css_frame_init_from_info(struct ia_css_frame *frame, + const struct ia_css_frame_info *frame_info) +{ + frame->frame_info.res.width = frame_info->res.width; + frame->frame_info.res.height = frame_info->res.height; + frame->frame_info.format = frame_info->format; + frame->frame_info.padded_width = frame_info->padded_width; + frame->frame_info.raw_bit_depth = frame_info->raw_bit_depth; + frame->valid = true; + /* To indicate it is not valid frame. */ + frame->dynamic_queue_id = SH_CSS_INVALID_QUEUE_ID; + frame->buf_type = IA_CSS_BUFFER_TYPE_INVALID; + + return ia_css_frame_init_planes(frame); +} diff --git a/drivers/staging/media/atomisp/pci/runtime/pipeline/interface/ia_css_pipeline.h b/drivers/staging/media/atomisp/pci/runtime/pipeline/interface/ia_css_pipeline.h index de2c526a58ae..222c381ff3b9 100644 --- a/drivers/staging/media/atomisp/pci/runtime/pipeline/interface/ia_css_pipeline.h +++ b/drivers/staging/media/atomisp/pci/runtime/pipeline/interface/ia_css_pipeline.h @@ -54,7 +54,6 @@ struct ia_css_pipeline { unsigned int inout_port_config; int num_execs; bool acquire_isp_each_stage; - u32 pipe_qos_config; }; #define DEFAULT_PIPELINE { \ @@ -65,7 +64,6 @@ struct ia_css_pipeline { .dvs_frame_delay = IA_CSS_FRAME_DELAY_1, \ .num_execs = -1, \ .acquire_isp_each_stage = true, \ - .pipe_qos_config = QOS_INVALID \ } /* Stage descriptor used to create a new stage in the pipeline */ diff --git a/drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c b/drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c index dfc50247ea8e..e9e187649a65 100644 --- a/drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c +++ b/drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c @@ -774,14 +774,6 @@ ia_css_pipeline_configure_inout_port(struct ia_css_pipeline *me, (uint8_t)SH_CSS_PORT_OUTPUT, (uint8_t)SH_CSS_HOST_TYPE, 1); break; - case IA_CSS_PIPE_ID_ACC: - SH_CSS_PIPE_PORT_CONFIG_SET(me->inout_port_config, - (uint8_t)SH_CSS_PORT_INPUT, - (uint8_t)SH_CSS_HOST_TYPE, 1); - SH_CSS_PIPE_PORT_CONFIG_SET(me->inout_port_config, - (uint8_t)SH_CSS_PORT_OUTPUT, - (uint8_t)SH_CSS_HOST_TYPE, 1); - break; default: break; } diff --git a/drivers/staging/media/atomisp/pci/sh_css.c b/drivers/staging/media/atomisp/pci/sh_css.c index da96aaffebc1..726cb7aa4ecd 100644 --- a/drivers/staging/media/atomisp/pci/sh_css.c +++ b/drivers/staging/media/atomisp/pci/sh_css.c @@ -209,13 +209,6 @@ ia_css_pipe_check_format(struct ia_css_pipe *pipe, enum ia_css_frame_format format); /* ISP 2401 */ -static int -ia_css_pipe_load_extension(struct ia_css_pipe *pipe, - struct ia_css_fw_info *firmware); - -static void -ia_css_pipe_unload_extension(struct ia_css_pipe *pipe, - struct ia_css_fw_info *firmware); static void ia_css_reset_defaults(struct sh_css *css); @@ -287,10 +280,6 @@ init_out_frameinfo_defaults(struct ia_css_pipe *pipe, struct ia_css_frame *out_frame, unsigned int idx); static int -sh_css_pipeline_add_acc_stage(struct ia_css_pipeline *pipeline, - const void *acc_fw); - -static int alloc_continuous_frames(struct ia_css_pipe *pipe, bool init_time); static void @@ -329,9 +318,6 @@ create_host_capture_pipeline(struct ia_css_pipe *pipe); static int create_host_yuvpp_pipeline(struct ia_css_pipe *pipe); -static int -create_host_acc_pipeline(struct ia_css_pipe *pipe); - static unsigned int sh_css_get_sw_interrupt_value(unsigned int irq); @@ -362,12 +348,6 @@ static struct sh_css_hmm_buffer_record *sh_css_hmm_buffer_record_validate(ia_css_ptr ddr_buffer_addr, enum ia_css_buffer_type type); -void -ia_css_get_acc_configs( - struct ia_css_pipe *pipe, - struct ia_css_isp_config *config); - - #ifdef ISP2401 static unsigned int get_crop_lines_for_bayer_order(const struct ia_css_stream_config *config); @@ -1649,15 +1629,6 @@ ia_css_enable_isys_event_queue(bool enable) return 0; } -/* For Acceleration API: Flush FW (shared buffer pointer) arguments */ -void -sh_css_flush(struct ia_css_acc_fw *fw) -{ - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "sh_css_flush() enter:\n"); - if ((fw) && (my_css.flush)) - my_css.flush(fw); -} - /* * Mapping sp threads. Currently, this is done when a stream is created and * pipelines are ready to be converted to sp pipelines. Be careful if you are @@ -1670,7 +1641,6 @@ map_sp_threads(struct ia_css_stream *stream, bool map) struct ia_css_pipe *main_pipe = NULL; struct ia_css_pipe *copy_pipe = NULL; struct ia_css_pipe *capture_pipe = NULL; - struct ia_css_pipe *acc_pipe = NULL; int err = 0; enum ia_css_pipe_id pipe_id; @@ -1691,7 +1661,6 @@ map_sp_threads(struct ia_css_stream *stream, bool map) case IA_CSS_PIPE_ID_PREVIEW: copy_pipe = main_pipe->pipe_settings.preview.copy_pipe; capture_pipe = main_pipe->pipe_settings.preview.capture_pipe; - acc_pipe = main_pipe->pipe_settings.preview.acc_pipe; break; case IA_CSS_PIPE_ID_VIDEO: @@ -1700,14 +1669,10 @@ map_sp_threads(struct ia_css_stream *stream, bool map) break; case IA_CSS_PIPE_ID_CAPTURE: - case IA_CSS_PIPE_ID_ACC: default: break; } - if (acc_pipe) - ia_css_pipeline_map(acc_pipe->pipe_num, map); - if (capture_pipe) ia_css_pipeline_map(capture_pipe->pipe_num, map); @@ -1735,7 +1700,6 @@ static int create_host_pipeline_structure(struct ia_css_stream *stream) { struct ia_css_pipe *copy_pipe = NULL, *capture_pipe = NULL; - struct ia_css_pipe *acc_pipe = NULL; enum ia_css_pipe_id pipe_id; struct ia_css_pipe *main_pipe = NULL; int err = 0; @@ -1763,7 +1727,6 @@ create_host_pipeline_structure(struct ia_css_stream *stream) copy_pipe_delay = main_pipe->dvs_frame_delay; capture_pipe = main_pipe->pipe_settings.preview.capture_pipe; capture_pipe_delay = IA_CSS_FRAME_DELAY_0; - acc_pipe = main_pipe->pipe_settings.preview.acc_pipe; err = ia_css_pipeline_create(&main_pipe->pipeline, main_pipe->mode, main_pipe->pipe_num, main_pipe->dvs_frame_delay); break; @@ -1787,11 +1750,6 @@ create_host_pipeline_structure(struct ia_css_stream *stream) main_pipe->pipe_num, main_pipe->dvs_frame_delay); break; - case IA_CSS_PIPE_ID_ACC: - err = ia_css_pipeline_create(&main_pipe->pipeline, main_pipe->mode, - main_pipe->pipe_num, main_pipe->dvs_frame_delay); - break; - default: err = -EINVAL; } @@ -1808,10 +1766,6 @@ create_host_pipeline_structure(struct ia_css_stream *stream) capture_pipe->pipe_num, capture_pipe_delay); - if (!(err) && acc_pipe) - err = ia_css_pipeline_create(&acc_pipe->pipeline, acc_pipe->mode, - acc_pipe->pipe_num, main_pipe->dvs_frame_delay); - /* DH regular multi pipe - not continuous mode: create the next pipelines too */ if (!stream->config.continuous) { int i; @@ -1837,7 +1791,6 @@ static int create_host_pipeline(struct ia_css_stream *stream) { struct ia_css_pipe *copy_pipe = NULL, *capture_pipe = NULL; - struct ia_css_pipe *acc_pipe = NULL; enum ia_css_pipe_id pipe_id; struct ia_css_pipe *main_pipe = NULL; int err = 0; @@ -1881,27 +1834,17 @@ create_host_pipeline(struct ia_css_stream *stream) } } -#if !defined(ISP2401) /* old isys: need to allocate_mipi_frames() even in IA_CSS_PIPE_MODE_COPY */ - if (pipe_id != IA_CSS_PIPE_ID_ACC) { - err = allocate_mipi_frames(main_pipe, &stream->info); - if (err) - goto ERR; - } -#elif defined(ISP2401) - if ((pipe_id != IA_CSS_PIPE_ID_ACC) && - (main_pipe->config.mode != IA_CSS_PIPE_MODE_COPY)) { + if (!IS_ISP2401 || main_pipe->config.mode != IA_CSS_PIPE_MODE_COPY) { err = allocate_mipi_frames(main_pipe, &stream->info); if (err) goto ERR; } -#endif switch (pipe_id) { case IA_CSS_PIPE_ID_PREVIEW: copy_pipe = main_pipe->pipe_settings.preview.copy_pipe; capture_pipe = main_pipe->pipe_settings.preview.capture_pipe; - acc_pipe = main_pipe->pipe_settings.preview.acc_pipe; max_input_width = main_pipe->pipe_settings.preview.preview_binary.info->sp.input.max_width; @@ -1935,12 +1878,6 @@ create_host_pipeline(struct ia_css_stream *stream) break; - case IA_CSS_PIPE_ID_ACC: - err = create_host_acc_pipeline(main_pipe); - if (err) - goto ERR; - - break; default: err = -EINVAL; } @@ -1960,12 +1897,6 @@ create_host_pipeline(struct ia_css_stream *stream) goto ERR; } - if (acc_pipe) { - err = create_host_acc_pipeline(acc_pipe); - if (err) - goto ERR; - } - /* DH regular multi pipe - not continuous mode: create the next pipelines too */ if (!stream->config.continuous) { int i; @@ -1984,9 +1915,6 @@ create_host_pipeline(struct ia_css_stream *stream) case IA_CSS_PIPE_ID_YUVPP: err = create_host_yuvpp_pipeline(stream->pipes[i]); break; - case IA_CSS_PIPE_ID_ACC: - err = create_host_acc_pipeline(stream->pipes[i]); - break; default: err = -EINVAL; } @@ -2037,9 +1965,6 @@ init_pipe_defaults(enum ia_css_pipe_mode mode, pipe->mode = IA_CSS_PIPE_ID_VIDEO; memcpy(&pipe->pipe_settings.video, &video, sizeof(video)); break; - case IA_CSS_PIPE_MODE_ACC: - pipe->mode = IA_CSS_PIPE_ID_ACC; - break; case IA_CSS_PIPE_MODE_COPY: pipe->mode = IA_CSS_PIPE_ID_CAPTURE; break; @@ -2156,27 +2081,6 @@ find_pipe_by_num(uint32_t pipe_num) return NULL; } -static void sh_css_pipe_free_acc_binaries( - struct ia_css_pipe *pipe) -{ - struct ia_css_pipeline *pipeline; - struct ia_css_pipeline_stage *stage; - - if (!pipe) { - IA_CSS_ERROR("NULL input pointer"); - return; - } - pipeline = &pipe->pipeline; - - /* loop through the stages and unload them */ - for (stage = pipeline->stages; stage; stage = stage->next) { - struct ia_css_fw_info *firmware = (struct ia_css_fw_info *) - stage->firmware; - if (firmware) - ia_css_pipe_unload_extension(pipe, firmware); - } -} - int ia_css_pipe_destroy(struct ia_css_pipe *pipe) { @@ -2241,9 +2145,6 @@ ia_css_pipe_destroy(struct ia_css_pipe *pipe) ia_css_frame_free_multiple(MAX_NUM_VIDEO_DELAY_FRAMES, pipe->pipe_settings.capture.delay_frames); break; - case IA_CSS_PIPE_MODE_ACC: - sh_css_pipe_free_acc_binaries(pipe); - break; case IA_CSS_PIPE_MODE_COPY: break; case IA_CSS_PIPE_MODE_YUVPP: @@ -2261,10 +2162,6 @@ ia_css_pipe_destroy(struct ia_css_pipe *pipe) ia_css_pipeline_destroy(&pipe->pipeline); pipe_release_pipe_num(ia_css_pipe_get_pipe_num(pipe)); - /* Temporarily, not every sh_css_pipe has an acc_extension. */ - if (pipe->config.acc_extension) - ia_css_pipe_unload_extension(pipe, pipe->config.acc_extension); - kfree(pipe); IA_CSS_LEAVE("err = %d", err); return err; @@ -3060,7 +2957,7 @@ init_vf_frameinfo_defaults(struct ia_css_pipe *pipe, assert(vf_frame); - sh_css_pipe_get_viewfinder_frame_info(pipe, &vf_frame->info, idx); + sh_css_pipe_get_viewfinder_frame_info(pipe, &vf_frame->frame_info, idx); vf_frame->flash_state = IA_CSS_FRAME_FLASH_STATE_NONE; ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(pipe), &thread_id); ia_css_query_internal_queue_id(IA_CSS_BUFFER_TYPE_VF_OUTPUT_FRAME + idx, thread_id, &queue_id); @@ -3229,31 +3126,31 @@ init_in_frameinfo_memory_defaults(struct ia_css_pipe *pipe, assert(frame); in_frame = frame; - in_frame->info.format = format; + in_frame->frame_info.format = format; #ifdef ISP2401 if (format == IA_CSS_FRAME_FORMAT_RAW) - in_frame->info.format = (pipe->stream->config.pack_raw_pixels) ? + in_frame->frame_info.format = (pipe->stream->config.pack_raw_pixels) ? IA_CSS_FRAME_FORMAT_RAW_PACKED : IA_CSS_FRAME_FORMAT_RAW; #endif - in_frame->info.res.width = pipe->stream->config.input_config.input_res.width; - in_frame->info.res.height = pipe->stream->config.input_config.input_res.height; - in_frame->info.raw_bit_depth = - ia_css_pipe_util_pipe_input_format_bpp(pipe); - ia_css_frame_info_set_width(&in_frame->info, pipe->stream->config.input_config.input_res.width, 0); + in_frame->frame_info.res.width = pipe->stream->config.input_config.input_res.width; + in_frame->frame_info.res.height = pipe->stream->config.input_config.input_res.height; + in_frame->frame_info.raw_bit_depth = ia_css_pipe_util_pipe_input_format_bpp(pipe); + ia_css_frame_info_set_width(&in_frame->frame_info, + pipe->stream->config.input_config.input_res.width, 0); in_frame->flash_state = IA_CSS_FRAME_FLASH_STATE_NONE; ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(pipe), &thread_id); ia_css_query_internal_queue_id(IA_CSS_BUFFER_TYPE_INPUT_FRAME, thread_id, &queue_id); in_frame->dynamic_queue_id = queue_id; in_frame->buf_type = IA_CSS_BUFFER_TYPE_INPUT_FRAME; #ifdef ISP2401 - ia_css_get_crop_offsets(pipe, &in_frame->info); + ia_css_get_crop_offsets(pipe, &in_frame->frame_info); #endif err = ia_css_frame_init_planes(in_frame); - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, - "init_in_frameinfo_memory_defaults() bayer_order = %d:\n", in_frame->info.raw_bayer_order); + ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, "%s() bayer_order = %d\n", + __func__, in_frame->frame_info.raw_bayer_order); return err; } @@ -3268,7 +3165,7 @@ init_out_frameinfo_defaults(struct ia_css_pipe *pipe, assert(out_frame); - sh_css_pipe_get_output_frame_info(pipe, &out_frame->info, idx); + sh_css_pipe_get_output_frame_info(pipe, &out_frame->frame_info, idx); out_frame->flash_state = IA_CSS_FRAME_FLASH_STATE_NONE; ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(pipe), &thread_id); ia_css_query_internal_queue_id(IA_CSS_BUFFER_TYPE_OUTPUT_FRAME + idx, thread_id, &queue_id); @@ -3433,31 +3330,6 @@ static int create_host_video_pipeline(struct ia_css_pipe *pipe) } } - /* Append Extension on Video out, if enabled */ - if (!need_vf_pp && video_stage && pipe->config.acc_extension && - (pipe->config.acc_extension->info.isp.type == IA_CSS_ACC_OUTPUT)) { - struct ia_css_frame *out = NULL; - struct ia_css_frame *in = NULL; - - if ((pipe->config.acc_extension->info.isp.sp.enable.output) && - (pipe->config.acc_extension->info.isp.sp.enable.in_frame) && - (pipe->config.acc_extension->info.isp.sp.enable.out_frame)) { - /* In/Out Frame mapping to support output frame extension.*/ - out = video_stage->args.out_frame[0]; - err = ia_css_frame_allocate_from_info(&in, &pipe->output_info[0]); - if (err) - goto ERR; - video_stage->args.out_frame[0] = in; - } - - err = add_firmwares(me, video_binary, pipe->output_stage, - last_output_firmware(pipe->output_stage), - IA_CSS_BINARY_MODE_VIDEO, - in, out, NULL, &video_stage, NULL); - if (err) - goto ERR; - } - if (need_yuv_pp && video_stage) { struct ia_css_frame *tmp_in_frame = video_stage->args.out_frame[0]; struct ia_css_frame *tmp_out_frame = NULL; @@ -3489,45 +3361,6 @@ ERR: return err; } -static int -create_host_acc_pipeline(struct ia_css_pipe *pipe) -{ - int err = 0; - const struct ia_css_fw_info *fw; - unsigned int i; - - IA_CSS_ENTER_PRIVATE("pipe = %p", pipe); - if ((!pipe) || (!pipe->stream)) { - IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL); - return -EINVAL; - } - - pipe->pipeline.num_execs = pipe->config.acc_num_execs; - /* Reset pipe_qos_config to default disable all QOS extension stages */ - if (pipe->config.acc_extension) - pipe->pipeline.pipe_qos_config = 0; - - for (fw = pipe->vf_stage; fw; fw = fw->next) { - err = sh_css_pipeline_add_acc_stage(&pipe->pipeline, fw); - if (err) - goto ERR; - } - - for (i = 0; i < pipe->config.num_acc_stages; i++) { - struct ia_css_fw_info *fw = pipe->config.acc_stages[i]; - - err = sh_css_pipeline_add_acc_stage(&pipe->pipeline, fw); - if (err) - goto ERR; - } - - ia_css_pipeline_finalize_stages(&pipe->pipeline, pipe->stream->config.continuous); - -ERR: - IA_CSS_LEAVE_ERR_PRIVATE(err); - return err; -} - /* Create stages for preview */ static int create_host_preview_pipeline(struct ia_css_pipe *pipe) @@ -3690,7 +3523,6 @@ preview_start(struct ia_css_pipe *pipe) { int err = 0; struct ia_css_pipe *copy_pipe, *capture_pipe; - struct ia_css_pipe *acc_pipe; enum sh_css_pipe_config_override copy_ovrd; enum ia_css_input_mode preview_pipe_input_mode; unsigned int thread_id; @@ -3705,7 +3537,6 @@ preview_start(struct ia_css_pipe *pipe) copy_pipe = pipe->pipe_settings.preview.copy_pipe; capture_pipe = pipe->pipe_settings.preview.capture_pipe; - acc_pipe = pipe->pipe_settings.preview.acc_pipe; sh_css_metrics_start_frame(); @@ -3764,22 +3595,6 @@ preview_start(struct ia_css_pipe *pipe) (enum mipi_port_id)0); } - if (acc_pipe) { - sh_css_sp_init_pipeline(&acc_pipe->pipeline, - IA_CSS_PIPE_ID_ACC, - (uint8_t)ia_css_pipe_get_pipe_num(acc_pipe), - false, - pipe->stream->config.pixels_per_clock == 2, - false, /* continuous */ - false, /* offline */ - pipe->required_bds_factor, - 0, - IA_CSS_INPUT_MODE_MEMORY, - NULL, - NULL, - (enum mipi_port_id)0); - } - start_pipe(pipe, copy_ovrd, preview_pipe_input_mode); IA_CSS_LEAVE_ERR_PRIVATE(err); @@ -3850,9 +3665,7 @@ ia_css_pipe_enqueue_buffer(struct ia_css_pipe *pipe, pipeline = &pipe->pipeline; - assert(pipeline || - pipe_id == IA_CSS_PIPE_ID_COPY || - pipe_id == IA_CSS_PIPE_ID_ACC); + assert(pipeline || pipe_id == IA_CSS_PIPE_ID_COPY); assert(sizeof(NULL) <= sizeof(ddr_buffer.kernel_ptr)); ddr_buffer.kernel_ptr = HOST_ADDRESS(NULL); @@ -4146,7 +3959,7 @@ ia_css_pipe_dequeue_buffer(struct ia_css_pipe *pipe, if (!frame->valid) pipe->num_invalid_frames--; - if (frame->info.format == IA_CSS_FRAME_FORMAT_BINARY_8) { + if (frame->frame_info.format == IA_CSS_FRAME_FORMAT_BINARY_8) { #ifdef ISP2401 frame->planes.binary.size = frame->data_bytes; #else @@ -4442,16 +4255,6 @@ ia_css_dequeue_isys_event(struct ia_css_event *event) return err; } -static void -acc_start(struct ia_css_pipe *pipe) -{ - assert(pipe); - assert(pipe->stream); - - start_pipe(pipe, SH_CSS_PIPE_CONFIG_OVRD_NO_OVRD, - pipe->stream->config.mode); -} - static int sh_css_pipe_start(struct ia_css_stream *stream) { @@ -4496,9 +4299,6 @@ sh_css_pipe_start(struct ia_css_stream *stream) case IA_CSS_PIPE_ID_YUVPP: err = yuvpp_start(pipe); break; - case IA_CSS_PIPE_ID_ACC: - acc_start(pipe); - break; default: err = -EINVAL; } @@ -4524,10 +4324,6 @@ sh_css_pipe_start(struct ia_css_stream *stream) stream->pipes[i]->stop_requested = false; err = yuvpp_start(stream->pipes[i]); break; - case IA_CSS_PIPE_ID_ACC: - stream->pipes[i]->stop_requested = false; - acc_start(stream->pipes[i]); - break; default: err = -EINVAL; } @@ -4620,22 +4416,6 @@ sh_css_pipe_start(struct ia_css_stream *stream) (uint8_t)thread_id, 0, 0); } - /* in case of PREVIEW mode, check whether QOS acc_pipe is available, then start the qos pipe */ - if (pipe_id == IA_CSS_PIPE_ID_PREVIEW) { - struct ia_css_pipe *acc_pipe = NULL; - - acc_pipe = pipe->pipe_settings.preview.acc_pipe; - - if (acc_pipe) { - ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(acc_pipe), - &thread_id); - /* by the time we reach here q is initialized and handle is available.*/ - ia_css_bufq_enqueue_psys_event( - IA_CSS_PSYS_SW_EVENT_START_STREAM, - (uint8_t)thread_id, 0, 0); - } - } - stream->started = true; IA_CSS_LEAVE_ERR_PRIVATE(err); @@ -6861,8 +6641,6 @@ sh_css_pipe_load_binaries(struct ia_css_pipe *pipe) case IA_CSS_PIPE_ID_YUVPP: err = load_yuvpp_binaries(pipe); break; - case IA_CSS_PIPE_ID_ACC: - break; default: err = -EINVAL; break; @@ -7102,7 +6880,7 @@ create_host_yuvpp_pipeline(struct ia_css_pipe *pipe) /* we use output port 1 as internal output port */ tmp_in_frame = yuv_scaler_stage->args.out_frame[1]; if (pipe->pipe_settings.yuvpp.is_output_stage[i]) { - if (tmp_vf_frame && (tmp_vf_frame->info.res.width != 0)) { + if (tmp_vf_frame && (tmp_vf_frame->frame_info.res.width != 0)) { in_frame = yuv_scaler_stage->args.out_vf_frame; err = add_vf_pp_stage(pipe, in_frame, tmp_vf_frame, @@ -7118,7 +6896,7 @@ create_host_yuvpp_pipeline(struct ia_css_pipe *pipe) } } } else if (copy_stage) { - if (vf_frame[0] && vf_frame[0]->info.res.width != 0) { + if (vf_frame[0] && vf_frame[0]->frame_info.res.width != 0) { in_frame = copy_stage->args.out_vf_frame; err = add_vf_pp_stage(pipe, in_frame, vf_frame[0], &vf_pp_binary[0], &vf_pp_stage); @@ -7158,10 +6936,10 @@ create_host_copy_pipeline(struct ia_css_pipe *pipe, if (copy_on_sp(pipe) && pipe->stream->config.input_config.format == ATOMISP_INPUT_FORMAT_BINARY_8) { - ia_css_frame_info_init(&out_frame->info, JPEG_BYTES, 1, + ia_css_frame_info_init(&out_frame->frame_info, JPEG_BYTES, 1, IA_CSS_FRAME_FORMAT_BINARY_8, 0); - } else if (out_frame->info.format == IA_CSS_FRAME_FORMAT_RAW) { - out_frame->info.raw_bit_depth = + } else if (out_frame->frame_info.format == IA_CSS_FRAME_FORMAT_RAW) { + out_frame->frame_info.raw_bit_depth = ia_css_pipe_util_pipe_input_format_bpp(pipe); } @@ -7200,7 +6978,7 @@ create_host_isyscopy_capture_pipeline(struct ia_css_pipe *pipe) ia_css_pipeline_clean(me); /* Construct out_frame info */ - err = sh_css_pipe_get_output_frame_info(pipe, &out_frame->info, 0); + err = sh_css_pipe_get_output_frame_info(pipe, &out_frame->frame_info, 0); if (err) return err; out_frame->flash_state = IA_CSS_FRAME_FLASH_STATE_NONE; @@ -7755,154 +7533,6 @@ ia_css_stream_end_input_frame(const struct ia_css_stream *stream) ia_css_inputfifo_end_frame(stream->config.channel_id); } -static void -append_firmware(struct ia_css_fw_info **l, struct ia_css_fw_info *firmware) -{ - IA_CSS_ENTER_PRIVATE("l = %p, firmware = %p", l, firmware); - if (!l) { - IA_CSS_ERROR("NULL fw_info"); - IA_CSS_LEAVE_PRIVATE(""); - return; - } - while (*l) - l = &(*l)->next; - *l = firmware; - /* when multiple acc extensions are loaded, 'next' can be not NULL */ - /*firmware->next = NULL;*/ - IA_CSS_LEAVE_PRIVATE(""); -} - -static void -remove_firmware(struct ia_css_fw_info **l, struct ia_css_fw_info *firmware) -{ - assert(*l); - assert(firmware); - (void)l; - (void)firmware; - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, "remove_firmware() enter:\n"); - - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE, "remove_firmware() leave:\n"); - return; /* removing single and multiple firmware is handled in acc_unload_extension() */ -} - -static int upload_isp_code(struct ia_css_fw_info *firmware) -{ - ia_css_ptr binary; - - if (!firmware) { - IA_CSS_ERROR("NULL input parameter"); - return -EINVAL; - } - binary = firmware->info.isp.xmem_addr; - - if (!binary) { - unsigned int size = firmware->blob.size; - const unsigned char *blob; - const unsigned char *binary_name; - - binary_name = - (const unsigned char *)(IA_CSS_EXT_ISP_PROG_NAME( - firmware)); - blob = binary_name + - strlen((const char *)binary_name) + - 1; - binary = sh_css_load_blob(blob, size); - firmware->info.isp.xmem_addr = binary; - } - - if (!binary) - return -ENOMEM; - return 0; -} - -static int -acc_load_extension(struct ia_css_fw_info *firmware) -{ - int err; - struct ia_css_fw_info *hd = firmware; - - while (hd) { - err = upload_isp_code(hd); - if (err) - return err; - hd = hd->next; - } - - if (!firmware) - return -EINVAL; - firmware->loaded = true; - return 0; -} - -static void -acc_unload_extension(struct ia_css_fw_info *firmware) -{ - struct ia_css_fw_info *hd = firmware; - struct ia_css_fw_info *hdn = NULL; - - if (!firmware) /* should not happen */ - return; - /* unload and remove multiple firmwares */ - while (hd) { - hdn = (hd->next) ? &(*hd->next) : NULL; - if (hd->info.isp.xmem_addr) { - hmm_free(hd->info.isp.xmem_addr); - hd->info.isp.xmem_addr = mmgr_NULL; - } - hd->isp_code = NULL; - hd->next = NULL; - hd = hdn; - } - - firmware->loaded = false; -} - -/* Load firmware for extension */ -static int -ia_css_pipe_load_extension(struct ia_css_pipe *pipe, - struct ia_css_fw_info *firmware) -{ - int err = 0; - - IA_CSS_ENTER_PRIVATE("fw = %p pipe = %p", firmware, pipe); - - if ((!firmware) || (!pipe)) { - IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL); - return -EINVAL; - } - - if (firmware->info.isp.type == IA_CSS_ACC_OUTPUT) - append_firmware(&pipe->output_stage, firmware); - else if (firmware->info.isp.type == IA_CSS_ACC_VIEWFINDER) - append_firmware(&pipe->vf_stage, firmware); - err = acc_load_extension(firmware); - - IA_CSS_LEAVE_ERR_PRIVATE(err); - return err; -} - -/* Unload firmware for extension */ -static void -ia_css_pipe_unload_extension(struct ia_css_pipe *pipe, - struct ia_css_fw_info *firmware) -{ - IA_CSS_ENTER_PRIVATE("fw = %p pipe = %p", firmware, pipe); - - if ((!firmware) || (!pipe)) { - IA_CSS_ERROR("NULL input parameters"); - IA_CSS_LEAVE_PRIVATE(""); - return; - } - - if (firmware->info.isp.type == IA_CSS_ACC_OUTPUT) - remove_firmware(&pipe->output_stage, firmware); - else if (firmware->info.isp.type == IA_CSS_ACC_VIEWFINDER) - remove_firmware(&pipe->vf_stage, firmware); - acc_unload_extension(firmware); - - IA_CSS_LEAVE_PRIVATE(""); -} - bool ia_css_pipeline_uses_params(struct ia_css_pipeline *me) { @@ -7924,35 +7554,6 @@ ia_css_pipeline_uses_params(struct ia_css_pipeline *me) return false; } -static int -sh_css_pipeline_add_acc_stage(struct ia_css_pipeline *pipeline, - const void *acc_fw) -{ - struct ia_css_fw_info *fw = (struct ia_css_fw_info *)acc_fw; - /* In QoS case, load_extension already called, so skipping */ - int err = 0; - - if (!fw->loaded) - err = acc_load_extension(fw); - - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "sh_css_pipeline_add_acc_stage() enter: pipeline=%p, acc_fw=%p\n", - pipeline, acc_fw); - - if (!err) { - struct ia_css_pipeline_stage_desc stage_desc; - - ia_css_pipe_get_acc_stage_desc(&stage_desc, NULL, fw); - err = ia_css_pipeline_create_and_add_stage(pipeline, - &stage_desc, - NULL); - } - - ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, - "sh_css_pipeline_add_acc_stage() leave: return_err=%d\n", err); - return err; -} - /* * @brief Tag a specific frame in continuous capture. * Refer to "sh_css_internal.h" for details. @@ -8177,26 +7778,6 @@ void ia_css_stream_config_defaults(struct ia_css_stream_config *stream_config) stream_config->source.port.rxcount = 0x04040404; } -static int -ia_css_acc_pipe_create(struct ia_css_pipe *pipe) -{ - int err = 0; - - if (!pipe) { - IA_CSS_ERROR("NULL input parameter"); - return -EINVAL; - } - - /* There is not meaning for num_execs = 0 semantically. Run at least once. */ - if (pipe->config.acc_num_execs == 0) - pipe->config.acc_num_execs = 1; - - if (pipe->config.acc_extension) - err = ia_css_pipe_load_extension(pipe, pipe->config.acc_extension); - - return err; -} - int ia_css_pipe_create(const struct ia_css_pipe_config *config, struct ia_css_pipe **pipe) { @@ -8257,23 +7838,6 @@ ia_css_pipe_create_extra(const struct ia_css_pipe_config *config, else ia_css_pipe_extra_config_defaults(&internal_pipe->extra_config); - if (config->mode == IA_CSS_PIPE_MODE_ACC) { - /* - * Temporary hack to migrate acceleration to CSS 2.0. - * In the future the code for all pipe types should be - * unified. - */ - *pipe = internal_pipe; - if (!internal_pipe->config.acc_extension && - internal_pipe->config.num_acc_stages == - 0) { /* if no acc binary and no standalone stage */ - *pipe = NULL; - IA_CSS_LEAVE_ERR_PRIVATE(0); - return 0; - } - return ia_css_acc_pipe_create(internal_pipe); - } - /* * Use config value when dvs_frame_delay setting equal to 2, * otherwise always 1 by default @@ -8368,15 +7932,6 @@ ia_css_pipe_create_extra(const struct ia_css_pipe_config *config, } } } - if (internal_pipe->config.acc_extension) { - err = ia_css_pipe_load_extension(internal_pipe, - internal_pipe->config.acc_extension); - if (err) { - IA_CSS_LEAVE_ERR_PRIVATE(err); - kvfree(internal_pipe); - return err; - } - } /* set all info to zeroes first */ memset(&internal_pipe->info, 0, sizeof(internal_pipe->info)); @@ -8525,57 +8080,6 @@ find_pipe(struct ia_css_pipe *pipes[], unsigned int num_pipes, } static int -ia_css_acc_stream_create(struct ia_css_stream *stream) -{ - int i; - int err = 0; - - IA_CSS_ENTER_PRIVATE("stream = %p", stream); - - if (!stream) { - IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL); - return -EINVAL; - } - - for (i = 0; i < stream->num_pipes; i++) { - struct ia_css_pipe *pipe = stream->pipes[i]; - - if (!pipe) { - IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL); - return -EINVAL; - } - - pipe->stream = stream; - } - - /* Map SP threads before doing anything. */ - err = map_sp_threads(stream, true); - if (err) { - IA_CSS_LEAVE_ERR_PRIVATE(err); - return err; - } - - for (i = 0; i < stream->num_pipes; i++) { - struct ia_css_pipe *pipe = stream->pipes[i]; - - assert(pipe); - ia_css_pipe_map_queue(pipe, true); - } - - err = create_host_pipeline_structure(stream); - if (err) { - IA_CSS_LEAVE_ERR_PRIVATE(err); - return err; - } - - stream->started = false; - - IA_CSS_LEAVE_ERR_PRIVATE(0); - - return 0; -} - -static int metadata_info_init(const struct ia_css_metadata_config *mdc, struct ia_css_metadata_info *md) { @@ -8807,11 +8311,6 @@ ia_css_stream_create(const struct ia_css_stream_config *stream_config, goto ERR; IA_CSS_LOG("isp_params_configs: %p", curr_stream->isp_params_configs); - if (num_pipes == 1 && pipes[0]->config.mode == IA_CSS_PIPE_MODE_ACC) { - *stream = curr_stream; - err = ia_css_acc_stream_create(curr_stream); - goto ERR; - } /* sensor binning */ if (!spcopyonly) { sensor_binning_changed = @@ -8832,7 +8331,6 @@ ia_css_stream_create(const struct ia_css_stream_config *stream_config, /* Search for the preview pipe and create the copy pipe */ struct ia_css_pipe *preview_pipe; struct ia_css_pipe *video_pipe; - struct ia_css_pipe *acc_pipe; struct ia_css_pipe *capture_pipe = NULL; struct ia_css_pipe *copy_pipe = NULL; @@ -8847,11 +8345,7 @@ ia_css_stream_create(const struct ia_css_stream_config *stream_config, IA_CSS_PIPE_MODE_PREVIEW, false); video_pipe = find_pipe(pipes, num_pipes, IA_CSS_PIPE_MODE_VIDEO, false); - acc_pipe = find_pipe(pipes, num_pipes, IA_CSS_PIPE_MODE_ACC, - false); - if (acc_pipe && num_pipes == 2 && curr_stream->cont_capt) - curr_stream->cont_capt = - false; /* preview + QoS case will not need cont_capt switch */ + if (curr_stream->cont_capt) { capture_pipe = find_pipe(pipes, num_pipes, IA_CSS_PIPE_MODE_CAPTURE, @@ -8888,9 +8382,6 @@ ia_css_stream_create(const struct ia_css_stream_config *stream_config, } if (video_pipe && curr_stream->cont_capt) video_pipe->pipe_settings.video.capture_pipe = capture_pipe; - - if (preview_pipe && acc_pipe) - preview_pipe->pipe_settings.preview.acc_pipe = acc_pipe; } for (i = 0; i < num_pipes; i++) { curr_pipe = pipes[i]; @@ -9738,13 +9229,6 @@ void ia_css_pipe_map_queue(struct ia_css_pipe *pipe, bool map) if (!pipe->stream->config.continuous) ia_css_queue_map(thread_id, IA_CSS_BUFFER_TYPE_OUTPUT_FRAME, map); ia_css_queue_map(thread_id, IA_CSS_BUFFER_TYPE_METADATA, map); - } else if (pipe->mode == IA_CSS_PIPE_ID_ACC) { - if (need_input_queue) - ia_css_queue_map(thread_id, IA_CSS_BUFFER_TYPE_INPUT_FRAME, map); - ia_css_queue_map(thread_id, IA_CSS_BUFFER_TYPE_OUTPUT_FRAME, map); - ia_css_queue_map(thread_id, IA_CSS_BUFFER_TYPE_PARAMETER_SET, map); - ia_css_queue_map(thread_id, IA_CSS_BUFFER_TYPE_PER_FRAME_PARAMETER_SET, map); - ia_css_queue_map(thread_id, IA_CSS_BUFFER_TYPE_METADATA, map); } else if (pipe->mode == IA_CSS_PIPE_ID_YUVPP) { unsigned int idx; @@ -9795,92 +9279,6 @@ ia_css_unlock_raw_frame(struct ia_css_stream *stream, uint32_t exp_id) return ret; } -/* - * @brief Set the state (Enable or Disable) of the Extension stage in the - * given pipe. - */ -int -ia_css_pipe_set_qos_ext_state(struct ia_css_pipe *pipe, uint32_t fw_handle, - bool enable) -{ - unsigned int thread_id; - struct ia_css_pipeline_stage *stage; - int err = 0; - - IA_CSS_ENTER(""); - - /* Parameter Check */ - if (!pipe || !pipe->stream) { - IA_CSS_ERROR("Invalid Pipe."); - err = -EINVAL; - } else if (!(pipe->config.acc_extension)) { - IA_CSS_ERROR("Invalid Pipe(No Extension Firmware)"); - err = -EINVAL; - } else if (!sh_css_sp_is_running()) { - IA_CSS_ERROR("Leaving: queue unavailable."); - err = -EBUSY; - } else { - /* Query the threadid and stage_num for the Extension firmware*/ - ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(pipe), &thread_id); - err = ia_css_pipeline_get_stage_from_fw(&pipe->pipeline, fw_handle, &stage); - if (!err) { - /* Set the Extension State;. TODO: Add check for stage firmware.type (QOS)*/ - err = ia_css_bufq_enqueue_psys_event( - (uint8_t)IA_CSS_PSYS_SW_EVENT_STAGE_ENABLE_DISABLE, - (uint8_t)thread_id, - (uint8_t)stage->stage_num, - enable ? 1 : 0); - if (!err) { - if (enable) - SH_CSS_QOS_STAGE_ENABLE(&sh_css_sp_group.pipe[thread_id], stage->stage_num); - else - SH_CSS_QOS_STAGE_DISABLE(&sh_css_sp_group.pipe[thread_id], stage->stage_num); - } - } - } - IA_CSS_LEAVE("err:%d handle:%u enable:%d", err, fw_handle, enable); - return err; -} - -/* - * @brief Get the state (Enable or Disable) of the Extension stage in the - * given pipe. - */ -int -ia_css_pipe_get_qos_ext_state(struct ia_css_pipe *pipe, uint32_t fw_handle, - bool *enable) -{ - struct ia_css_pipeline_stage *stage; - unsigned int thread_id; - int err = 0; - - IA_CSS_ENTER(""); - - /* Parameter Check */ - if (!pipe || !pipe->stream) { - IA_CSS_ERROR("Invalid Pipe."); - err = -EINVAL; - } else if (!(pipe->config.acc_extension)) { - IA_CSS_ERROR("Invalid Pipe (No Extension Firmware)."); - err = -EINVAL; - } else if (!sh_css_sp_is_running()) { - IA_CSS_ERROR("Leaving: queue unavailable."); - err = -EBUSY; - } else { - /* Query the threadid and stage_num corresponding to the Extension firmware*/ - ia_css_pipeline_get_sp_thread_id(ia_css_pipe_get_pipe_num(pipe), &thread_id); - err = ia_css_pipeline_get_stage_from_fw(&pipe->pipeline, fw_handle, &stage); - - if (!err) { - /* Get the Extension State */ - *enable = (SH_CSS_QOS_STAGE_IS_ENABLED(&sh_css_sp_group.pipe[thread_id], - stage->stage_num)) ? true : false; - } - } - IA_CSS_LEAVE("err:%d handle:%u enable:%d", err, fw_handle, *enable); - return err; -} - static void sh_css_hmm_buffer_record_init(void) { diff --git a/drivers/staging/media/atomisp/pci/sh_css_internal.h b/drivers/staging/media/atomisp/pci/sh_css_internal.h index 435b3cedd1c3..d98f1323441e 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_internal.h +++ b/drivers/staging/media/atomisp/pci/sh_css_internal.h @@ -488,16 +488,6 @@ ia_css_metadata_free_multiple(unsigned int num_bufs, /* Macro for handling pipe_qos_config */ #define QOS_INVALID (~0U) -#define QOS_ALL_STAGES_DISABLED (0U) -#define QOS_STAGE_MASK(num) (0x00000001 << num) -#define SH_CSS_IS_QOS_PIPE(pipe) ((pipe)->pipe_qos_config != QOS_INVALID) -#define SH_CSS_QOS_STAGE_ENABLE(pipe, num) ((pipe)->pipe_qos_config |= QOS_STAGE_MASK(num)) -#define SH_CSS_QOS_STAGE_DISABLE(pipe, num) ((pipe)->pipe_qos_config &= ~QOS_STAGE_MASK(num)) -#define SH_CSS_QOS_STAGE_IS_ENABLED(pipe, num) ((pipe)->pipe_qos_config & QOS_STAGE_MASK(num)) -#define SH_CSS_QOS_STAGE_IS_ALL_DISABLED(pipe) ((pipe)->pipe_qos_config == QOS_ALL_STAGES_DISABLED) -#define SH_CSS_QOS_MODE_PIPE_ADD(mode, pipe) ((mode) |= (0x1 << (pipe)->pipe_id)) -#define SH_CSS_QOS_MODE_PIPE_REMOVE(mode, pipe) ((mode) &= ~(0x1 << (pipe)->pipe_id)) -#define SH_CSS_IS_QOS_ONLY_MODE(mode) ((mode) == (0x1 << IA_CSS_PIPE_ID_ACC)) /* Information for a pipeline */ struct sh_css_sp_pipeline { @@ -907,9 +897,6 @@ sh_css_params_init(void); void sh_css_params_uninit(void); -/* For Acceleration API: Flush FW (shared buffer pointer) arguments */ -void sh_css_flush(struct ia_css_acc_fw *fw); - void sh_css_binary_args_reset(struct sh_css_binary_args *args); diff --git a/drivers/staging/media/atomisp/pci/sh_css_legacy.h b/drivers/staging/media/atomisp/pci/sh_css_legacy.h index 567c8d6dcc2c..cdf239b070a8 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_legacy.h +++ b/drivers/staging/media/atomisp/pci/sh_css_legacy.h @@ -32,7 +32,6 @@ enum ia_css_pipe_id { IA_CSS_PIPE_ID_VIDEO, IA_CSS_PIPE_ID_CAPTURE, IA_CSS_PIPE_ID_YUVPP, - IA_CSS_PIPE_ID_ACC, IA_CSS_PIPE_ID_NUM }; diff --git a/drivers/staging/media/atomisp/pci/sh_css_param_shading.c b/drivers/staging/media/atomisp/pci/sh_css_param_shading.c index 41a4c9162319..5b43cc656269 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_param_shading.c +++ b/drivers/staging/media/atomisp/pci/sh_css_param_shading.c @@ -13,6 +13,7 @@ * more details. */ +#include <linux/math.h> #include <linux/slab.h> #include <math_support.h> @@ -239,10 +240,9 @@ prepare_shading_table(const struct ia_css_shading_table *in_table, { unsigned int input_width, input_height, table_width, table_height, i; unsigned int left_padding, top_padding, left_cropping; - unsigned int bds_numerator, bds_denominator; - int right_padding; - struct ia_css_shading_table *result; + struct u32_fract bds; + int right_padding; assert(target_table); assert(binary); @@ -265,17 +265,16 @@ prepare_shading_table(const struct ia_css_shading_table *in_table, left_cropping = (binary->info->sp.pipeline.left_cropping == 0) ? binary->dvs_envelope.width : 2 * ISP_VEC_NELEMS; - sh_css_bds_factor_get_numerator_denominator - (bds_factor, &bds_numerator, &bds_denominator); + sh_css_bds_factor_get_fract(bds_factor, &bds); left_padding = (left_padding + binary->info->sp.pipeline.left_cropping) * - bds_numerator / bds_denominator - + bds.numerator / bds.denominator - binary->info->sp.pipeline.left_cropping; right_padding = (binary->internal_frame_info.res.width - - binary->effective_in_frame_res.width * bds_denominator / - bds_numerator - left_cropping) * bds_numerator / bds_denominator; - top_padding = binary->info->sp.pipeline.top_cropping * bds_numerator / - bds_denominator - + binary->effective_in_frame_res.width * bds.denominator / + bds.numerator - left_cropping) * bds.numerator / bds.denominator; + top_padding = binary->info->sp.pipeline.top_cropping * bds.numerator / + bds.denominator - binary->info->sp.pipeline.top_cropping; /* diff --git a/drivers/staging/media/atomisp/pci/sh_css_params.c b/drivers/staging/media/atomisp/pci/sh_css_params.c index 67915d76a87f..f08564f58242 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_params.c +++ b/drivers/staging/media/atomisp/pci/sh_css_params.c @@ -936,8 +936,8 @@ sh_css_set_black_frame(struct ia_css_stream *stream, assert(raw_black_frame); params = stream->isp_params_configs; - height = raw_black_frame->info.res.height; - width = raw_black_frame->info.padded_width; + height = raw_black_frame->frame_info.res.height; + width = raw_black_frame->frame_info.padded_width; ptr = raw_black_frame->data + raw_black_frame->planes.raw.offset; @@ -1187,16 +1187,15 @@ ia_css_process_zoom_and_motion( const struct sh_css_binary_args *args = &stage->args; const struct ia_css_frame_info *out_infos[IA_CSS_BINARY_MAX_OUTPUT_PORTS] = {NULL}; - if (args->out_frame[0]) - out_infos[0] = &args->out_frame[0]->info; + out_infos[0] = ia_css_frame_get_info(args->out_frame[0]); + info = &stage->firmware->info.isp; ia_css_binary_fill_info(info, false, false, ATOMISP_INPUT_FORMAT_RAW_10, - args->in_frame ? &args->in_frame->info : NULL, + ia_css_frame_get_info(args->in_frame), NULL, out_infos, - args->out_vf_frame ? &args->out_vf_frame->info - : NULL, + ia_css_frame_get_info(args->out_vf_frame), &tmp_binary, NULL, -1, true); @@ -3461,10 +3460,10 @@ sh_css_params_write_to_ddr_internal( if (stage->args.delay_frames[0]) { /*When delay frames are present(as in case of video), they are used for dvs. Configure DVS using those params*/ - dvs_in_frame_info = &stage->args.delay_frames[0]->info; + dvs_in_frame_info = &stage->args.delay_frames[0]->frame_info; } else { /*Otherwise, use input frame to configure DVS*/ - dvs_in_frame_info = &stage->args.in_frame->info; + dvs_in_frame_info = &stage->args.in_frame->frame_info; } /* Generate default DVS unity table on start up*/ diff --git a/drivers/staging/media/atomisp/pci/sh_css_sp.c b/drivers/staging/media/atomisp/pci/sh_css_sp.c index 615500a7d3c4..0dd58a7fe2cc 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_sp.c +++ b/drivers/staging/media/atomisp/pci/sh_css_sp.c @@ -277,10 +277,10 @@ sh_css_sp_start_raw_copy(struct ia_css_frame *out_frame, ia_css_pipeline_get_sp_thread_id(pipe_num, &thread_id); pipe = &sh_css_sp_group.pipe[thread_id]; - pipe->copy.raw.height = out_frame->info.res.height; - pipe->copy.raw.width = out_frame->info.res.width; - pipe->copy.raw.padded_width = out_frame->info.padded_width; - pipe->copy.raw.raw_bit_depth = out_frame->info.raw_bit_depth; + pipe->copy.raw.height = out_frame->frame_info.res.height; + pipe->copy.raw.width = out_frame->frame_info.res.width; + pipe->copy.raw.padded_width = out_frame->frame_info.padded_width; + pipe->copy.raw.raw_bit_depth = out_frame->frame_info.raw_bit_depth; pipe->copy.raw.max_input_width = max_input_width; pipe->num_stages = 1; pipe->pipe_id = pipe_id; @@ -351,10 +351,10 @@ sh_css_sp_start_isys_copy(struct ia_css_frame *out_frame, ia_css_pipeline_get_sp_thread_id(pipe_num, &thread_id); pipe = &sh_css_sp_group.pipe[thread_id]; - pipe->copy.raw.height = out_frame->info.res.height; - pipe->copy.raw.width = out_frame->info.res.width; - pipe->copy.raw.padded_width = out_frame->info.padded_width; - pipe->copy.raw.raw_bit_depth = out_frame->info.raw_bit_depth; + pipe->copy.raw.height = out_frame->frame_info.res.height; + pipe->copy.raw.width = out_frame->frame_info.res.width; + pipe->copy.raw.padded_width = out_frame->frame_info.padded_width; + pipe->copy.raw.raw_bit_depth = out_frame->frame_info.raw_bit_depth; pipe->copy.raw.max_input_width = max_input_width; pipe->num_stages = 1; pipe->pipe_id = pipe_id; @@ -451,9 +451,9 @@ sh_css_copy_frame_to_spframe(struct ia_css_frame_sp *sp_frame_out, frame_in->data, frame_in->buf_type); - ia_css_frame_info_to_frame_sp_info(&sp_frame_out->info, &frame_in->info); + ia_css_frame_info_to_frame_sp_info(&sp_frame_out->info, &frame_in->frame_info); - switch (frame_in->info.format) { + switch (frame_in->frame_info.format) { case IA_CSS_FRAME_FORMAT_RAW_PACKED: case IA_CSS_FRAME_FORMAT_RAW: sp_frame_out->planes.raw.offset = frame_in->planes.raw.offset; @@ -536,7 +536,7 @@ set_input_frame_buffer(const struct ia_css_frame *frame) if (!frame) return -EINVAL; - switch (frame->info.format) { + switch (frame->frame_info.format) { case IA_CSS_FRAME_FORMAT_QPLANE6: case IA_CSS_FRAME_FORMAT_YUV420_16: case IA_CSS_FRAME_FORMAT_RAW_PACKED: @@ -567,7 +567,7 @@ set_output_frame_buffer(const struct ia_css_frame *frame, if (!frame) return -EINVAL; - switch (frame->info.format) { + switch (frame->frame_info.format) { case IA_CSS_FRAME_FORMAT_YUV420: case IA_CSS_FRAME_FORMAT_YUV422: case IA_CSS_FRAME_FORMAT_YUV444: @@ -608,7 +608,7 @@ set_view_finder_buffer(const struct ia_css_frame *frame) if (!frame) return -EINVAL; - switch (frame->info.format) { + switch (frame->frame_info.format) { /* the dual output pin */ case IA_CSS_FRAME_FORMAT_NV12: case IA_CSS_FRAME_FORMAT_NV12_16: @@ -819,34 +819,35 @@ static int configure_isp_from_args(const struct sh_css_sp_pipeline *pipeline, ret = ia_css_fpn_configure(binary, &binary->in_frame_info); if (ret) return ret; - ret = ia_css_crop_configure(binary, &args->delay_frames[0]->info); + ret = ia_css_crop_configure(binary, ia_css_frame_get_info(args->delay_frames[0])); if (ret) return ret; ret = ia_css_qplane_configure(pipeline, binary, &binary->in_frame_info); if (ret) return ret; - ret = ia_css_output0_configure(binary, &args->out_frame[0]->info); + ret = ia_css_output0_configure(binary, ia_css_frame_get_info(args->out_frame[0])); if (ret) return ret; - ret = ia_css_output1_configure(binary, &args->out_vf_frame->info); + ret = ia_css_output1_configure(binary, ia_css_frame_get_info(args->out_vf_frame)); if (ret) return ret; ret = ia_css_copy_output_configure(binary, args->copy_output); if (ret) return ret; - ret = ia_css_output0_configure(binary, &args->out_frame[0]->info); + ret = ia_css_output0_configure(binary, ia_css_frame_get_info(args->out_frame[0])); if (ret) return ret; - ret = ia_css_iterator_configure(binary, &args->in_frame->info); + ret = ia_css_iterator_configure(binary, ia_css_frame_get_info(args->in_frame)); if (ret) return ret; - ret = ia_css_dvs_configure(binary, &args->out_frame[0]->info); + ret = ia_css_dvs_configure(binary, ia_css_frame_get_info(args->out_frame[0])); if (ret) return ret; - ret = ia_css_output_configure(binary, &args->out_frame[0]->info); + ret = ia_css_output_configure(binary, ia_css_frame_get_info(args->out_frame[0])); if (ret) return ret; - ret = ia_css_raw_configure(pipeline, binary, &args->in_frame->info, &binary->in_frame_info, two_ppc, deinterleaved); + ret = ia_css_raw_configure(pipeline, binary, ia_css_frame_get_info(args->in_frame), + &binary->in_frame_info, two_ppc, deinterleaved); if (ret) return ret; @@ -1037,7 +1038,7 @@ sh_css_sp_init_stage(struct ia_css_binary *binary, return -EINVAL; if (args->in_frame) - ia_css_get_crop_offsets(pipe, &args->in_frame->info); + ia_css_get_crop_offsets(pipe, &args->in_frame->frame_info); else ia_css_get_crop_offsets(pipe, &binary->in_frame_info); #else @@ -1124,15 +1125,14 @@ sp_init_stage(struct ia_css_pipeline_stage *stage, const struct ia_css_frame_info *out_infos[IA_CSS_BINARY_MAX_OUTPUT_PORTS] = {NULL}; if (args->out_frame[0]) - out_infos[0] = &args->out_frame[0]->info; + out_infos[0] = &args->out_frame[0]->frame_info; info = &firmware->info.isp; ia_css_binary_fill_info(info, false, false, ATOMISP_INPUT_FORMAT_RAW_10, - args->in_frame ? &args->in_frame->info : NULL, + ia_css_frame_get_info(args->in_frame), NULL, out_infos, - args->out_vf_frame ? &args->out_vf_frame->info - : NULL, + ia_css_frame_get_info(args->out_vf_frame), &tmp_binary, NULL, -1, true); @@ -1266,7 +1266,7 @@ sh_css_sp_init_pipeline(struct ia_css_pipeline *me, sh_css_sp_group.pipe[thread_id].thread_id = thread_id; sh_css_sp_group.pipe[thread_id].pipe_num = pipe_num; sh_css_sp_group.pipe[thread_id].num_execs = me->num_execs; - sh_css_sp_group.pipe[thread_id].pipe_qos_config = me->pipe_qos_config; + sh_css_sp_group.pipe[thread_id].pipe_qos_config = QOS_INVALID; sh_css_sp_group.pipe[thread_id].required_bds_factor = required_bds_factor; sh_css_sp_group.pipe[thread_id].input_system_mode = (uint32_t)input_mode; diff --git a/drivers/staging/media/meson/vdec/codec_vp9.c b/drivers/staging/media/meson/vdec/codec_vp9.c index da7265c8de37..394df5761556 100644 --- a/drivers/staging/media/meson/vdec/codec_vp9.c +++ b/drivers/staging/media/meson/vdec/codec_vp9.c @@ -1649,8 +1649,7 @@ static void adapt_coef_probs(int prev_kf, int cur_kf, int pre_fc, else if (coef_count_node_start == VP9_MV_BITS_1_COUNT_START) coef_node_start = VP9_MV_BITS_1_START; - else if (coef_count_node_start == - VP9_MV_CLASS0_HP_0_COUNT_START) + else /* node_start == VP9_MV_CLASS0_HP_0_COUNT_START */ coef_node_start = VP9_MV_CLASS0_HP_0_START; den = count[coef_count_node_start] + diff --git a/drivers/staging/media/sunxi/Kconfig b/drivers/staging/media/sunxi/Kconfig index 4549a135741f..62a486aba88b 100644 --- a/drivers/staging/media/sunxi/Kconfig +++ b/drivers/staging/media/sunxi/Kconfig @@ -12,5 +12,6 @@ config VIDEO_SUNXI if VIDEO_SUNXI source "drivers/staging/media/sunxi/cedrus/Kconfig" +source "drivers/staging/media/sunxi/sun6i-isp/Kconfig" endif diff --git a/drivers/staging/media/sunxi/Makefile b/drivers/staging/media/sunxi/Makefile index b87140b0e15f..3d20b2f0e644 100644 --- a/drivers/staging/media/sunxi/Makefile +++ b/drivers/staging/media/sunxi/Makefile @@ -1,2 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_VIDEO_SUNXI_CEDRUS) += cedrus/ +obj-$(CONFIG_VIDEO_SUN6I_ISP) += sun6i-isp/ diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.h b/drivers/staging/media/sunxi/cedrus/cedrus.h index d8d22c7b7731..522c184e2afc 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.h +++ b/drivers/staging/media/sunxi/cedrus/cedrus.h @@ -100,7 +100,15 @@ struct cedrus_buffer { struct { unsigned int position; enum cedrus_h264_pic_type pic_type; + void *mv_col_buf; + dma_addr_t mv_col_buf_dma; + ssize_t mv_col_buf_size; } h264; + struct { + void *mv_col_buf; + dma_addr_t mv_col_buf_dma; + ssize_t mv_col_buf_size; + } h265; } codec; }; @@ -118,10 +126,6 @@ struct cedrus_ctx { union { struct { - void *mv_col_buf; - dma_addr_t mv_col_buf_dma; - ssize_t mv_col_buf_field_size; - ssize_t mv_col_buf_size; void *pic_info_buf; dma_addr_t pic_info_buf_dma; ssize_t pic_info_buf_size; @@ -135,10 +139,6 @@ struct cedrus_ctx { ssize_t intra_pred_buf_size; } h264; struct { - void *mv_col_buf; - dma_addr_t mv_col_buf_addr; - ssize_t mv_col_buf_size; - ssize_t mv_col_buf_unit_size; void *neighbor_info_buf; dma_addr_t neighbor_info_buf_addr; void *entry_points_buf; diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c index c139a37a567c..dfb401df138a 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c @@ -54,17 +54,13 @@ static void cedrus_h264_write_sram(struct cedrus_dev *dev, cedrus_write(dev, VE_AVC_SRAM_PORT_DATA, *buffer++); } -static dma_addr_t cedrus_h264_mv_col_buf_addr(struct cedrus_ctx *ctx, - unsigned int position, +static dma_addr_t cedrus_h264_mv_col_buf_addr(struct cedrus_buffer *buf, unsigned int field) { - dma_addr_t addr = ctx->codec.h264.mv_col_buf_dma; - - /* Adjust for the position */ - addr += position * ctx->codec.h264.mv_col_buf_field_size * 2; + dma_addr_t addr = buf->codec.h264.mv_col_buf_dma; /* Adjust for the field */ - addr += field * ctx->codec.h264.mv_col_buf_field_size; + addr += field * buf->codec.h264.mv_col_buf_size / 2; return addr; } @@ -76,7 +72,6 @@ static void cedrus_fill_ref_pic(struct cedrus_ctx *ctx, struct cedrus_h264_sram_ref_pic *pic) { struct vb2_buffer *vbuf = &buf->m2m_buf.vb.vb2_buf; - unsigned int position = buf->codec.h264.position; pic->top_field_order_cnt = cpu_to_le32(top_field_order_cnt); pic->bottom_field_order_cnt = cpu_to_le32(bottom_field_order_cnt); @@ -84,14 +79,12 @@ static void cedrus_fill_ref_pic(struct cedrus_ctx *ctx, pic->luma_ptr = cpu_to_le32(cedrus_buf_addr(vbuf, &ctx->dst_fmt, 0)); pic->chroma_ptr = cpu_to_le32(cedrus_buf_addr(vbuf, &ctx->dst_fmt, 1)); - pic->mv_col_top_ptr = - cpu_to_le32(cedrus_h264_mv_col_buf_addr(ctx, position, 0)); - pic->mv_col_bot_ptr = - cpu_to_le32(cedrus_h264_mv_col_buf_addr(ctx, position, 1)); + pic->mv_col_top_ptr = cpu_to_le32(cedrus_h264_mv_col_buf_addr(buf, 0)); + pic->mv_col_bot_ptr = cpu_to_le32(cedrus_h264_mv_col_buf_addr(buf, 1)); } -static void cedrus_write_frame_list(struct cedrus_ctx *ctx, - struct cedrus_run *run) +static int cedrus_write_frame_list(struct cedrus_ctx *ctx, + struct cedrus_run *run) { struct cedrus_h264_sram_ref_pic pic_list[CEDRUS_H264_FRAME_NUM]; const struct v4l2_ctrl_h264_decode_params *decode = run->h264.decode_params; @@ -146,6 +139,31 @@ static void cedrus_write_frame_list(struct cedrus_ctx *ctx, output_buf = vb2_to_cedrus_buffer(&run->dst->vb2_buf); output_buf->codec.h264.position = position; + if (!output_buf->codec.h264.mv_col_buf_size) { + const struct v4l2_ctrl_h264_sps *sps = run->h264.sps; + unsigned int field_size; + + field_size = DIV_ROUND_UP(ctx->src_fmt.width, 16) * + DIV_ROUND_UP(ctx->src_fmt.height, 16) * 16; + if (!(sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE)) + field_size = field_size * 2; + if (!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY)) + field_size = field_size * 2; + + output_buf->codec.h264.mv_col_buf_size = field_size * 2; + /* Buffer is never accessed by CPU, so we can skip kernel mapping. */ + output_buf->codec.h264.mv_col_buf = + dma_alloc_attrs(dev->dev, + output_buf->codec.h264.mv_col_buf_size, + &output_buf->codec.h264.mv_col_buf_dma, + GFP_KERNEL, DMA_ATTR_NO_KERNEL_MAPPING); + + if (!output_buf->codec.h264.mv_col_buf) { + output_buf->codec.h264.mv_col_buf_size = 0; + return -ENOMEM; + } + } + if (decode->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC) output_buf->codec.h264.pic_type = CEDRUS_H264_PIC_TYPE_FIELD; else if (sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD) @@ -162,6 +180,8 @@ static void cedrus_write_frame_list(struct cedrus_ctx *ctx, pic_list, sizeof(pic_list)); cedrus_write(dev, VE_H264_OUTPUT_FRAME_IDX, position); + + return 0; } #define CEDRUS_MAX_REF_IDX 32 @@ -496,6 +516,7 @@ static void cedrus_h264_irq_disable(struct cedrus_ctx *ctx) static int cedrus_h264_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) { struct cedrus_dev *dev = ctx->dev; + int ret; cedrus_engine_enable(ctx); @@ -506,7 +527,9 @@ static int cedrus_h264_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) ctx->codec.h264.neighbor_info_buf_dma); cedrus_write_scaling_lists(ctx, run); - cedrus_write_frame_list(ctx, run); + ret = cedrus_write_frame_list(ctx, run); + if (ret) + return ret; cedrus_set_params(ctx, run); @@ -517,8 +540,6 @@ static int cedrus_h264_start(struct cedrus_ctx *ctx) { struct cedrus_dev *dev = ctx->dev; unsigned int pic_info_size; - unsigned int field_size; - unsigned int mv_col_size; int ret; /* @@ -566,38 +587,6 @@ static int cedrus_h264_start(struct cedrus_ctx *ctx) goto err_pic_buf; } - field_size = DIV_ROUND_UP(ctx->src_fmt.width, 16) * - DIV_ROUND_UP(ctx->src_fmt.height, 16) * 16; - - /* - * FIXME: This is actually conditional to - * V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE not being set, we - * might have to rework this if memory efficiency ever is - * something we need to work on. - */ - field_size = field_size * 2; - - /* - * FIXME: This is actually conditional to - * V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY not being set, we might - * have to rework this if memory efficiency ever is something - * we need to work on. - */ - field_size = field_size * 2; - ctx->codec.h264.mv_col_buf_field_size = field_size; - - mv_col_size = field_size * 2 * CEDRUS_H264_FRAME_NUM; - ctx->codec.h264.mv_col_buf_size = mv_col_size; - ctx->codec.h264.mv_col_buf = - dma_alloc_attrs(dev->dev, - ctx->codec.h264.mv_col_buf_size, - &ctx->codec.h264.mv_col_buf_dma, - GFP_KERNEL, DMA_ATTR_NO_KERNEL_MAPPING); - if (!ctx->codec.h264.mv_col_buf) { - ret = -ENOMEM; - goto err_neighbor_buf; - } - if (ctx->src_fmt.width > 2048) { /* * Formulas for deblock and intra prediction buffer sizes @@ -613,7 +602,7 @@ static int cedrus_h264_start(struct cedrus_ctx *ctx) GFP_KERNEL, DMA_ATTR_NO_KERNEL_MAPPING); if (!ctx->codec.h264.deblk_buf) { ret = -ENOMEM; - goto err_mv_col_buf; + goto err_neighbor_buf; } /* @@ -641,12 +630,6 @@ err_deblk_buf: ctx->codec.h264.deblk_buf_dma, DMA_ATTR_NO_KERNEL_MAPPING); -err_mv_col_buf: - dma_free_attrs(dev->dev, ctx->codec.h264.mv_col_buf_size, - ctx->codec.h264.mv_col_buf, - ctx->codec.h264.mv_col_buf_dma, - DMA_ATTR_NO_KERNEL_MAPPING); - err_neighbor_buf: dma_free_attrs(dev->dev, CEDRUS_NEIGHBOR_INFO_BUF_SIZE, ctx->codec.h264.neighbor_info_buf, @@ -664,11 +647,26 @@ err_pic_buf: static void cedrus_h264_stop(struct cedrus_ctx *ctx) { struct cedrus_dev *dev = ctx->dev; + struct cedrus_buffer *buf; + struct vb2_queue *vq; + unsigned int i; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); + + for (i = 0; i < vq->num_buffers; i++) { + buf = vb2_to_cedrus_buffer(vb2_get_buffer(vq, i)); + + if (buf->codec.h264.mv_col_buf_size > 0) { + dma_free_attrs(dev->dev, + buf->codec.h264.mv_col_buf_size, + buf->codec.h264.mv_col_buf, + buf->codec.h264.mv_col_buf_dma, + DMA_ATTR_NO_KERNEL_MAPPING); + + buf->codec.h264.mv_col_buf_size = 0; + } + } - dma_free_attrs(dev->dev, ctx->codec.h264.mv_col_buf_size, - ctx->codec.h264.mv_col_buf, - ctx->codec.h264.mv_col_buf_dma, - DMA_ATTR_NO_KERNEL_MAPPING); dma_free_attrs(dev->dev, CEDRUS_NEIGHBOR_INFO_BUF_SIZE, ctx->codec.h264.neighbor_info_buf, ctx->codec.h264.neighbor_info_buf_dma, diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c index 374eb4cb2266..fc9297232456 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_h265.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus_h265.c @@ -103,12 +103,13 @@ static void cedrus_h265_sram_write_data(struct cedrus_dev *dev, void *data, } static inline dma_addr_t -cedrus_h265_frame_info_mv_col_buf_addr(struct cedrus_ctx *ctx, - unsigned int index, unsigned int field) +cedrus_h265_frame_info_mv_col_buf_addr(struct vb2_buffer *buf, + unsigned int field) { - return ctx->codec.h265.mv_col_buf_addr + index * - ctx->codec.h265.mv_col_buf_unit_size + - field * ctx->codec.h265.mv_col_buf_unit_size / 2; + struct cedrus_buffer *cedrus_buf = vb2_to_cedrus_buffer(buf); + + return cedrus_buf->codec.h265.mv_col_buf_dma + + field * cedrus_buf->codec.h265.mv_col_buf_size / 2; } static void cedrus_h265_frame_info_write_single(struct cedrus_ctx *ctx, @@ -121,9 +122,8 @@ static void cedrus_h265_frame_info_write_single(struct cedrus_ctx *ctx, dma_addr_t dst_luma_addr = cedrus_dst_buf_addr(ctx, buf, 0); dma_addr_t dst_chroma_addr = cedrus_dst_buf_addr(ctx, buf, 1); dma_addr_t mv_col_buf_addr[2] = { - cedrus_h265_frame_info_mv_col_buf_addr(ctx, buf->index, 0), - cedrus_h265_frame_info_mv_col_buf_addr(ctx, buf->index, - field_pic ? 1 : 0) + cedrus_h265_frame_info_mv_col_buf_addr(buf, 0), + cedrus_h265_frame_info_mv_col_buf_addr(buf, field_pic ? 1 : 0) }; u32 offset = VE_DEC_H265_SRAM_OFFSET_FRAME_INFO + VE_DEC_H265_SRAM_OFFSET_FRAME_INFO_UNIT * index; @@ -255,6 +255,18 @@ static void cedrus_h265_skip_bits(struct cedrus_dev *dev, int num) } } +static u32 cedrus_h265_show_bits(struct cedrus_dev *dev, int num) +{ + cedrus_write(dev, VE_DEC_H265_TRIGGER, + VE_DEC_H265_TRIGGER_SHOW_BITS | + VE_DEC_H265_TRIGGER_TYPE_N_BITS(num)); + + cedrus_wait_for(dev, VE_DEC_H265_STATUS, + VE_DEC_H265_STATUS_VLD_BUSY); + + return cedrus_read(dev, VE_DEC_H265_BITS_READ); +} + static void cedrus_h265_write_scaling_list(struct cedrus_ctx *ctx, struct cedrus_run *run) { @@ -413,13 +425,14 @@ static int cedrus_h265_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) unsigned int width_in_ctb_luma, ctb_size_luma; unsigned int log2_max_luma_coding_block_size; unsigned int ctb_addr_x, ctb_addr_y; + struct cedrus_buffer *cedrus_buf; dma_addr_t src_buf_addr; dma_addr_t src_buf_end_addr; u32 chroma_log2_weight_denom; u32 num_entry_point_offsets; u32 output_pic_list_index; u32 pic_order_cnt[2]; - u8 *padding; + u8 padding; int count; u32 reg; @@ -429,6 +442,7 @@ static int cedrus_h265_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) decode_params = run->h265.decode_params; pred_weight_table = &slice_params->pred_weight_table; num_entry_point_offsets = slice_params->num_entry_point_offsets; + cedrus_buf = vb2_to_cedrus_buffer(&run->dst->vb2_buf); /* * If entry points offsets are present, we should get them @@ -446,31 +460,25 @@ static int cedrus_h265_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) DIV_ROUND_UP(sps->pic_width_in_luma_samples, ctb_size_luma); /* MV column buffer size and allocation. */ - if (!ctx->codec.h265.mv_col_buf_size) { - unsigned int num_buffers = - run->dst->vb2_buf.vb2_queue->num_buffers; - + if (!cedrus_buf->codec.h265.mv_col_buf_size) { /* * Each CTB requires a MV col buffer with a specific unit size. * Since the address is given with missing lsb bits, 1 KiB is * added to each buffer to ensure proper alignment. */ - ctx->codec.h265.mv_col_buf_unit_size = + cedrus_buf->codec.h265.mv_col_buf_size = DIV_ROUND_UP(ctx->src_fmt.width, ctb_size_luma) * DIV_ROUND_UP(ctx->src_fmt.height, ctb_size_luma) * CEDRUS_H265_MV_COL_BUF_UNIT_CTB_SIZE + SZ_1K; - ctx->codec.h265.mv_col_buf_size = num_buffers * - ctx->codec.h265.mv_col_buf_unit_size; - /* Buffer is never accessed by CPU, so we can skip kernel mapping. */ - ctx->codec.h265.mv_col_buf = + cedrus_buf->codec.h265.mv_col_buf = dma_alloc_attrs(dev->dev, - ctx->codec.h265.mv_col_buf_size, - &ctx->codec.h265.mv_col_buf_addr, + cedrus_buf->codec.h265.mv_col_buf_size, + &cedrus_buf->codec.h265.mv_col_buf_dma, GFP_KERNEL, DMA_ATTR_NO_KERNEL_MAPPING); - if (!ctx->codec.h265.mv_col_buf) { - ctx->codec.h265.mv_col_buf_size = 0; + if (!cedrus_buf->codec.h265.mv_col_buf) { + cedrus_buf->codec.h265.mv_col_buf_size = 0; return -ENOMEM; } } @@ -533,21 +541,22 @@ static int cedrus_h265_setup(struct cedrus_ctx *ctx, struct cedrus_run *run) if (slice_params->data_byte_offset == 0) return -EOPNOTSUPP; - padding = (u8 *)vb2_plane_vaddr(&run->src->vb2_buf, 0) + - slice_params->data_byte_offset - 1; + cedrus_h265_skip_bits(dev, (slice_params->data_byte_offset - 1) * 8); + + padding = cedrus_h265_show_bits(dev, 8); /* at least one bit must be set in that byte */ - if (*padding == 0) + if (padding == 0) return -EINVAL; for (count = 0; count < 8; count++) - if (*padding & (1 << count)) + if (padding & (1 << count)) break; /* Include the one bit. */ count++; - cedrus_h265_skip_bits(dev, slice_params->data_byte_offset * 8 - count); + cedrus_h265_skip_bits(dev, 8 - count); /* Bitstream parameters. */ @@ -828,9 +837,6 @@ static int cedrus_h265_start(struct cedrus_ctx *ctx) { struct cedrus_dev *dev = ctx->dev; - /* The buffer size is calculated at setup time. */ - ctx->codec.h265.mv_col_buf_size = 0; - /* Buffer is never accessed by CPU, so we can skip kernel mapping. */ ctx->codec.h265.neighbor_info_buf = dma_alloc_attrs(dev->dev, CEDRUS_H265_NEIGHBOR_INFO_BUF_SIZE, @@ -857,14 +863,24 @@ static int cedrus_h265_start(struct cedrus_ctx *ctx) static void cedrus_h265_stop(struct cedrus_ctx *ctx) { struct cedrus_dev *dev = ctx->dev; + struct cedrus_buffer *buf; + struct vb2_queue *vq; + unsigned int i; - if (ctx->codec.h265.mv_col_buf_size > 0) { - dma_free_attrs(dev->dev, ctx->codec.h265.mv_col_buf_size, - ctx->codec.h265.mv_col_buf, - ctx->codec.h265.mv_col_buf_addr, - DMA_ATTR_NO_KERNEL_MAPPING); + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - ctx->codec.h265.mv_col_buf_size = 0; + for (i = 0; i < vq->num_buffers; i++) { + buf = vb2_to_cedrus_buffer(vb2_get_buffer(vq, i)); + + if (buf->codec.h265.mv_col_buf_size > 0) { + dma_free_attrs(dev->dev, + buf->codec.h265.mv_col_buf_size, + buf->codec.h265.mv_col_buf, + buf->codec.h265.mv_col_buf_dma, + DMA_ATTR_NO_KERNEL_MAPPING); + + buf->codec.h265.mv_col_buf_size = 0; + } } dma_free_attrs(dev->dev, CEDRUS_H265_NEIGHBOR_INFO_BUF_SIZE, diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h index d30c3ffd31c0..05e6cbc548ab 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus_regs.h +++ b/drivers/staging/media/sunxi/cedrus/cedrus_regs.h @@ -521,6 +521,8 @@ #define VE_DEC_H265_LOW_ADDR_ENTRY_POINTS_BUF(a) \ SHIFT_AND_MASK_BITS(a, 7, 0) +#define VE_DEC_H265_BITS_READ (VE_ENGINE_DEC_H265 + 0xdc) + #define VE_DEC_H265_SRAM_OFFSET (VE_ENGINE_DEC_H265 + 0xe0) #define VE_DEC_H265_SRAM_OFFSET_PRED_WEIGHT_LUMA_L0 0x00 diff --git a/drivers/staging/media/sunxi/sun6i-isp/Kconfig b/drivers/staging/media/sunxi/sun6i-isp/Kconfig new file mode 100644 index 000000000000..68dcae9cd7d7 --- /dev/null +++ b/drivers/staging/media/sunxi/sun6i-isp/Kconfig @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_SUN6I_ISP + tristate "Allwinner A31 Image Signal Processor (ISP) Driver" + depends on V4L_PLATFORM_DRIVERS && VIDEO_DEV + depends on ARCH_SUNXI || COMPILE_TEST + depends on PM && COMMON_CLK && HAS_DMA + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select VIDEOBUF2_DMA_CONTIG + select VIDEOBUF2_VMALLOC + select V4L2_FWNODE + select REGMAP_MMIO + help + Support for the Allwinner A31 Image Signal Processor (ISP), also + found on other platforms such as the A80, A83T or V3/V3s. diff --git a/drivers/staging/media/sunxi/sun6i-isp/Makefile b/drivers/staging/media/sunxi/sun6i-isp/Makefile new file mode 100644 index 000000000000..da1034785144 --- /dev/null +++ b/drivers/staging/media/sunxi/sun6i-isp/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only +sun6i-isp-y += sun6i_isp.o sun6i_isp_proc.o sun6i_isp_capture.o sun6i_isp_params.o + +obj-$(CONFIG_VIDEO_SUN6I_ISP) += sun6i-isp.o diff --git a/drivers/staging/media/sunxi/sun6i-isp/TODO.txt b/drivers/staging/media/sunxi/sun6i-isp/TODO.txt new file mode 100644 index 000000000000..1e3236edc1ab --- /dev/null +++ b/drivers/staging/media/sunxi/sun6i-isp/TODO.txt @@ -0,0 +1,6 @@ +Unstaging requirements: +- Add uAPI support and documentation for the configuration of all the hardware + modules and description of the statistics data structures; +- Add support for statistics reporting; +- Add userspace support in libcamera which demonstrates the ability to receive + statistics and adapt hardware modules configuration accordingly; diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.c b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.c new file mode 100644 index 000000000000..7b7947509b69 --- /dev/null +++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.c @@ -0,0 +1,555 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021-2022 Bootlin + * Author: Paul Kocialkowski <[email protected]> + */ + +#include <linux/clk.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <media/v4l2-device.h> +#include <media/v4l2-mc.h> + +#include "sun6i_isp.h" +#include "sun6i_isp_capture.h" +#include "sun6i_isp_params.h" +#include "sun6i_isp_proc.h" +#include "sun6i_isp_reg.h" + +/* Helpers */ + +u32 sun6i_isp_load_read(struct sun6i_isp_device *isp_dev, u32 offset) +{ + u32 *data = (u32 *)(isp_dev->tables.load.data + offset); + + return *data; +} + +void sun6i_isp_load_write(struct sun6i_isp_device *isp_dev, u32 offset, + u32 value) +{ + u32 *data = (u32 *)(isp_dev->tables.load.data + offset); + + *data = value; +} + +/* State */ + +/* + * The ISP works with a load buffer, which gets copied to the actual registers + * by the hardware before processing a frame when a specific flag is set. + * This is represented by tracking the ISP state in the different parts of + * the code with explicit sync points: + * - state update: to update the load buffer for the next frame if necessary; + * - state complete: to indicate that the state update was applied. + */ + +static void sun6i_isp_state_ready(struct sun6i_isp_device *isp_dev) +{ + struct regmap *regmap = isp_dev->regmap; + u32 value; + + regmap_read(regmap, SUN6I_ISP_FE_CTRL_REG, &value); + value |= SUN6I_ISP_FE_CTRL_PARA_READY; + regmap_write(regmap, SUN6I_ISP_FE_CTRL_REG, value); +} + +static void sun6i_isp_state_complete(struct sun6i_isp_device *isp_dev) +{ + unsigned long flags; + + spin_lock_irqsave(&isp_dev->state_lock, flags); + + sun6i_isp_capture_state_complete(isp_dev); + sun6i_isp_params_state_complete(isp_dev); + + spin_unlock_irqrestore(&isp_dev->state_lock, flags); +} + +void sun6i_isp_state_update(struct sun6i_isp_device *isp_dev, bool ready_hold) +{ + bool update = false; + unsigned long flags; + + spin_lock_irqsave(&isp_dev->state_lock, flags); + + sun6i_isp_capture_state_update(isp_dev, &update); + sun6i_isp_params_state_update(isp_dev, &update); + + if (update && !ready_hold) + sun6i_isp_state_ready(isp_dev); + + spin_unlock_irqrestore(&isp_dev->state_lock, flags); +} + +/* Tables */ + +static int sun6i_isp_table_setup(struct sun6i_isp_device *isp_dev, + struct sun6i_isp_table *table) +{ + table->data = dma_alloc_coherent(isp_dev->dev, table->size, + &table->address, GFP_KERNEL); + if (!table->data) + return -ENOMEM; + + return 0; +} + +static void sun6i_isp_table_cleanup(struct sun6i_isp_device *isp_dev, + struct sun6i_isp_table *table) +{ + dma_free_coherent(isp_dev->dev, table->size, table->data, + table->address); +} + +void sun6i_isp_tables_configure(struct sun6i_isp_device *isp_dev) +{ + struct regmap *regmap = isp_dev->regmap; + + regmap_write(regmap, SUN6I_ISP_REG_LOAD_ADDR_REG, + SUN6I_ISP_ADDR_VALUE(isp_dev->tables.load.address)); + + regmap_write(regmap, SUN6I_ISP_REG_SAVE_ADDR_REG, + SUN6I_ISP_ADDR_VALUE(isp_dev->tables.save.address)); + + regmap_write(regmap, SUN6I_ISP_LUT_TABLE_ADDR_REG, + SUN6I_ISP_ADDR_VALUE(isp_dev->tables.lut.address)); + + regmap_write(regmap, SUN6I_ISP_DRC_TABLE_ADDR_REG, + SUN6I_ISP_ADDR_VALUE(isp_dev->tables.drc.address)); + + regmap_write(regmap, SUN6I_ISP_STATS_ADDR_REG, + SUN6I_ISP_ADDR_VALUE(isp_dev->tables.stats.address)); +} + +static int sun6i_isp_tables_setup(struct sun6i_isp_device *isp_dev, + const struct sun6i_isp_variant *variant) +{ + struct sun6i_isp_tables *tables = &isp_dev->tables; + int ret; + + tables->load.size = variant->table_load_save_size; + ret = sun6i_isp_table_setup(isp_dev, &tables->load); + if (ret) + return ret; + + tables->save.size = variant->table_load_save_size; + ret = sun6i_isp_table_setup(isp_dev, &tables->save); + if (ret) + return ret; + + tables->lut.size = variant->table_lut_size; + ret = sun6i_isp_table_setup(isp_dev, &tables->lut); + if (ret) + return ret; + + tables->drc.size = variant->table_drc_size; + ret = sun6i_isp_table_setup(isp_dev, &tables->drc); + if (ret) + return ret; + + tables->stats.size = variant->table_stats_size; + ret = sun6i_isp_table_setup(isp_dev, &tables->stats); + if (ret) + return ret; + + return 0; +} + +static void sun6i_isp_tables_cleanup(struct sun6i_isp_device *isp_dev) +{ + struct sun6i_isp_tables *tables = &isp_dev->tables; + + sun6i_isp_table_cleanup(isp_dev, &tables->stats); + sun6i_isp_table_cleanup(isp_dev, &tables->drc); + sun6i_isp_table_cleanup(isp_dev, &tables->lut); + sun6i_isp_table_cleanup(isp_dev, &tables->save); + sun6i_isp_table_cleanup(isp_dev, &tables->load); +} + +/* Media */ + +static const struct media_device_ops sun6i_isp_media_ops = { + .link_notify = v4l2_pipeline_link_notify, +}; + +/* V4L2 */ + +static int sun6i_isp_v4l2_setup(struct sun6i_isp_device *isp_dev) +{ + struct sun6i_isp_v4l2 *v4l2 = &isp_dev->v4l2; + struct v4l2_device *v4l2_dev = &v4l2->v4l2_dev; + struct media_device *media_dev = &v4l2->media_dev; + struct device *dev = isp_dev->dev; + int ret; + + /* Media Device */ + + strscpy(media_dev->model, SUN6I_ISP_DESCRIPTION, + sizeof(media_dev->model)); + media_dev->ops = &sun6i_isp_media_ops; + media_dev->hw_revision = 0; + media_dev->dev = dev; + + media_device_init(media_dev); + + ret = media_device_register(media_dev); + if (ret) { + dev_err(dev, "failed to register media device\n"); + return ret; + } + + /* V4L2 Device */ + + v4l2_dev->mdev = media_dev; + + ret = v4l2_device_register(dev, v4l2_dev); + if (ret) { + dev_err(dev, "failed to register v4l2 device\n"); + goto error_media; + } + + return 0; + +error_media: + media_device_unregister(media_dev); + media_device_cleanup(media_dev); + + return ret; +} + +static void sun6i_isp_v4l2_cleanup(struct sun6i_isp_device *isp_dev) +{ + struct sun6i_isp_v4l2 *v4l2 = &isp_dev->v4l2; + + media_device_unregister(&v4l2->media_dev); + v4l2_device_unregister(&v4l2->v4l2_dev); + media_device_cleanup(&v4l2->media_dev); +} + +/* Platform */ + +static irqreturn_t sun6i_isp_interrupt(int irq, void *private) +{ + struct sun6i_isp_device *isp_dev = private; + struct regmap *regmap = isp_dev->regmap; + u32 status = 0, enable = 0; + + regmap_read(regmap, SUN6I_ISP_FE_INT_STA_REG, &status); + regmap_read(regmap, SUN6I_ISP_FE_INT_EN_REG, &enable); + + if (!status) + return IRQ_NONE; + else if (!(status & enable)) + goto complete; + + /* + * The ISP working cycle starts with a params-load, which makes the + * state from the load buffer active. Then it starts processing the + * frame and gives a finish interrupt. Soon after that, the next state + * coming from the load buffer will be applied for the next frame, + * giving a params-load as well. + * + * Because both frame finish and params-load are received almost + * at the same time (one ISR call), handle them in chronology order. + */ + + if (status & SUN6I_ISP_FE_INT_STA_FINISH) + sun6i_isp_capture_finish(isp_dev); + + if (status & SUN6I_ISP_FE_INT_STA_PARA_LOAD) { + sun6i_isp_state_complete(isp_dev); + sun6i_isp_state_update(isp_dev, false); + } + +complete: + regmap_write(regmap, SUN6I_ISP_FE_INT_STA_REG, status); + + return IRQ_HANDLED; +} + +static int sun6i_isp_suspend(struct device *dev) +{ + struct sun6i_isp_device *isp_dev = dev_get_drvdata(dev); + + reset_control_assert(isp_dev->reset); + clk_disable_unprepare(isp_dev->clock_ram); + clk_disable_unprepare(isp_dev->clock_mod); + + return 0; +} + +static int sun6i_isp_resume(struct device *dev) +{ + struct sun6i_isp_device *isp_dev = dev_get_drvdata(dev); + int ret; + + ret = reset_control_deassert(isp_dev->reset); + if (ret) { + dev_err(dev, "failed to deassert reset\n"); + return ret; + } + + ret = clk_prepare_enable(isp_dev->clock_mod); + if (ret) { + dev_err(dev, "failed to enable module clock\n"); + goto error_reset; + } + + ret = clk_prepare_enable(isp_dev->clock_ram); + if (ret) { + dev_err(dev, "failed to enable ram clock\n"); + goto error_clock_mod; + } + + return 0; + +error_clock_mod: + clk_disable_unprepare(isp_dev->clock_mod); + +error_reset: + reset_control_assert(isp_dev->reset); + + return ret; +} + +static const struct dev_pm_ops sun6i_isp_pm_ops = { + .runtime_suspend = sun6i_isp_suspend, + .runtime_resume = sun6i_isp_resume, +}; + +static const struct regmap_config sun6i_isp_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x400, +}; + +static int sun6i_isp_resources_setup(struct sun6i_isp_device *isp_dev, + struct platform_device *platform_dev) +{ + struct device *dev = isp_dev->dev; + void __iomem *io_base; + int irq; + int ret; + + /* Registers */ + + io_base = devm_platform_ioremap_resource(platform_dev, 0); + if (IS_ERR(io_base)) + return PTR_ERR(io_base); + + isp_dev->regmap = devm_regmap_init_mmio_clk(dev, "bus", io_base, + &sun6i_isp_regmap_config); + if (IS_ERR(isp_dev->regmap)) { + dev_err(dev, "failed to init register map\n"); + return PTR_ERR(isp_dev->regmap); + } + + /* Clocks */ + + isp_dev->clock_mod = devm_clk_get(dev, "mod"); + if (IS_ERR(isp_dev->clock_mod)) { + dev_err(dev, "failed to acquire module clock\n"); + return PTR_ERR(isp_dev->clock_mod); + } + + isp_dev->clock_ram = devm_clk_get(dev, "ram"); + if (IS_ERR(isp_dev->clock_ram)) { + dev_err(dev, "failed to acquire ram clock\n"); + return PTR_ERR(isp_dev->clock_ram); + } + + ret = clk_set_rate_exclusive(isp_dev->clock_mod, 297000000); + if (ret) { + dev_err(dev, "failed to set mod clock rate\n"); + return ret; + } + + /* Reset */ + + isp_dev->reset = devm_reset_control_get_shared(dev, NULL); + if (IS_ERR(isp_dev->reset)) { + dev_err(dev, "failed to acquire reset\n"); + ret = PTR_ERR(isp_dev->reset); + goto error_clock_rate_exclusive; + } + + /* Interrupt */ + + irq = platform_get_irq(platform_dev, 0); + if (irq < 0) { + dev_err(dev, "failed to get interrupt\n"); + ret = -ENXIO; + goto error_clock_rate_exclusive; + } + + ret = devm_request_irq(dev, irq, sun6i_isp_interrupt, IRQF_SHARED, + SUN6I_ISP_NAME, isp_dev); + if (ret) { + dev_err(dev, "failed to request interrupt\n"); + goto error_clock_rate_exclusive; + } + + /* Runtime PM */ + + pm_runtime_enable(dev); + + return 0; + +error_clock_rate_exclusive: + clk_rate_exclusive_put(isp_dev->clock_mod); + + return ret; +} + +static void sun6i_isp_resources_cleanup(struct sun6i_isp_device *isp_dev) +{ + struct device *dev = isp_dev->dev; + + pm_runtime_disable(dev); + clk_rate_exclusive_put(isp_dev->clock_mod); +} + +static int sun6i_isp_probe(struct platform_device *platform_dev) +{ + struct sun6i_isp_device *isp_dev; + struct device *dev = &platform_dev->dev; + const struct sun6i_isp_variant *variant; + int ret; + + variant = of_device_get_match_data(dev); + if (!variant) + return -EINVAL; + + isp_dev = devm_kzalloc(dev, sizeof(*isp_dev), GFP_KERNEL); + if (!isp_dev) + return -ENOMEM; + + isp_dev->dev = dev; + platform_set_drvdata(platform_dev, isp_dev); + + spin_lock_init(&isp_dev->state_lock); + + ret = sun6i_isp_resources_setup(isp_dev, platform_dev); + if (ret) + return ret; + + ret = sun6i_isp_tables_setup(isp_dev, variant); + if (ret) { + dev_err(dev, "failed to setup tables\n"); + goto error_resources; + } + + ret = sun6i_isp_v4l2_setup(isp_dev); + if (ret) { + dev_err(dev, "failed to setup v4l2\n"); + goto error_tables; + } + + ret = sun6i_isp_proc_setup(isp_dev); + if (ret) { + dev_err(dev, "failed to setup proc\n"); + goto error_v4l2; + } + + ret = sun6i_isp_capture_setup(isp_dev); + if (ret) { + dev_err(dev, "failed to setup capture\n"); + goto error_proc; + } + + ret = sun6i_isp_params_setup(isp_dev); + if (ret) { + dev_err(dev, "failed to setup params\n"); + goto error_capture; + } + + return 0; + +error_capture: + sun6i_isp_capture_cleanup(isp_dev); + +error_proc: + sun6i_isp_proc_cleanup(isp_dev); + +error_v4l2: + sun6i_isp_v4l2_cleanup(isp_dev); + +error_tables: + sun6i_isp_tables_cleanup(isp_dev); + +error_resources: + sun6i_isp_resources_cleanup(isp_dev); + + return ret; +} + +static int sun6i_isp_remove(struct platform_device *platform_dev) +{ + struct sun6i_isp_device *isp_dev = platform_get_drvdata(platform_dev); + + sun6i_isp_params_cleanup(isp_dev); + sun6i_isp_capture_cleanup(isp_dev); + sun6i_isp_proc_cleanup(isp_dev); + sun6i_isp_v4l2_cleanup(isp_dev); + sun6i_isp_tables_cleanup(isp_dev); + sun6i_isp_resources_cleanup(isp_dev); + + return 0; +} + +/* + * History of sun6i-isp: + * - sun4i-a10-isp: initial ISP tied to the CSI0 controller, + * apparently unused in software implementations; + * - sun6i-a31-isp: separate ISP loosely based on sun4i-a10-isp, + * adding extra modules and features; + * - sun9i-a80-isp: based on sun6i-a31-isp with some register offset changes + * and new modules like saturation and cnr; + * - sun8i-a23-isp/sun8i-h3-isp: based on sun9i-a80-isp with most modules + * related to raw removed; + * - sun8i-a83t-isp: based on sun9i-a80-isp with some register offset changes + * - sun8i-v3s-isp: based on sun8i-a83t-isp with a new disc module; + */ + +static const struct sun6i_isp_variant sun8i_v3s_isp_variant = { + .table_load_save_size = 0x1000, + .table_lut_size = 0xe00, + .table_drc_size = 0x600, + .table_stats_size = 0x2100, +}; + +static const struct of_device_id sun6i_isp_of_match[] = { + { + .compatible = "allwinner,sun8i-v3s-isp", + .data = &sun8i_v3s_isp_variant, + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, sun6i_isp_of_match); + +static struct platform_driver sun6i_isp_platform_driver = { + .probe = sun6i_isp_probe, + .remove = sun6i_isp_remove, + .driver = { + .name = SUN6I_ISP_NAME, + .of_match_table = of_match_ptr(sun6i_isp_of_match), + .pm = &sun6i_isp_pm_ops, + }, +}; + +module_platform_driver(sun6i_isp_platform_driver); + +MODULE_DESCRIPTION("Allwinner A31 Image Signal Processor driver"); +MODULE_AUTHOR("Paul Kocialkowski <[email protected]>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.h b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.h new file mode 100644 index 000000000000..0e5f188319ff --- /dev/null +++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2021-2022 Bootlin + * Author: Paul Kocialkowski <[email protected]> + */ + +#ifndef _SUN6I_ISP_H_ +#define _SUN6I_ISP_H_ + +#include <media/v4l2-device.h> +#include <media/videobuf2-v4l2.h> + +#include "sun6i_isp_capture.h" +#include "sun6i_isp_params.h" +#include "sun6i_isp_proc.h" + +#define SUN6I_ISP_NAME "sun6i-isp" +#define SUN6I_ISP_DESCRIPTION "Allwinner A31 ISP Device" + +enum sun6i_isp_port { + SUN6I_ISP_PORT_CSI0 = 0, + SUN6I_ISP_PORT_CSI1 = 1, +}; + +struct sun6i_isp_buffer { + struct vb2_v4l2_buffer v4l2_buffer; + struct list_head list; +}; + +struct sun6i_isp_v4l2 { + struct v4l2_device v4l2_dev; + struct media_device media_dev; +}; + +struct sun6i_isp_table { + void *data; + dma_addr_t address; + unsigned int size; +}; + +struct sun6i_isp_tables { + struct sun6i_isp_table load; + struct sun6i_isp_table save; + + struct sun6i_isp_table lut; + struct sun6i_isp_table drc; + struct sun6i_isp_table stats; +}; + +struct sun6i_isp_device { + struct device *dev; + + struct sun6i_isp_tables tables; + + struct sun6i_isp_v4l2 v4l2; + struct sun6i_isp_proc proc; + struct sun6i_isp_capture capture; + struct sun6i_isp_params params; + + struct regmap *regmap; + struct clk *clock_mod; + struct clk *clock_ram; + struct reset_control *reset; + + spinlock_t state_lock; /* State helpers lock. */ +}; + +struct sun6i_isp_variant { + unsigned int table_load_save_size; + unsigned int table_lut_size; + unsigned int table_drc_size; + unsigned int table_stats_size; +}; + +/* Helpers */ + +u32 sun6i_isp_load_read(struct sun6i_isp_device *isp_dev, u32 offset); +void sun6i_isp_load_write(struct sun6i_isp_device *isp_dev, u32 offset, + u32 value); +u32 sun6i_isp_address_value(dma_addr_t address); + +/* State */ + +void sun6i_isp_state_update(struct sun6i_isp_device *isp_dev, bool ready_hold); + +/* Tables */ + +void sun6i_isp_tables_configure(struct sun6i_isp_device *isp_dev); + +#endif diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_capture.c b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_capture.c new file mode 100644 index 000000000000..4b592820845a --- /dev/null +++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_capture.c @@ -0,0 +1,742 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021-2022 Bootlin + * Author: Paul Kocialkowski <[email protected]> + */ + +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-mc.h> +#include <media/videobuf2-dma-contig.h> +#include <media/videobuf2-v4l2.h> + +#include "sun6i_isp.h" +#include "sun6i_isp_capture.h" +#include "sun6i_isp_proc.h" +#include "sun6i_isp_reg.h" + +/* Helpers */ + +void sun6i_isp_capture_dimensions(struct sun6i_isp_device *isp_dev, + unsigned int *width, unsigned int *height) +{ + if (width) + *width = isp_dev->capture.format.fmt.pix.width; + if (height) + *height = isp_dev->capture.format.fmt.pix.height; +} + +void sun6i_isp_capture_format(struct sun6i_isp_device *isp_dev, + u32 *pixelformat) +{ + if (pixelformat) + *pixelformat = isp_dev->capture.format.fmt.pix.pixelformat; +} + +/* Format */ + +static const struct sun6i_isp_capture_format sun6i_isp_capture_formats[] = { + { + .pixelformat = V4L2_PIX_FMT_NV12, + .output_format = SUN6I_ISP_OUTPUT_FMT_YUV420SP, + }, + { + .pixelformat = V4L2_PIX_FMT_NV21, + .output_format = SUN6I_ISP_OUTPUT_FMT_YVU420SP, + }, +}; + +const struct sun6i_isp_capture_format * +sun6i_isp_capture_format_find(u32 pixelformat) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(sun6i_isp_capture_formats); i++) + if (sun6i_isp_capture_formats[i].pixelformat == pixelformat) + return &sun6i_isp_capture_formats[i]; + + return NULL; +} + +/* Capture */ + +static void +sun6i_isp_capture_buffer_configure(struct sun6i_isp_device *isp_dev, + struct sun6i_isp_buffer *isp_buffer) +{ + const struct v4l2_format_info *info; + struct vb2_buffer *vb2_buffer; + unsigned int width, height; + unsigned int width_aligned; + dma_addr_t address; + u32 pixelformat; + + vb2_buffer = &isp_buffer->v4l2_buffer.vb2_buf; + address = vb2_dma_contig_plane_dma_addr(vb2_buffer, 0); + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_MCH_Y_ADDR0_REG, + SUN6I_ISP_ADDR_VALUE(address)); + + sun6i_isp_capture_dimensions(isp_dev, &width, &height); + sun6i_isp_capture_format(isp_dev, &pixelformat); + + info = v4l2_format_info(pixelformat); + if (WARN_ON(!info)) + return; + + /* Stride needs to be aligned to 4. */ + width_aligned = ALIGN(width, 2); + + if (info->comp_planes > 1) { + address += info->bpp[0] * width_aligned * height; + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_MCH_U_ADDR0_REG, + SUN6I_ISP_ADDR_VALUE(address)); + } + + if (info->comp_planes > 2) { + address += info->bpp[1] * + DIV_ROUND_UP(width_aligned, info->hdiv) * + DIV_ROUND_UP(height, info->vdiv); + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_MCH_V_ADDR0_REG, + SUN6I_ISP_ADDR_VALUE(address)); + } +} + +void sun6i_isp_capture_configure(struct sun6i_isp_device *isp_dev) +{ + unsigned int width, height; + unsigned int stride_luma, stride_chroma = 0; + unsigned int stride_luma_div4, stride_chroma_div4; + const struct sun6i_isp_capture_format *format; + const struct v4l2_format_info *info; + u32 pixelformat; + + sun6i_isp_capture_dimensions(isp_dev, &width, &height); + sun6i_isp_capture_format(isp_dev, &pixelformat); + + format = sun6i_isp_capture_format_find(pixelformat); + if (WARN_ON(!format)) + return; + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_MCH_SIZE_CFG_REG, + SUN6I_ISP_MCH_SIZE_CFG_WIDTH(width) | + SUN6I_ISP_MCH_SIZE_CFG_HEIGHT(height)); + + info = v4l2_format_info(pixelformat); + if (WARN_ON(!info)) + return; + + stride_luma = width * info->bpp[0]; + stride_luma_div4 = DIV_ROUND_UP(stride_luma, 4); + + if (info->comp_planes > 1) { + stride_chroma = width * info->bpp[1] / info->hdiv; + stride_chroma_div4 = DIV_ROUND_UP(stride_chroma, 4); + } + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_MCH_CFG_REG, + SUN6I_ISP_MCH_CFG_EN | + SUN6I_ISP_MCH_CFG_OUTPUT_FMT(format->output_format) | + SUN6I_ISP_MCH_CFG_STRIDE_Y_DIV4(stride_luma_div4) | + SUN6I_ISP_MCH_CFG_STRIDE_UV_DIV4(stride_chroma_div4)); +} + +/* State */ + +static void sun6i_isp_capture_state_cleanup(struct sun6i_isp_device *isp_dev, + bool error) +{ + struct sun6i_isp_capture_state *state = &isp_dev->capture.state; + struct sun6i_isp_buffer **isp_buffer_states[] = { + &state->pending, &state->current, &state->complete, + }; + struct sun6i_isp_buffer *isp_buffer; + struct vb2_buffer *vb2_buffer; + unsigned long flags; + unsigned int i; + + spin_lock_irqsave(&state->lock, flags); + + for (i = 0; i < ARRAY_SIZE(isp_buffer_states); i++) { + isp_buffer = *isp_buffer_states[i]; + if (!isp_buffer) + continue; + + vb2_buffer = &isp_buffer->v4l2_buffer.vb2_buf; + vb2_buffer_done(vb2_buffer, error ? VB2_BUF_STATE_ERROR : + VB2_BUF_STATE_QUEUED); + + *isp_buffer_states[i] = NULL; + } + + list_for_each_entry(isp_buffer, &state->queue, list) { + vb2_buffer = &isp_buffer->v4l2_buffer.vb2_buf; + vb2_buffer_done(vb2_buffer, error ? VB2_BUF_STATE_ERROR : + VB2_BUF_STATE_QUEUED); + } + + INIT_LIST_HEAD(&state->queue); + + spin_unlock_irqrestore(&state->lock, flags); +} + +void sun6i_isp_capture_state_update(struct sun6i_isp_device *isp_dev, + bool *update) +{ + struct sun6i_isp_capture_state *state = &isp_dev->capture.state; + struct sun6i_isp_buffer *isp_buffer; + unsigned long flags; + + spin_lock_irqsave(&state->lock, flags); + + if (list_empty(&state->queue)) + goto complete; + + if (state->pending) + goto complete; + + isp_buffer = list_first_entry(&state->queue, struct sun6i_isp_buffer, + list); + + sun6i_isp_capture_buffer_configure(isp_dev, isp_buffer); + + list_del(&isp_buffer->list); + + state->pending = isp_buffer; + + if (update) + *update = true; + +complete: + spin_unlock_irqrestore(&state->lock, flags); +} + +void sun6i_isp_capture_state_complete(struct sun6i_isp_device *isp_dev) +{ + struct sun6i_isp_capture_state *state = &isp_dev->capture.state; + unsigned long flags; + + spin_lock_irqsave(&state->lock, flags); + + if (!state->pending) + goto complete; + + state->complete = state->current; + state->current = state->pending; + state->pending = NULL; + + if (state->complete) { + struct sun6i_isp_buffer *isp_buffer = state->complete; + struct vb2_buffer *vb2_buffer = + &isp_buffer->v4l2_buffer.vb2_buf; + + vb2_buffer->timestamp = ktime_get_ns(); + isp_buffer->v4l2_buffer.sequence = state->sequence; + + vb2_buffer_done(vb2_buffer, VB2_BUF_STATE_DONE); + + state->complete = NULL; + } + +complete: + spin_unlock_irqrestore(&state->lock, flags); +} + +void sun6i_isp_capture_finish(struct sun6i_isp_device *isp_dev) +{ + struct sun6i_isp_capture_state *state = &isp_dev->capture.state; + unsigned long flags; + + spin_lock_irqsave(&state->lock, flags); + state->sequence++; + spin_unlock_irqrestore(&state->lock, flags); +} + +/* Queue */ + +static int sun6i_isp_capture_queue_setup(struct vb2_queue *queue, + unsigned int *buffers_count, + unsigned int *planes_count, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct sun6i_isp_device *isp_dev = vb2_get_drv_priv(queue); + unsigned int size = isp_dev->capture.format.fmt.pix.sizeimage; + + if (*planes_count) + return sizes[0] < size ? -EINVAL : 0; + + *planes_count = 1; + sizes[0] = size; + + return 0; +} + +static int sun6i_isp_capture_buffer_prepare(struct vb2_buffer *vb2_buffer) +{ + struct sun6i_isp_device *isp_dev = + vb2_get_drv_priv(vb2_buffer->vb2_queue); + struct v4l2_device *v4l2_dev = &isp_dev->v4l2.v4l2_dev; + unsigned int size = isp_dev->capture.format.fmt.pix.sizeimage; + + if (vb2_plane_size(vb2_buffer, 0) < size) { + v4l2_err(v4l2_dev, "buffer too small (%lu < %u)\n", + vb2_plane_size(vb2_buffer, 0), size); + return -EINVAL; + } + + vb2_set_plane_payload(vb2_buffer, 0, size); + + return 0; +} + +static void sun6i_isp_capture_buffer_queue(struct vb2_buffer *vb2_buffer) +{ + struct sun6i_isp_device *isp_dev = + vb2_get_drv_priv(vb2_buffer->vb2_queue); + struct sun6i_isp_capture_state *state = &isp_dev->capture.state; + struct vb2_v4l2_buffer *v4l2_buffer = to_vb2_v4l2_buffer(vb2_buffer); + struct sun6i_isp_buffer *isp_buffer = + container_of(v4l2_buffer, struct sun6i_isp_buffer, v4l2_buffer); + unsigned long flags; + + spin_lock_irqsave(&state->lock, flags); + list_add_tail(&isp_buffer->list, &state->queue); + spin_unlock_irqrestore(&state->lock, flags); + + /* Update the state to schedule our buffer as soon as possible. */ + if (state->streaming) + sun6i_isp_state_update(isp_dev, false); +} + +static int sun6i_isp_capture_start_streaming(struct vb2_queue *queue, + unsigned int count) +{ + struct sun6i_isp_device *isp_dev = vb2_get_drv_priv(queue); + struct sun6i_isp_capture_state *state = &isp_dev->capture.state; + struct video_device *video_dev = &isp_dev->capture.video_dev; + struct v4l2_subdev *subdev = &isp_dev->proc.subdev; + int ret; + + state->sequence = 0; + + ret = video_device_pipeline_alloc_start(video_dev); + if (ret < 0) + goto error_state; + + state->streaming = true; + + ret = v4l2_subdev_call(subdev, video, s_stream, 1); + if (ret && ret != -ENOIOCTLCMD) + goto error_streaming; + + return 0; + +error_streaming: + state->streaming = false; + + video_device_pipeline_stop(video_dev); + +error_state: + sun6i_isp_capture_state_cleanup(isp_dev, false); + + return ret; +} + +static void sun6i_isp_capture_stop_streaming(struct vb2_queue *queue) +{ + struct sun6i_isp_device *isp_dev = vb2_get_drv_priv(queue); + struct sun6i_isp_capture_state *state = &isp_dev->capture.state; + struct video_device *video_dev = &isp_dev->capture.video_dev; + struct v4l2_subdev *subdev = &isp_dev->proc.subdev; + + v4l2_subdev_call(subdev, video, s_stream, 0); + + state->streaming = false; + + video_device_pipeline_stop(video_dev); + + sun6i_isp_capture_state_cleanup(isp_dev, true); +} + +static const struct vb2_ops sun6i_isp_capture_queue_ops = { + .queue_setup = sun6i_isp_capture_queue_setup, + .buf_prepare = sun6i_isp_capture_buffer_prepare, + .buf_queue = sun6i_isp_capture_buffer_queue, + .start_streaming = sun6i_isp_capture_start_streaming, + .stop_streaming = sun6i_isp_capture_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +/* Video Device */ + +static void sun6i_isp_capture_format_prepare(struct v4l2_format *format) +{ + struct v4l2_pix_format *pix_format = &format->fmt.pix; + const struct v4l2_format_info *info; + unsigned int width, height; + unsigned int width_aligned; + unsigned int i; + + v4l_bound_align_image(&pix_format->width, SUN6I_ISP_CAPTURE_WIDTH_MIN, + SUN6I_ISP_CAPTURE_WIDTH_MAX, 1, + &pix_format->height, SUN6I_ISP_CAPTURE_HEIGHT_MIN, + SUN6I_ISP_CAPTURE_HEIGHT_MAX, 1, 0); + + if (!sun6i_isp_capture_format_find(pix_format->pixelformat)) + pix_format->pixelformat = + sun6i_isp_capture_formats[0].pixelformat; + + info = v4l2_format_info(pix_format->pixelformat); + if (WARN_ON(!info)) + return; + + width = pix_format->width; + height = pix_format->height; + + /* Stride needs to be aligned to 4. */ + width_aligned = ALIGN(width, 2); + + pix_format->bytesperline = width_aligned * info->bpp[0]; + pix_format->sizeimage = 0; + + for (i = 0; i < info->comp_planes; i++) { + unsigned int hdiv = (i == 0) ? 1 : info->hdiv; + unsigned int vdiv = (i == 0) ? 1 : info->vdiv; + + pix_format->sizeimage += info->bpp[i] * + DIV_ROUND_UP(width_aligned, hdiv) * + DIV_ROUND_UP(height, vdiv); + } + + pix_format->field = V4L2_FIELD_NONE; + + pix_format->colorspace = V4L2_COLORSPACE_RAW; + pix_format->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + pix_format->quantization = V4L2_QUANTIZATION_DEFAULT; + pix_format->xfer_func = V4L2_XFER_FUNC_DEFAULT; +} + +static int sun6i_isp_capture_querycap(struct file *file, void *private, + struct v4l2_capability *capability) +{ + struct sun6i_isp_device *isp_dev = video_drvdata(file); + struct video_device *video_dev = &isp_dev->capture.video_dev; + + strscpy(capability->driver, SUN6I_ISP_NAME, sizeof(capability->driver)); + strscpy(capability->card, video_dev->name, sizeof(capability->card)); + snprintf(capability->bus_info, sizeof(capability->bus_info), + "platform:%s", dev_name(isp_dev->dev)); + + return 0; +} + +static int sun6i_isp_capture_enum_fmt(struct file *file, void *private, + struct v4l2_fmtdesc *fmtdesc) +{ + u32 index = fmtdesc->index; + + if (index >= ARRAY_SIZE(sun6i_isp_capture_formats)) + return -EINVAL; + + fmtdesc->pixelformat = sun6i_isp_capture_formats[index].pixelformat; + + return 0; +} + +static int sun6i_isp_capture_g_fmt(struct file *file, void *private, + struct v4l2_format *format) +{ + struct sun6i_isp_device *isp_dev = video_drvdata(file); + + *format = isp_dev->capture.format; + + return 0; +} + +static int sun6i_isp_capture_s_fmt(struct file *file, void *private, + struct v4l2_format *format) +{ + struct sun6i_isp_device *isp_dev = video_drvdata(file); + + if (vb2_is_busy(&isp_dev->capture.queue)) + return -EBUSY; + + sun6i_isp_capture_format_prepare(format); + + isp_dev->capture.format = *format; + + return 0; +} + +static int sun6i_isp_capture_try_fmt(struct file *file, void *private, + struct v4l2_format *format) +{ + sun6i_isp_capture_format_prepare(format); + + return 0; +} + +static int sun6i_isp_capture_enum_input(struct file *file, void *private, + struct v4l2_input *input) +{ + if (input->index != 0) + return -EINVAL; + + input->type = V4L2_INPUT_TYPE_CAMERA; + strscpy(input->name, "Camera", sizeof(input->name)); + + return 0; +} + +static int sun6i_isp_capture_g_input(struct file *file, void *private, + unsigned int *index) +{ + *index = 0; + + return 0; +} + +static int sun6i_isp_capture_s_input(struct file *file, void *private, + unsigned int index) +{ + if (index != 0) + return -EINVAL; + + return 0; +} + +static const struct v4l2_ioctl_ops sun6i_isp_capture_ioctl_ops = { + .vidioc_querycap = sun6i_isp_capture_querycap, + + .vidioc_enum_fmt_vid_cap = sun6i_isp_capture_enum_fmt, + .vidioc_g_fmt_vid_cap = sun6i_isp_capture_g_fmt, + .vidioc_s_fmt_vid_cap = sun6i_isp_capture_s_fmt, + .vidioc_try_fmt_vid_cap = sun6i_isp_capture_try_fmt, + + .vidioc_enum_input = sun6i_isp_capture_enum_input, + .vidioc_g_input = sun6i_isp_capture_g_input, + .vidioc_s_input = sun6i_isp_capture_s_input, + + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +}; + +static int sun6i_isp_capture_open(struct file *file) +{ + struct sun6i_isp_device *isp_dev = video_drvdata(file); + struct video_device *video_dev = &isp_dev->capture.video_dev; + struct mutex *lock = &isp_dev->capture.lock; + int ret; + + if (mutex_lock_interruptible(lock)) + return -ERESTARTSYS; + + ret = v4l2_pipeline_pm_get(&video_dev->entity); + if (ret) + goto error_mutex; + + ret = v4l2_fh_open(file); + if (ret) + goto error_pipeline; + + mutex_unlock(lock); + + return 0; + +error_pipeline: + v4l2_pipeline_pm_put(&video_dev->entity); + +error_mutex: + mutex_unlock(lock); + + return ret; +} + +static int sun6i_isp_capture_release(struct file *file) +{ + struct sun6i_isp_device *isp_dev = video_drvdata(file); + struct video_device *video_dev = &isp_dev->capture.video_dev; + struct mutex *lock = &isp_dev->capture.lock; + + mutex_lock(lock); + + _vb2_fop_release(file, NULL); + v4l2_pipeline_pm_put(&video_dev->entity); + + mutex_unlock(lock); + + return 0; +} + +static const struct v4l2_file_operations sun6i_isp_capture_fops = { + .owner = THIS_MODULE, + .open = sun6i_isp_capture_open, + .release = sun6i_isp_capture_release, + .unlocked_ioctl = video_ioctl2, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, +}; + +/* Media Entity */ + +static int sun6i_isp_capture_link_validate(struct media_link *link) +{ + struct video_device *video_dev = + media_entity_to_video_device(link->sink->entity); + struct sun6i_isp_device *isp_dev = video_get_drvdata(video_dev); + struct v4l2_device *v4l2_dev = &isp_dev->v4l2.v4l2_dev; + unsigned int capture_width, capture_height; + unsigned int proc_width, proc_height; + + sun6i_isp_capture_dimensions(isp_dev, &capture_width, &capture_height); + sun6i_isp_proc_dimensions(isp_dev, &proc_width, &proc_height); + + /* No cropping/scaling is supported (yet). */ + if (capture_width != proc_width || capture_height != proc_height) { + v4l2_err(v4l2_dev, + "invalid input/output dimensions: %ux%u/%ux%u\n", + proc_width, proc_height, capture_width, + capture_height); + return -EINVAL; + } + + return 0; +} + +static const struct media_entity_operations sun6i_isp_capture_entity_ops = { + .link_validate = sun6i_isp_capture_link_validate, +}; + +/* Capture */ + +int sun6i_isp_capture_setup(struct sun6i_isp_device *isp_dev) +{ + struct sun6i_isp_capture *capture = &isp_dev->capture; + struct sun6i_isp_capture_state *state = &capture->state; + struct v4l2_device *v4l2_dev = &isp_dev->v4l2.v4l2_dev; + struct v4l2_subdev *proc_subdev = &isp_dev->proc.subdev; + struct video_device *video_dev = &capture->video_dev; + struct vb2_queue *queue = &capture->queue; + struct media_pad *pad = &capture->pad; + struct v4l2_format *format = &capture->format; + struct v4l2_pix_format *pix_format = &format->fmt.pix; + int ret; + + /* State */ + + INIT_LIST_HEAD(&state->queue); + spin_lock_init(&state->lock); + + /* Media Entity */ + + video_dev->entity.ops = &sun6i_isp_capture_entity_ops; + + /* Media Pads */ + + pad->flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; + + ret = media_entity_pads_init(&video_dev->entity, 1, pad); + if (ret) + goto error_mutex; + + /* Queue */ + + mutex_init(&capture->lock); + + queue->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + queue->io_modes = VB2_MMAP | VB2_DMABUF; + queue->buf_struct_size = sizeof(struct sun6i_isp_buffer); + queue->ops = &sun6i_isp_capture_queue_ops; + queue->mem_ops = &vb2_dma_contig_memops; + queue->min_buffers_needed = 2; + queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + queue->lock = &capture->lock; + queue->dev = isp_dev->dev; + queue->drv_priv = isp_dev; + + ret = vb2_queue_init(queue); + if (ret) { + v4l2_err(v4l2_dev, "failed to initialize vb2 queue: %d\n", ret); + goto error_media_entity; + } + + /* V4L2 Format */ + + format->type = queue->type; + pix_format->pixelformat = sun6i_isp_capture_formats[0].pixelformat; + pix_format->width = 1280; + pix_format->height = 720; + + sun6i_isp_capture_format_prepare(format); + + /* Video Device */ + + strscpy(video_dev->name, SUN6I_ISP_CAPTURE_NAME, + sizeof(video_dev->name)); + video_dev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + video_dev->vfl_dir = VFL_DIR_RX; + video_dev->release = video_device_release_empty; + video_dev->fops = &sun6i_isp_capture_fops; + video_dev->ioctl_ops = &sun6i_isp_capture_ioctl_ops; + video_dev->v4l2_dev = v4l2_dev; + video_dev->queue = queue; + video_dev->lock = &capture->lock; + + video_set_drvdata(video_dev, isp_dev); + + ret = video_register_device(video_dev, VFL_TYPE_VIDEO, -1); + if (ret) { + v4l2_err(v4l2_dev, "failed to register video device: %d\n", + ret); + goto error_media_entity; + } + + /* Media Pad Link */ + + ret = media_create_pad_link(&proc_subdev->entity, + SUN6I_ISP_PROC_PAD_SOURCE, + &video_dev->entity, 0, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret < 0) { + v4l2_err(v4l2_dev, "failed to create %s:%u -> %s:%u link\n", + proc_subdev->entity.name, SUN6I_ISP_PROC_PAD_SOURCE, + video_dev->entity.name, 0); + goto error_video_device; + } + + return 0; + +error_video_device: + vb2_video_unregister_device(video_dev); + +error_media_entity: + media_entity_cleanup(&video_dev->entity); + +error_mutex: + mutex_destroy(&capture->lock); + + return ret; +} + +void sun6i_isp_capture_cleanup(struct sun6i_isp_device *isp_dev) +{ + struct sun6i_isp_capture *capture = &isp_dev->capture; + struct video_device *video_dev = &capture->video_dev; + + vb2_video_unregister_device(video_dev); + media_entity_cleanup(&video_dev->entity); + mutex_destroy(&capture->lock); +} diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_capture.h b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_capture.h new file mode 100644 index 000000000000..0e3e4fa7a0f4 --- /dev/null +++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_capture.h @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2021-2022 Bootlin + * Author: Paul Kocialkowski <[email protected]> + */ + +#ifndef _SUN6I_ISP_CAPTURE_H_ +#define _SUN6I_ISP_CAPTURE_H_ + +#include <media/v4l2-device.h> + +#define SUN6I_ISP_CAPTURE_NAME "sun6i-isp-capture" + +#define SUN6I_ISP_CAPTURE_WIDTH_MIN 16 +#define SUN6I_ISP_CAPTURE_WIDTH_MAX 3264 +#define SUN6I_ISP_CAPTURE_HEIGHT_MIN 16 +#define SUN6I_ISP_CAPTURE_HEIGHT_MAX 2448 + +struct sun6i_isp_device; + +struct sun6i_isp_capture_format { + u32 pixelformat; + u8 output_format; +}; + +#undef current +struct sun6i_isp_capture_state { + struct list_head queue; + spinlock_t lock; /* Queue and buffers lock. */ + + struct sun6i_isp_buffer *pending; + struct sun6i_isp_buffer *current; + struct sun6i_isp_buffer *complete; + + unsigned int sequence; + bool streaming; +}; + +struct sun6i_isp_capture { + struct sun6i_isp_capture_state state; + + struct video_device video_dev; + struct vb2_queue queue; + struct mutex lock; /* Queue lock. */ + struct media_pad pad; + + struct v4l2_format format; +}; + +/* Helpers */ + +void sun6i_isp_capture_dimensions(struct sun6i_isp_device *isp_dev, + unsigned int *width, unsigned int *height); +void sun6i_isp_capture_format(struct sun6i_isp_device *isp_dev, + u32 *pixelformat); + +/* Format */ + +const struct sun6i_isp_capture_format * +sun6i_isp_capture_format_find(u32 pixelformat); + +/* Capture */ + +void sun6i_isp_capture_configure(struct sun6i_isp_device *isp_dev); + +/* State */ + +void sun6i_isp_capture_state_update(struct sun6i_isp_device *isp_dev, + bool *update); +void sun6i_isp_capture_state_complete(struct sun6i_isp_device *isp_dev); +void sun6i_isp_capture_finish(struct sun6i_isp_device *isp_dev); + +/* Capture */ + +int sun6i_isp_capture_setup(struct sun6i_isp_device *isp_dev); +void sun6i_isp_capture_cleanup(struct sun6i_isp_device *isp_dev); + +#endif diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_params.c b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_params.c new file mode 100644 index 000000000000..8039e311cb1c --- /dev/null +++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_params.c @@ -0,0 +1,566 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021-2022 Bootlin + * Author: Paul Kocialkowski <[email protected]> + */ + +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-mc.h> +#include <media/videobuf2-vmalloc.h> +#include <media/videobuf2-v4l2.h> + +#include "sun6i_isp.h" +#include "sun6i_isp_params.h" +#include "sun6i_isp_reg.h" +#include "uapi/sun6i-isp-config.h" + +/* Params */ + +static const struct sun6i_isp_params_config sun6i_isp_params_config_default = { + .modules_used = SUN6I_ISP_MODULE_BAYER, + + .bayer = { + .offset_r = 32, + .offset_gr = 32, + .offset_gb = 32, + .offset_b = 32, + + .gain_r = 256, + .gain_gr = 256, + .gain_gb = 256, + .gain_b = 256, + + }, + + .bdnf = { + .in_dis_min = 8, + .in_dis_max = 16, + + .coefficients_g = { 15, 4, 1 }, + .coefficients_rb = { 15, 4 }, + }, +}; + +static void sun6i_isp_params_configure_ob(struct sun6i_isp_device *isp_dev) +{ + unsigned int width, height; + + sun6i_isp_proc_dimensions(isp_dev, &width, &height); + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_OB_SIZE_REG, + SUN6I_ISP_OB_SIZE_WIDTH(width) | + SUN6I_ISP_OB_SIZE_HEIGHT(height)); + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_OB_VALID_REG, + SUN6I_ISP_OB_VALID_WIDTH(width) | + SUN6I_ISP_OB_VALID_HEIGHT(height)); + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_OB_SRC0_VALID_START_REG, + SUN6I_ISP_OB_SRC0_VALID_START_HORZ(0) | + SUN6I_ISP_OB_SRC0_VALID_START_VERT(0)); +} + +static void sun6i_isp_params_configure_ae(struct sun6i_isp_device *isp_dev) +{ + /* These are default values that need to be set to get an output. */ + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_AE_CFG_REG, + SUN6I_ISP_AE_CFG_LOW_BRI_TH(0xff) | + SUN6I_ISP_AE_CFG_HORZ_NUM(8) | + SUN6I_ISP_AE_CFG_HIGH_BRI_TH(0xf00) | + SUN6I_ISP_AE_CFG_VERT_NUM(8)); +} + +static void +sun6i_isp_params_configure_bayer(struct sun6i_isp_device *isp_dev, + const struct sun6i_isp_params_config *config) +{ + const struct sun6i_isp_params_config_bayer *bayer = &config->bayer; + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_BAYER_OFFSET0_REG, + SUN6I_ISP_BAYER_OFFSET0_R(bayer->offset_r) | + SUN6I_ISP_BAYER_OFFSET0_GR(bayer->offset_gr)); + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_BAYER_OFFSET1_REG, + SUN6I_ISP_BAYER_OFFSET1_GB(bayer->offset_gb) | + SUN6I_ISP_BAYER_OFFSET1_B(bayer->offset_b)); + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_BAYER_GAIN0_REG, + SUN6I_ISP_BAYER_GAIN0_R(bayer->gain_r) | + SUN6I_ISP_BAYER_GAIN0_GR(bayer->gain_gr)); + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_BAYER_GAIN1_REG, + SUN6I_ISP_BAYER_GAIN1_GB(bayer->gain_gb) | + SUN6I_ISP_BAYER_GAIN1_B(bayer->gain_b)); +} + +static void sun6i_isp_params_configure_wb(struct sun6i_isp_device *isp_dev) +{ + /* These are default values that need to be set to get an output. */ + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_WB_GAIN0_REG, + SUN6I_ISP_WB_GAIN0_R(256) | + SUN6I_ISP_WB_GAIN0_GR(256)); + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_WB_GAIN1_REG, + SUN6I_ISP_WB_GAIN1_GB(256) | + SUN6I_ISP_WB_GAIN1_B(256)); + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_WB_CFG_REG, + SUN6I_ISP_WB_CFG_CLIP(0xfff)); +} + +static void sun6i_isp_params_configure_base(struct sun6i_isp_device *isp_dev) +{ + sun6i_isp_params_configure_ae(isp_dev); + sun6i_isp_params_configure_ob(isp_dev); + sun6i_isp_params_configure_wb(isp_dev); +} + +static void +sun6i_isp_params_configure_bdnf(struct sun6i_isp_device *isp_dev, + const struct sun6i_isp_params_config *config) +{ + const struct sun6i_isp_params_config_bdnf *bdnf = &config->bdnf; + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_BDNF_CFG_REG, + SUN6I_ISP_BDNF_CFG_IN_DIS_MIN(bdnf->in_dis_min) | + SUN6I_ISP_BDNF_CFG_IN_DIS_MAX(bdnf->in_dis_max)); + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_BDNF_COEF_RB_REG, + SUN6I_ISP_BDNF_COEF_RB(0, bdnf->coefficients_rb[0]) | + SUN6I_ISP_BDNF_COEF_RB(1, bdnf->coefficients_rb[1]) | + SUN6I_ISP_BDNF_COEF_RB(2, bdnf->coefficients_rb[2]) | + SUN6I_ISP_BDNF_COEF_RB(3, bdnf->coefficients_rb[3]) | + SUN6I_ISP_BDNF_COEF_RB(4, bdnf->coefficients_rb[4])); + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_BDNF_COEF_G_REG, + SUN6I_ISP_BDNF_COEF_G(0, bdnf->coefficients_g[0]) | + SUN6I_ISP_BDNF_COEF_G(1, bdnf->coefficients_g[1]) | + SUN6I_ISP_BDNF_COEF_G(2, bdnf->coefficients_g[2]) | + SUN6I_ISP_BDNF_COEF_G(3, bdnf->coefficients_g[3]) | + SUN6I_ISP_BDNF_COEF_G(4, bdnf->coefficients_g[4]) | + SUN6I_ISP_BDNF_COEF_G(5, bdnf->coefficients_g[5]) | + SUN6I_ISP_BDNF_COEF_G(6, bdnf->coefficients_g[6])); +} + +static void +sun6i_isp_params_configure_modules(struct sun6i_isp_device *isp_dev, + const struct sun6i_isp_params_config *config) +{ + u32 value; + + if (config->modules_used & SUN6I_ISP_MODULE_BDNF) + sun6i_isp_params_configure_bdnf(isp_dev, config); + + if (config->modules_used & SUN6I_ISP_MODULE_BAYER) + sun6i_isp_params_configure_bayer(isp_dev, config); + + value = sun6i_isp_load_read(isp_dev, SUN6I_ISP_MODULE_EN_REG); + /* Clear all modules but keep input configuration. */ + value &= SUN6I_ISP_MODULE_EN_SRC0 | SUN6I_ISP_MODULE_EN_SRC1; + + if (config->modules_used & SUN6I_ISP_MODULE_BDNF) + value |= SUN6I_ISP_MODULE_EN_BDNF; + + /* Bayer stage is always enabled. */ + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_MODULE_EN_REG, value); +} + +void sun6i_isp_params_configure(struct sun6i_isp_device *isp_dev) +{ + struct sun6i_isp_params_state *state = &isp_dev->params.state; + unsigned long flags; + + spin_lock_irqsave(&state->lock, flags); + + sun6i_isp_params_configure_base(isp_dev); + + /* Default config is only applied at the very first stream start. */ + if (state->configured) + goto complete; + + sun6i_isp_params_configure_modules(isp_dev, + &sun6i_isp_params_config_default); + + state->configured = true; + +complete: + spin_unlock_irqrestore(&state->lock, flags); +} + +/* State */ + +static void sun6i_isp_params_state_cleanup(struct sun6i_isp_device *isp_dev, + bool error) +{ + struct sun6i_isp_params_state *state = &isp_dev->params.state; + struct sun6i_isp_buffer *isp_buffer; + struct vb2_buffer *vb2_buffer; + unsigned long flags; + + spin_lock_irqsave(&state->lock, flags); + + if (state->pending) { + vb2_buffer = &state->pending->v4l2_buffer.vb2_buf; + vb2_buffer_done(vb2_buffer, error ? VB2_BUF_STATE_ERROR : + VB2_BUF_STATE_QUEUED); + } + + list_for_each_entry(isp_buffer, &state->queue, list) { + vb2_buffer = &isp_buffer->v4l2_buffer.vb2_buf; + vb2_buffer_done(vb2_buffer, error ? VB2_BUF_STATE_ERROR : + VB2_BUF_STATE_QUEUED); + } + + INIT_LIST_HEAD(&state->queue); + + spin_unlock_irqrestore(&state->lock, flags); +} + +void sun6i_isp_params_state_update(struct sun6i_isp_device *isp_dev, + bool *update) +{ + struct sun6i_isp_params_state *state = &isp_dev->params.state; + struct sun6i_isp_buffer *isp_buffer; + struct vb2_buffer *vb2_buffer; + const struct sun6i_isp_params_config *config; + unsigned long flags; + + spin_lock_irqsave(&state->lock, flags); + + if (list_empty(&state->queue)) + goto complete; + + if (state->pending) + goto complete; + + isp_buffer = list_first_entry(&state->queue, struct sun6i_isp_buffer, + list); + + vb2_buffer = &isp_buffer->v4l2_buffer.vb2_buf; + config = vb2_plane_vaddr(vb2_buffer, 0); + + sun6i_isp_params_configure_modules(isp_dev, config); + + list_del(&isp_buffer->list); + + state->pending = isp_buffer; + + if (update) + *update = true; + +complete: + spin_unlock_irqrestore(&state->lock, flags); +} + +void sun6i_isp_params_state_complete(struct sun6i_isp_device *isp_dev) +{ + struct sun6i_isp_params_state *state = &isp_dev->params.state; + struct sun6i_isp_buffer *isp_buffer; + struct vb2_buffer *vb2_buffer; + unsigned long flags; + + spin_lock_irqsave(&state->lock, flags); + + if (!state->pending) + goto complete; + + isp_buffer = state->pending; + vb2_buffer = &isp_buffer->v4l2_buffer.vb2_buf; + + vb2_buffer->timestamp = ktime_get_ns(); + + /* Parameters will be applied starting from the next frame. */ + isp_buffer->v4l2_buffer.sequence = isp_dev->capture.state.sequence + 1; + + vb2_buffer_done(vb2_buffer, VB2_BUF_STATE_DONE); + + state->pending = NULL; + +complete: + spin_unlock_irqrestore(&state->lock, flags); +} + +/* Queue */ + +static int sun6i_isp_params_queue_setup(struct vb2_queue *queue, + unsigned int *buffers_count, + unsigned int *planes_count, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct sun6i_isp_device *isp_dev = vb2_get_drv_priv(queue); + unsigned int size = isp_dev->params.format.fmt.meta.buffersize; + + if (*planes_count) + return sizes[0] < size ? -EINVAL : 0; + + *planes_count = 1; + sizes[0] = size; + + return 0; +} + +static int sun6i_isp_params_buffer_prepare(struct vb2_buffer *vb2_buffer) +{ + struct sun6i_isp_device *isp_dev = + vb2_get_drv_priv(vb2_buffer->vb2_queue); + struct v4l2_device *v4l2_dev = &isp_dev->v4l2.v4l2_dev; + unsigned int size = isp_dev->params.format.fmt.meta.buffersize; + + if (vb2_plane_size(vb2_buffer, 0) < size) { + v4l2_err(v4l2_dev, "buffer too small (%lu < %u)\n", + vb2_plane_size(vb2_buffer, 0), size); + return -EINVAL; + } + + vb2_set_plane_payload(vb2_buffer, 0, size); + + return 0; +} + +static void sun6i_isp_params_buffer_queue(struct vb2_buffer *vb2_buffer) +{ + struct sun6i_isp_device *isp_dev = + vb2_get_drv_priv(vb2_buffer->vb2_queue); + struct sun6i_isp_params_state *state = &isp_dev->params.state; + struct vb2_v4l2_buffer *v4l2_buffer = to_vb2_v4l2_buffer(vb2_buffer); + struct sun6i_isp_buffer *isp_buffer = + container_of(v4l2_buffer, struct sun6i_isp_buffer, v4l2_buffer); + bool capture_streaming = isp_dev->capture.state.streaming; + unsigned long flags; + + spin_lock_irqsave(&state->lock, flags); + list_add_tail(&isp_buffer->list, &state->queue); + spin_unlock_irqrestore(&state->lock, flags); + + if (state->streaming && capture_streaming) + sun6i_isp_state_update(isp_dev, false); +} + +static int sun6i_isp_params_start_streaming(struct vb2_queue *queue, + unsigned int count) +{ + struct sun6i_isp_device *isp_dev = vb2_get_drv_priv(queue); + struct sun6i_isp_params_state *state = &isp_dev->params.state; + bool capture_streaming = isp_dev->capture.state.streaming; + + state->streaming = true; + + /* + * Update the state as soon as possible if capture is streaming, + * otherwise it will be applied when capture starts streaming. + */ + + if (capture_streaming) + sun6i_isp_state_update(isp_dev, false); + + return 0; +} + +static void sun6i_isp_params_stop_streaming(struct vb2_queue *queue) +{ + struct sun6i_isp_device *isp_dev = vb2_get_drv_priv(queue); + struct sun6i_isp_params_state *state = &isp_dev->params.state; + + state->streaming = false; + sun6i_isp_params_state_cleanup(isp_dev, true); +} + +static const struct vb2_ops sun6i_isp_params_queue_ops = { + .queue_setup = sun6i_isp_params_queue_setup, + .buf_prepare = sun6i_isp_params_buffer_prepare, + .buf_queue = sun6i_isp_params_buffer_queue, + .start_streaming = sun6i_isp_params_start_streaming, + .stop_streaming = sun6i_isp_params_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +/* Video Device */ + +static int sun6i_isp_params_querycap(struct file *file, void *private, + struct v4l2_capability *capability) +{ + struct sun6i_isp_device *isp_dev = video_drvdata(file); + struct video_device *video_dev = &isp_dev->params.video_dev; + + strscpy(capability->driver, SUN6I_ISP_NAME, sizeof(capability->driver)); + strscpy(capability->card, video_dev->name, sizeof(capability->card)); + snprintf(capability->bus_info, sizeof(capability->bus_info), + "platform:%s", dev_name(isp_dev->dev)); + + return 0; +} + +static int sun6i_isp_params_enum_fmt(struct file *file, void *private, + struct v4l2_fmtdesc *fmtdesc) +{ + struct sun6i_isp_device *isp_dev = video_drvdata(file); + struct v4l2_meta_format *params_format = + &isp_dev->params.format.fmt.meta; + + if (fmtdesc->index > 0) + return -EINVAL; + + fmtdesc->pixelformat = params_format->dataformat; + + return 0; +} + +static int sun6i_isp_params_g_fmt(struct file *file, void *private, + struct v4l2_format *format) +{ + struct sun6i_isp_device *isp_dev = video_drvdata(file); + + *format = isp_dev->params.format; + + return 0; +} + +static const struct v4l2_ioctl_ops sun6i_isp_params_ioctl_ops = { + .vidioc_querycap = sun6i_isp_params_querycap, + + .vidioc_enum_fmt_meta_out = sun6i_isp_params_enum_fmt, + .vidioc_g_fmt_meta_out = sun6i_isp_params_g_fmt, + .vidioc_s_fmt_meta_out = sun6i_isp_params_g_fmt, + .vidioc_try_fmt_meta_out = sun6i_isp_params_g_fmt, + + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +}; + +static const struct v4l2_file_operations sun6i_isp_params_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll, +}; + +/* Params */ + +int sun6i_isp_params_setup(struct sun6i_isp_device *isp_dev) +{ + struct sun6i_isp_params *params = &isp_dev->params; + struct sun6i_isp_params_state *state = ¶ms->state; + struct v4l2_device *v4l2_dev = &isp_dev->v4l2.v4l2_dev; + struct v4l2_subdev *proc_subdev = &isp_dev->proc.subdev; + struct video_device *video_dev = ¶ms->video_dev; + struct vb2_queue *queue = &isp_dev->params.queue; + struct media_pad *pad = &isp_dev->params.pad; + struct v4l2_format *format = &isp_dev->params.format; + struct v4l2_meta_format *params_format = &format->fmt.meta; + int ret; + + /* State */ + + INIT_LIST_HEAD(&state->queue); + spin_lock_init(&state->lock); + + /* Media Pads */ + + pad->flags = MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT; + + ret = media_entity_pads_init(&video_dev->entity, 1, pad); + if (ret) + goto error_mutex; + + /* Queue */ + + mutex_init(¶ms->lock); + + queue->type = V4L2_BUF_TYPE_META_OUTPUT; + queue->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; + queue->buf_struct_size = sizeof(struct sun6i_isp_buffer); + queue->ops = &sun6i_isp_params_queue_ops; + queue->mem_ops = &vb2_vmalloc_memops; + queue->min_buffers_needed = 1; + queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + queue->lock = ¶ms->lock; + queue->dev = isp_dev->dev; + queue->drv_priv = isp_dev; + + ret = vb2_queue_init(queue); + if (ret) { + v4l2_err(v4l2_dev, "failed to initialize vb2 queue: %d\n", ret); + goto error_media_entity; + } + + /* V4L2 Format */ + + format->type = queue->type; + params_format->dataformat = V4L2_META_FMT_SUN6I_ISP_PARAMS; + params_format->buffersize = sizeof(struct sun6i_isp_params_config); + + /* Video Device */ + + strscpy(video_dev->name, SUN6I_ISP_PARAMS_NAME, + sizeof(video_dev->name)); + video_dev->device_caps = V4L2_CAP_META_OUTPUT | V4L2_CAP_STREAMING; + video_dev->vfl_dir = VFL_DIR_TX; + video_dev->release = video_device_release_empty; + video_dev->fops = &sun6i_isp_params_fops; + video_dev->ioctl_ops = &sun6i_isp_params_ioctl_ops; + video_dev->v4l2_dev = v4l2_dev; + video_dev->queue = queue; + video_dev->lock = ¶ms->lock; + + video_set_drvdata(video_dev, isp_dev); + + ret = video_register_device(video_dev, VFL_TYPE_VIDEO, -1); + if (ret) { + v4l2_err(v4l2_dev, "failed to register video device: %d\n", + ret); + goto error_media_entity; + } + + /* Media Pad Link */ + + ret = media_create_pad_link(&video_dev->entity, 0, + &proc_subdev->entity, + SUN6I_ISP_PROC_PAD_SINK_PARAMS, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret < 0) { + v4l2_err(v4l2_dev, "failed to create %s:%u -> %s:%u link\n", + video_dev->entity.name, 0, proc_subdev->entity.name, + SUN6I_ISP_PROC_PAD_SINK_PARAMS); + goto error_video_device; + } + + return 0; + +error_video_device: + vb2_video_unregister_device(video_dev); + +error_media_entity: + media_entity_cleanup(&video_dev->entity); + +error_mutex: + mutex_destroy(¶ms->lock); + + return ret; +} + +void sun6i_isp_params_cleanup(struct sun6i_isp_device *isp_dev) +{ + struct sun6i_isp_params *params = &isp_dev->params; + struct video_device *video_dev = ¶ms->video_dev; + + vb2_video_unregister_device(video_dev); + media_entity_cleanup(&video_dev->entity); + mutex_destroy(¶ms->lock); +} diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_params.h b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_params.h new file mode 100644 index 000000000000..50f10f879c42 --- /dev/null +++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_params.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2021-2022 Bootlin + * Author: Paul Kocialkowski <[email protected]> + */ + +#ifndef _SUN6I_ISP_PARAMS_H_ +#define _SUN6I_ISP_PARAMS_H_ + +#include <media/v4l2-device.h> + +#define SUN6I_ISP_PARAMS_NAME "sun6i-isp-params" + +struct sun6i_isp_device; + +struct sun6i_isp_params_state { + struct list_head queue; /* Queue and buffers lock. */ + spinlock_t lock; + + struct sun6i_isp_buffer *pending; + + bool configured; + bool streaming; +}; + +struct sun6i_isp_params { + struct sun6i_isp_params_state state; + + struct video_device video_dev; + struct vb2_queue queue; + struct mutex lock; /* Queue lock. */ + struct media_pad pad; + + struct v4l2_format format; +}; + +/* Params */ + +void sun6i_isp_params_configure(struct sun6i_isp_device *isp_dev); + +/* State */ + +void sun6i_isp_params_state_update(struct sun6i_isp_device *isp_dev, + bool *update); +void sun6i_isp_params_state_complete(struct sun6i_isp_device *isp_dev); + +/* Params */ + +int sun6i_isp_params_setup(struct sun6i_isp_device *isp_dev); +void sun6i_isp_params_cleanup(struct sun6i_isp_device *isp_dev); + +#endif diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c new file mode 100644 index 000000000000..d69d2be0add2 --- /dev/null +++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c @@ -0,0 +1,577 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021-2022 Bootlin + * Author: Paul Kocialkowski <[email protected]> + */ + +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> + +#include "sun6i_isp.h" +#include "sun6i_isp_capture.h" +#include "sun6i_isp_params.h" +#include "sun6i_isp_proc.h" +#include "sun6i_isp_reg.h" + +/* Helpers */ + +void sun6i_isp_proc_dimensions(struct sun6i_isp_device *isp_dev, + unsigned int *width, unsigned int *height) +{ + if (width) + *width = isp_dev->proc.mbus_format.width; + if (height) + *height = isp_dev->proc.mbus_format.height; +} + +/* Format */ + +static const struct sun6i_isp_proc_format sun6i_isp_proc_formats[] = { + { + .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, + .input_format = SUN6I_ISP_INPUT_FMT_RAW_BGGR, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, + .input_format = SUN6I_ISP_INPUT_FMT_RAW_GBRG, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, + .input_format = SUN6I_ISP_INPUT_FMT_RAW_GRBG, + }, + { + .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, + .input_format = SUN6I_ISP_INPUT_FMT_RAW_RGGB, + }, + + { + .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, + .input_format = SUN6I_ISP_INPUT_FMT_RAW_BGGR, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, + .input_format = SUN6I_ISP_INPUT_FMT_RAW_GBRG, + }, + { + .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, + .input_format = SUN6I_ISP_INPUT_FMT_RAW_GRBG, + }, + { + .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, + .input_format = SUN6I_ISP_INPUT_FMT_RAW_RGGB, + }, +}; + +const struct sun6i_isp_proc_format *sun6i_isp_proc_format_find(u32 mbus_code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(sun6i_isp_proc_formats); i++) + if (sun6i_isp_proc_formats[i].mbus_code == mbus_code) + return &sun6i_isp_proc_formats[i]; + + return NULL; +} + +/* Processor */ + +static void sun6i_isp_proc_irq_enable(struct sun6i_isp_device *isp_dev) +{ + struct regmap *regmap = isp_dev->regmap; + + regmap_write(regmap, SUN6I_ISP_FE_INT_EN_REG, + SUN6I_ISP_FE_INT_EN_FINISH | + SUN6I_ISP_FE_INT_EN_START | + SUN6I_ISP_FE_INT_EN_PARA_SAVE | + SUN6I_ISP_FE_INT_EN_PARA_LOAD | + SUN6I_ISP_FE_INT_EN_SRC0_FIFO | + SUN6I_ISP_FE_INT_EN_ROT_FINISH); +} + +static void sun6i_isp_proc_irq_disable(struct sun6i_isp_device *isp_dev) +{ + struct regmap *regmap = isp_dev->regmap; + + regmap_write(regmap, SUN6I_ISP_FE_INT_EN_REG, 0); +} + +static void sun6i_isp_proc_irq_clear(struct sun6i_isp_device *isp_dev) +{ + struct regmap *regmap = isp_dev->regmap; + + regmap_write(regmap, SUN6I_ISP_FE_INT_EN_REG, 0); + regmap_write(regmap, SUN6I_ISP_FE_INT_STA_REG, + SUN6I_ISP_FE_INT_STA_CLEAR); +} + +static void sun6i_isp_proc_enable(struct sun6i_isp_device *isp_dev, + struct sun6i_isp_proc_source *source) +{ + struct sun6i_isp_proc *proc = &isp_dev->proc; + struct regmap *regmap = isp_dev->regmap; + u8 mode; + + /* Frontend */ + + if (source == &proc->source_csi0) + mode = SUN6I_ISP_SRC_MODE_CSI(0); + else + mode = SUN6I_ISP_SRC_MODE_CSI(1); + + regmap_write(regmap, SUN6I_ISP_FE_CFG_REG, + SUN6I_ISP_FE_CFG_EN | SUN6I_ISP_FE_CFG_SRC0_MODE(mode)); + + regmap_write(regmap, SUN6I_ISP_FE_CTRL_REG, + SUN6I_ISP_FE_CTRL_VCAP_EN | SUN6I_ISP_FE_CTRL_PARA_READY); +} + +static void sun6i_isp_proc_disable(struct sun6i_isp_device *isp_dev) +{ + struct regmap *regmap = isp_dev->regmap; + + /* Frontend */ + + regmap_write(regmap, SUN6I_ISP_FE_CTRL_REG, 0); + regmap_write(regmap, SUN6I_ISP_FE_CFG_REG, 0); +} + +static void sun6i_isp_proc_configure(struct sun6i_isp_device *isp_dev) +{ + struct v4l2_mbus_framefmt *mbus_format = &isp_dev->proc.mbus_format; + const struct sun6i_isp_proc_format *format; + u32 value; + + /* Module */ + + value = sun6i_isp_load_read(isp_dev, SUN6I_ISP_MODULE_EN_REG); + value |= SUN6I_ISP_MODULE_EN_SRC0; + sun6i_isp_load_write(isp_dev, SUN6I_ISP_MODULE_EN_REG, value); + + /* Input */ + + format = sun6i_isp_proc_format_find(mbus_format->code); + if (WARN_ON(!format)) + return; + + sun6i_isp_load_write(isp_dev, SUN6I_ISP_MODE_REG, + SUN6I_ISP_MODE_INPUT_FMT(format->input_format) | + SUN6I_ISP_MODE_INPUT_YUV_SEQ(format->input_yuv_seq) | + SUN6I_ISP_MODE_SHARP(1) | + SUN6I_ISP_MODE_HIST(2)); +} + +/* V4L2 Subdev */ + +static int sun6i_isp_proc_s_stream(struct v4l2_subdev *subdev, int on) +{ + struct sun6i_isp_device *isp_dev = v4l2_get_subdevdata(subdev); + struct sun6i_isp_proc *proc = &isp_dev->proc; + struct media_pad *local_pad = &proc->pads[SUN6I_ISP_PROC_PAD_SINK_CSI]; + struct device *dev = isp_dev->dev; + struct sun6i_isp_proc_source *source; + struct v4l2_subdev *source_subdev; + struct media_pad *remote_pad; + /* Initialize to 0 to use both in disable label (ret != 0) and off. */ + int ret = 0; + + /* Source */ + + remote_pad = media_pad_remote_pad_unique(local_pad); + if (IS_ERR(remote_pad)) { + dev_err(dev, + "zero or more than a single source connected to the bridge\n"); + return PTR_ERR(remote_pad); + } + + source_subdev = media_entity_to_v4l2_subdev(remote_pad->entity); + + if (source_subdev == proc->source_csi0.subdev) + source = &proc->source_csi0; + else + source = &proc->source_csi1; + + if (!on) { + sun6i_isp_proc_irq_disable(isp_dev); + v4l2_subdev_call(source_subdev, video, s_stream, 0); + goto disable; + } + + /* PM */ + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + /* Clear */ + + sun6i_isp_proc_irq_clear(isp_dev); + + /* Configure */ + + sun6i_isp_tables_configure(isp_dev); + sun6i_isp_params_configure(isp_dev); + sun6i_isp_proc_configure(isp_dev); + sun6i_isp_capture_configure(isp_dev); + + /* State Update */ + + sun6i_isp_state_update(isp_dev, true); + + /* Enable */ + + sun6i_isp_proc_irq_enable(isp_dev); + sun6i_isp_proc_enable(isp_dev, source); + + ret = v4l2_subdev_call(source_subdev, video, s_stream, 1); + if (ret && ret != -ENOIOCTLCMD) { + sun6i_isp_proc_irq_disable(isp_dev); + goto disable; + } + + return 0; + +disable: + sun6i_isp_proc_disable(isp_dev); + + pm_runtime_put(dev); + + return ret; +} + +static const struct v4l2_subdev_video_ops sun6i_isp_proc_video_ops = { + .s_stream = sun6i_isp_proc_s_stream, +}; + +static void +sun6i_isp_proc_mbus_format_prepare(struct v4l2_mbus_framefmt *mbus_format) +{ + if (!sun6i_isp_proc_format_find(mbus_format->code)) + mbus_format->code = sun6i_isp_proc_formats[0].mbus_code; + + mbus_format->field = V4L2_FIELD_NONE; + mbus_format->colorspace = V4L2_COLORSPACE_RAW; + mbus_format->quantization = V4L2_QUANTIZATION_DEFAULT; + mbus_format->xfer_func = V4L2_XFER_FUNC_DEFAULT; +} + +static int sun6i_isp_proc_init_cfg(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state) +{ + struct sun6i_isp_device *isp_dev = v4l2_get_subdevdata(subdev); + unsigned int pad = SUN6I_ISP_PROC_PAD_SINK_CSI; + struct v4l2_mbus_framefmt *mbus_format = + v4l2_subdev_get_try_format(subdev, state, pad); + struct mutex *lock = &isp_dev->proc.lock; + + mutex_lock(lock); + + mbus_format->code = sun6i_isp_proc_formats[0].mbus_code; + mbus_format->width = 1280; + mbus_format->height = 720; + + sun6i_isp_proc_mbus_format_prepare(mbus_format); + + mutex_unlock(lock); + + return 0; +} + +static int +sun6i_isp_proc_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code_enum) +{ + if (code_enum->index >= ARRAY_SIZE(sun6i_isp_proc_formats)) + return -EINVAL; + + code_enum->code = sun6i_isp_proc_formats[code_enum->index].mbus_code; + + return 0; +} + +static int sun6i_isp_proc_get_fmt(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct sun6i_isp_device *isp_dev = v4l2_get_subdevdata(subdev); + struct v4l2_mbus_framefmt *mbus_format = &format->format; + struct mutex *lock = &isp_dev->proc.lock; + + mutex_lock(lock); + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) + *mbus_format = *v4l2_subdev_get_try_format(subdev, state, + format->pad); + else + *mbus_format = isp_dev->proc.mbus_format; + + mutex_unlock(lock); + + return 0; +} + +static int sun6i_isp_proc_set_fmt(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct sun6i_isp_device *isp_dev = v4l2_get_subdevdata(subdev); + struct v4l2_mbus_framefmt *mbus_format = &format->format; + struct mutex *lock = &isp_dev->proc.lock; + + mutex_lock(lock); + + sun6i_isp_proc_mbus_format_prepare(mbus_format); + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) + *v4l2_subdev_get_try_format(subdev, state, format->pad) = + *mbus_format; + else + isp_dev->proc.mbus_format = *mbus_format; + + mutex_unlock(lock); + + return 0; +} + +static const struct v4l2_subdev_pad_ops sun6i_isp_proc_pad_ops = { + .init_cfg = sun6i_isp_proc_init_cfg, + .enum_mbus_code = sun6i_isp_proc_enum_mbus_code, + .get_fmt = sun6i_isp_proc_get_fmt, + .set_fmt = sun6i_isp_proc_set_fmt, +}; + +const struct v4l2_subdev_ops sun6i_isp_proc_subdev_ops = { + .video = &sun6i_isp_proc_video_ops, + .pad = &sun6i_isp_proc_pad_ops, +}; + +/* Media Entity */ + +static const struct media_entity_operations sun6i_isp_proc_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +/* V4L2 Async */ + +static int sun6i_isp_proc_link(struct sun6i_isp_device *isp_dev, + int sink_pad_index, + struct v4l2_subdev *remote_subdev, bool enabled) +{ + struct device *dev = isp_dev->dev; + struct v4l2_subdev *subdev = &isp_dev->proc.subdev; + struct media_entity *sink_entity = &subdev->entity; + struct media_entity *source_entity = &remote_subdev->entity; + int source_pad_index; + int ret; + + /* Get the first remote source pad. */ + ret = media_entity_get_fwnode_pad(source_entity, remote_subdev->fwnode, + MEDIA_PAD_FL_SOURCE); + if (ret < 0) { + dev_err(dev, "missing source pad in external entity %s\n", + source_entity->name); + return -EINVAL; + } + + source_pad_index = ret; + + dev_dbg(dev, "creating %s:%u -> %s:%u link\n", source_entity->name, + source_pad_index, sink_entity->name, sink_pad_index); + + ret = media_create_pad_link(source_entity, source_pad_index, + sink_entity, sink_pad_index, + enabled ? MEDIA_LNK_FL_ENABLED : 0); + if (ret < 0) { + dev_err(dev, "failed to create %s:%u -> %s:%u link\n", + source_entity->name, source_pad_index, + sink_entity->name, sink_pad_index); + return ret; + } + + return 0; +} + +static int sun6i_isp_proc_notifier_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *remote_subdev, + struct v4l2_async_subdev *async_subdev) +{ + struct sun6i_isp_device *isp_dev = + container_of(notifier, struct sun6i_isp_device, proc.notifier); + struct sun6i_isp_proc_async_subdev *proc_async_subdev = + container_of(async_subdev, struct sun6i_isp_proc_async_subdev, + async_subdev); + struct sun6i_isp_proc *proc = &isp_dev->proc; + struct sun6i_isp_proc_source *source = proc_async_subdev->source; + bool enabled; + + switch (source->endpoint.base.port) { + case SUN6I_ISP_PORT_CSI0: + source = &proc->source_csi0; + enabled = true; + break; + case SUN6I_ISP_PORT_CSI1: + source = &proc->source_csi1; + enabled = !proc->source_csi0.expected; + break; + default: + break; + } + + source->subdev = remote_subdev; + + return sun6i_isp_proc_link(isp_dev, SUN6I_ISP_PROC_PAD_SINK_CSI, + remote_subdev, enabled); +} + +static int +sun6i_isp_proc_notifier_complete(struct v4l2_async_notifier *notifier) +{ + struct sun6i_isp_device *isp_dev = + container_of(notifier, struct sun6i_isp_device, proc.notifier); + struct v4l2_device *v4l2_dev = &isp_dev->v4l2.v4l2_dev; + int ret; + + ret = v4l2_device_register_subdev_nodes(v4l2_dev); + if (ret) + return ret; + + return 0; +} + +static const struct v4l2_async_notifier_operations +sun6i_isp_proc_notifier_ops = { + .bound = sun6i_isp_proc_notifier_bound, + .complete = sun6i_isp_proc_notifier_complete, +}; + +/* Processor */ + +static int sun6i_isp_proc_source_setup(struct sun6i_isp_device *isp_dev, + struct sun6i_isp_proc_source *source, + u32 port) +{ + struct device *dev = isp_dev->dev; + struct v4l2_async_notifier *notifier = &isp_dev->proc.notifier; + struct v4l2_fwnode_endpoint *endpoint = &source->endpoint; + struct sun6i_isp_proc_async_subdev *proc_async_subdev; + struct fwnode_handle *handle = NULL; + int ret; + + handle = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), port, 0, 0); + if (!handle) + return -ENODEV; + + ret = v4l2_fwnode_endpoint_parse(handle, endpoint); + if (ret) + goto complete; + + proc_async_subdev = + v4l2_async_nf_add_fwnode_remote(notifier, handle, + struct + sun6i_isp_proc_async_subdev); + if (IS_ERR(proc_async_subdev)) { + ret = PTR_ERR(proc_async_subdev); + goto complete; + } + + proc_async_subdev->source = source; + + source->expected = true; + +complete: + fwnode_handle_put(handle); + + return ret; +} + +int sun6i_isp_proc_setup(struct sun6i_isp_device *isp_dev) +{ + struct device *dev = isp_dev->dev; + struct sun6i_isp_proc *proc = &isp_dev->proc; + struct v4l2_device *v4l2_dev = &isp_dev->v4l2.v4l2_dev; + struct v4l2_async_notifier *notifier = &proc->notifier; + struct v4l2_subdev *subdev = &proc->subdev; + struct media_pad *pads = proc->pads; + int ret; + + mutex_init(&proc->lock); + + /* V4L2 Subdev */ + + v4l2_subdev_init(subdev, &sun6i_isp_proc_subdev_ops); + strscpy(subdev->name, SUN6I_ISP_PROC_NAME, sizeof(subdev->name)); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + subdev->owner = THIS_MODULE; + subdev->dev = dev; + + v4l2_set_subdevdata(subdev, isp_dev); + + /* Media Entity */ + + subdev->entity.function = MEDIA_ENT_F_PROC_VIDEO_ISP; + subdev->entity.ops = &sun6i_isp_proc_entity_ops; + + /* Media Pads */ + + pads[SUN6I_ISP_PROC_PAD_SINK_CSI].flags = MEDIA_PAD_FL_SINK | + MEDIA_PAD_FL_MUST_CONNECT; + pads[SUN6I_ISP_PROC_PAD_SINK_PARAMS].flags = MEDIA_PAD_FL_SINK | + MEDIA_PAD_FL_MUST_CONNECT; + pads[SUN6I_ISP_PROC_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&subdev->entity, SUN6I_ISP_PROC_PAD_COUNT, + pads); + if (ret) + return ret; + + /* V4L2 Subdev */ + + ret = v4l2_device_register_subdev(v4l2_dev, subdev); + if (ret < 0) { + v4l2_err(v4l2_dev, "failed to register v4l2 subdev: %d\n", ret); + goto error_media_entity; + } + + /* V4L2 Async */ + + v4l2_async_nf_init(notifier); + notifier->ops = &sun6i_isp_proc_notifier_ops; + + sun6i_isp_proc_source_setup(isp_dev, &proc->source_csi0, + SUN6I_ISP_PORT_CSI0); + sun6i_isp_proc_source_setup(isp_dev, &proc->source_csi1, + SUN6I_ISP_PORT_CSI1); + + ret = v4l2_async_nf_register(v4l2_dev, notifier); + if (ret) { + v4l2_err(v4l2_dev, + "failed to register v4l2 async notifier: %d\n", ret); + goto error_v4l2_async_notifier; + } + + return 0; + +error_v4l2_async_notifier: + v4l2_async_nf_cleanup(notifier); + + v4l2_device_unregister_subdev(subdev); + +error_media_entity: + media_entity_cleanup(&subdev->entity); + + return ret; +} + +void sun6i_isp_proc_cleanup(struct sun6i_isp_device *isp_dev) +{ + struct v4l2_async_notifier *notifier = &isp_dev->proc.notifier; + struct v4l2_subdev *subdev = &isp_dev->proc.subdev; + + v4l2_async_nf_unregister(notifier); + v4l2_async_nf_cleanup(notifier); + + v4l2_device_unregister_subdev(subdev); + media_entity_cleanup(&subdev->entity); +} diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.h b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.h new file mode 100644 index 000000000000..c5c274e21ad5 --- /dev/null +++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2021-2022 Bootlin + * Author: Paul Kocialkowski <[email protected]> + */ + +#ifndef _SUN6I_ISP_PROC_H_ +#define _SUN6I_ISP_PROC_H_ + +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> + +#define SUN6I_ISP_PROC_NAME "sun6i-isp-proc" + +enum sun6i_isp_proc_pad { + SUN6I_ISP_PROC_PAD_SINK_CSI = 0, + SUN6I_ISP_PROC_PAD_SINK_PARAMS = 1, + SUN6I_ISP_PROC_PAD_SOURCE = 2, + SUN6I_ISP_PROC_PAD_COUNT = 3, +}; + +struct sun6i_isp_device; + +struct sun6i_isp_proc_format { + u32 mbus_code; + u8 input_format; + u8 input_yuv_seq; +}; + +struct sun6i_isp_proc_source { + struct v4l2_subdev *subdev; + struct v4l2_fwnode_endpoint endpoint; + bool expected; +}; + +struct sun6i_isp_proc_async_subdev { + struct v4l2_async_subdev async_subdev; + struct sun6i_isp_proc_source *source; +}; + +struct sun6i_isp_proc { + struct v4l2_subdev subdev; + struct media_pad pads[3]; + struct v4l2_async_notifier notifier; + struct v4l2_mbus_framefmt mbus_format; + struct mutex lock; /* Mbus format lock. */ + + struct sun6i_isp_proc_source source_csi0; + struct sun6i_isp_proc_source source_csi1; +}; + +/* Helpers */ + +void sun6i_isp_proc_dimensions(struct sun6i_isp_device *isp_dev, + unsigned int *width, unsigned int *height); + +/* Format */ + +const struct sun6i_isp_proc_format *sun6i_isp_proc_format_find(u32 mbus_code); + +/* Proc */ + +int sun6i_isp_proc_setup(struct sun6i_isp_device *isp_dev); +void sun6i_isp_proc_cleanup(struct sun6i_isp_device *isp_dev); + +#endif diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_reg.h b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_reg.h new file mode 100644 index 000000000000..83b9cdab2134 --- /dev/null +++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_reg.h @@ -0,0 +1,275 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2021-2022 Bootlin + * Author: Paul Kocialkowski <[email protected]> + */ + +#ifndef _SUN6I_ISP_REG_H_ +#define _SUN6I_ISP_REG_H_ + +#include <linux/kernel.h> + +#define SUN6I_ISP_ADDR_VALUE(a) ((a) >> 2) + +/* Frontend */ + +#define SUN6I_ISP_SRC_MODE_DRAM 0 +#define SUN6I_ISP_SRC_MODE_CSI(n) (1 + (n)) + +#define SUN6I_ISP_FE_CFG_REG 0x0 +#define SUN6I_ISP_FE_CFG_EN BIT(0) +#define SUN6I_ISP_FE_CFG_SRC0_MODE(v) (((v) << 8) & GENMASK(9, 8)) +#define SUN6I_ISP_FE_CFG_SRC1_MODE(v) (((v) << 16) & GENMASK(17, 16)) + +#define SUN6I_ISP_FE_CTRL_REG 0x4 +#define SUN6I_ISP_FE_CTRL_SCAP_EN BIT(0) +#define SUN6I_ISP_FE_CTRL_VCAP_EN BIT(1) +#define SUN6I_ISP_FE_CTRL_PARA_READY BIT(2) +#define SUN6I_ISP_FE_CTRL_LUT_UPDATE BIT(3) +#define SUN6I_ISP_FE_CTRL_LENS_UPDATE BIT(4) +#define SUN6I_ISP_FE_CTRL_GAMMA_UPDATE BIT(5) +#define SUN6I_ISP_FE_CTRL_DRC_UPDATE BIT(6) +#define SUN6I_ISP_FE_CTRL_DISC_UPDATE BIT(7) +#define SUN6I_ISP_FE_CTRL_OUTPUT_SPEED_CTRL(v) (((v) << 16) & GENMASK(17, 16)) +#define SUN6I_ISP_FE_CTRL_VCAP_READ_START BIT(31) + +#define SUN6I_ISP_FE_INT_EN_REG 0x8 +#define SUN6I_ISP_FE_INT_EN_FINISH BIT(0) +#define SUN6I_ISP_FE_INT_EN_START BIT(1) +#define SUN6I_ISP_FE_INT_EN_PARA_SAVE BIT(2) +#define SUN6I_ISP_FE_INT_EN_PARA_LOAD BIT(3) +#define SUN6I_ISP_FE_INT_EN_SRC0_FIFO BIT(4) +#define SUN6I_ISP_FE_INT_EN_SRC1_FIFO BIT(5) +#define SUN6I_ISP_FE_INT_EN_ROT_FINISH BIT(6) +#define SUN6I_ISP_FE_INT_EN_LINE_NUM_START BIT(7) + +#define SUN6I_ISP_FE_INT_STA_REG 0xc +#define SUN6I_ISP_FE_INT_STA_CLEAR 0xff +#define SUN6I_ISP_FE_INT_STA_FINISH BIT(0) +#define SUN6I_ISP_FE_INT_STA_START BIT(1) +#define SUN6I_ISP_FE_INT_STA_PARA_SAVE BIT(2) +#define SUN6I_ISP_FE_INT_STA_PARA_LOAD BIT(3) +#define SUN6I_ISP_FE_INT_STA_SRC0_FIFO BIT(4) +#define SUN6I_ISP_FE_INT_STA_SRC1_FIFO BIT(5) +#define SUN6I_ISP_FE_INT_STA_ROT_FINISH BIT(6) +#define SUN6I_ISP_FE_INT_STA_LINE_NUM_START BIT(7) + +/* Only since sun9i-a80-isp. */ +#define SUN6I_ISP_FE_INT_LINE_NUM_REG 0x18 +#define SUN6I_ISP_FE_ROT_OF_CFG_REG 0x1c + +/* Buffers/tables */ + +#define SUN6I_ISP_REG_LOAD_ADDR_REG 0x20 +#define SUN6I_ISP_REG_SAVE_ADDR_REG 0x24 + +#define SUN6I_ISP_LUT_TABLE_ADDR_REG 0x28 +#define SUN6I_ISP_DRC_TABLE_ADDR_REG 0x2c +#define SUN6I_ISP_STATS_ADDR_REG 0x30 + +/* SRAM */ + +#define SUN6I_ISP_SRAM_RW_OFFSET_REG 0x38 +#define SUN6I_ISP_SRAM_RW_DATA_REG 0x3c + +/* Global */ + +#define SUN6I_ISP_MODULE_EN_REG 0x40 +#define SUN6I_ISP_MODULE_EN_AE BIT(0) +#define SUN6I_ISP_MODULE_EN_OBC BIT(1) +#define SUN6I_ISP_MODULE_EN_DPC_LUT BIT(2) +#define SUN6I_ISP_MODULE_EN_DPC_OTF BIT(3) +#define SUN6I_ISP_MODULE_EN_BDNF BIT(4) +#define SUN6I_ISP_MODULE_EN_AWB BIT(6) +#define SUN6I_ISP_MODULE_EN_WB BIT(7) +#define SUN6I_ISP_MODULE_EN_LSC BIT(8) +#define SUN6I_ISP_MODULE_EN_BGC BIT(9) +#define SUN6I_ISP_MODULE_EN_SAP BIT(10) +#define SUN6I_ISP_MODULE_EN_AF BIT(11) +#define SUN6I_ISP_MODULE_EN_RGB2RGB BIT(12) +#define SUN6I_ISP_MODULE_EN_RGB_DRC BIT(13) +#define SUN6I_ISP_MODULE_EN_TDNF BIT(15) +#define SUN6I_ISP_MODULE_EN_AFS BIT(16) +#define SUN6I_ISP_MODULE_EN_HIST BIT(17) +#define SUN6I_ISP_MODULE_EN_YUV_GAIN_OFFSET BIT(18) +#define SUN6I_ISP_MODULE_EN_YUV_DRC BIT(19) +#define SUN6I_ISP_MODULE_EN_TG BIT(20) +#define SUN6I_ISP_MODULE_EN_ROT BIT(21) +#define SUN6I_ISP_MODULE_EN_CONTRAST BIT(22) +#define SUN6I_ISP_MODULE_EN_SATU BIT(24) +#define SUN6I_ISP_MODULE_EN_SRC1 BIT(30) +#define SUN6I_ISP_MODULE_EN_SRC0 BIT(31) + +#define SUN6I_ISP_MODE_REG 0x44 +#define SUN6I_ISP_MODE_INPUT_FMT(v) ((v) & GENMASK(2, 0)) +#define SUN6I_ISP_MODE_INPUT_YUV_SEQ(v) (((v) << 3) & GENMASK(4, 3)) +#define SUN6I_ISP_MODE_OTF_DPC(v) (((v) << 16) & BIT(16)) +#define SUN6I_ISP_MODE_SHARP(v) (((v) << 17) & BIT(17)) +#define SUN6I_ISP_MODE_HIST(v) (((v) << 20) & GENMASK(21, 20)) + +#define SUN6I_ISP_INPUT_FMT_YUV420 0 +#define SUN6I_ISP_INPUT_FMT_YUV422 1 +#define SUN6I_ISP_INPUT_FMT_RAW_BGGR 4 +#define SUN6I_ISP_INPUT_FMT_RAW_RGGB 5 +#define SUN6I_ISP_INPUT_FMT_RAW_GBRG 6 +#define SUN6I_ISP_INPUT_FMT_RAW_GRBG 7 + +#define SUN6I_ISP_INPUT_YUV_SEQ_YUYV 0 +#define SUN6I_ISP_INPUT_YUV_SEQ_YVYU 1 +#define SUN6I_ISP_INPUT_YUV_SEQ_UYVY 2 +#define SUN6I_ISP_INPUT_YUV_SEQ_VYUY 3 + +#define SUN6I_ISP_IN_CFG_REG 0x48 +#define SUN6I_ISP_IN_CFG_STRIDE_DIV16(v) ((v) & GENMASK(10, 0)) + +#define SUN6I_ISP_IN_LUMA_RGB_ADDR0_REG 0x4c +#define SUN6I_ISP_IN_CHROMA_ADDR0_REG 0x50 +#define SUN6I_ISP_IN_LUMA_RGB_ADDR1_REG 0x54 +#define SUN6I_ISP_IN_CHROMA_ADDR1_REG 0x58 + +/* AE */ + +#define SUN6I_ISP_AE_CFG_REG 0x60 +#define SUN6I_ISP_AE_CFG_LOW_BRI_TH(v) ((v) & GENMASK(11, 0)) +#define SUN6I_ISP_AE_CFG_HORZ_NUM(v) (((v) << 12) & GENMASK(15, 12)) +#define SUN6I_ISP_AE_CFG_HIGH_BRI_TH(v) (((v) << 16) & GENMASK(27, 16)) +#define SUN6I_ISP_AE_CFG_VERT_NUM(v) (((v) << 28) & GENMASK(31, 28)) + +#define SUN6I_ISP_AE_SIZE_REG 0x64 +#define SUN6I_ISP_AE_SIZE_WIDTH(v) ((v) & GENMASK(10, 0)) +#define SUN6I_ISP_AE_SIZE_HEIGHT(v) (((v) << 16) & GENMASK(26, 16)) + +#define SUN6I_ISP_AE_POS_REG 0x68 +#define SUN6I_ISP_AE_POS_HORZ_START(v) ((v) & GENMASK(10, 0)) +#define SUN6I_ISP_AE_POS_VERT_START(v) (((v) << 16) & GENMASK(26, 16)) + +/* OB */ + +#define SUN6I_ISP_OB_SIZE_REG 0x78 +#define SUN6I_ISP_OB_SIZE_WIDTH(v) ((v) & GENMASK(13, 0)) +#define SUN6I_ISP_OB_SIZE_HEIGHT(v) (((v) << 16) & GENMASK(29, 16)) + +#define SUN6I_ISP_OB_VALID_REG 0x7c +#define SUN6I_ISP_OB_VALID_WIDTH(v) ((v) & GENMASK(12, 0)) +#define SUN6I_ISP_OB_VALID_HEIGHT(v) (((v) << 16) & GENMASK(28, 16)) + +#define SUN6I_ISP_OB_SRC0_VALID_START_REG 0x80 +#define SUN6I_ISP_OB_SRC0_VALID_START_HORZ(v) ((v) & GENMASK(11, 0)) +#define SUN6I_ISP_OB_SRC0_VALID_START_VERT(v) (((v) << 16) & GENMASK(27, 16)) + +#define SUN6I_ISP_OB_SRC1_VALID_START_REG 0x84 +#define SUN6I_ISP_OB_SRC1_VALID_START_HORZ(v) ((v) & GENMASK(11, 0)) +#define SUN6I_ISP_OB_SRC1_VALID_START_VERT(v) (((v) << 16) & GENMASK(27, 16)) + +#define SUN6I_ISP_OB_SPRITE_REG 0x88 +#define SUN6I_ISP_OB_SPRITE_WIDTH(v) ((v) & GENMASK(12, 0)) +#define SUN6I_ISP_OB_SPRITE_HEIGHT(v) (((v) << 16) & GENMASK(28, 16)) + +#define SUN6I_ISP_OB_SPRITE_START_REG 0x8c +#define SUN6I_ISP_OB_SPRITE_START_HORZ(v) ((v) & GENMASK(11, 0)) +#define SUN6I_ISP_OB_SPRITE_START_VERT(v) (((v) << 16) & GENMASK(27, 16)) + +#define SUN6I_ISP_OB_CFG_REG 0x90 +#define SUN6I_ISP_OB_HORZ_POS_REG 0x94 +#define SUN6I_ISP_OB_VERT_PARA_REG 0x98 +#define SUN6I_ISP_OB_OFFSET_FIXED_REG 0x9c + +/* BDNF */ + +#define SUN6I_ISP_BDNF_CFG_REG 0xcc +#define SUN6I_ISP_BDNF_CFG_IN_DIS_MIN(v) ((v) & GENMASK(7, 0)) +#define SUN6I_ISP_BDNF_CFG_IN_DIS_MAX(v) (((v) << 16) & GENMASK(23, 16)) + +#define SUN6I_ISP_BDNF_COEF_RB_REG 0xd0 +#define SUN6I_ISP_BDNF_COEF_RB(i, v) (((v) << (4 * (i))) & \ + GENMASK(4 * (i) + 3, 4 * (i))) + +#define SUN6I_ISP_BDNF_COEF_G_REG 0xd4 +#define SUN6I_ISP_BDNF_COEF_G(i, v) (((v) << (4 * (i))) & \ + GENMASK(4 * (i) + 3, 4 * (i))) + +/* Bayer */ + +#define SUN6I_ISP_BAYER_OFFSET0_REG 0xe0 +#define SUN6I_ISP_BAYER_OFFSET0_R(v) ((v) & GENMASK(12, 0)) +#define SUN6I_ISP_BAYER_OFFSET0_GR(v) (((v) << 16) & GENMASK(28, 16)) + +#define SUN6I_ISP_BAYER_OFFSET1_REG 0xe4 +#define SUN6I_ISP_BAYER_OFFSET1_GB(v) ((v) & GENMASK(12, 0)) +#define SUN6I_ISP_BAYER_OFFSET1_B(v) (((v) << 16) & GENMASK(28, 16)) + +#define SUN6I_ISP_BAYER_GAIN0_REG 0xe8 +#define SUN6I_ISP_BAYER_GAIN0_R(v) ((v) & GENMASK(11, 0)) +#define SUN6I_ISP_BAYER_GAIN0_GR(v) (((v) << 16) & GENMASK(27, 16)) + +#define SUN6I_ISP_BAYER_GAIN1_REG 0xec +#define SUN6I_ISP_BAYER_GAIN1_GB(v) ((v) & GENMASK(11, 0)) +#define SUN6I_ISP_BAYER_GAIN1_B(v) (((v) << 16) & GENMASK(27, 16)) + +/* WB */ + +#define SUN6I_ISP_WB_GAIN0_REG 0x140 +#define SUN6I_ISP_WB_GAIN0_R(v) ((v) & GENMASK(11, 0)) +#define SUN6I_ISP_WB_GAIN0_GR(v) (((v) << 16) & GENMASK(27, 16)) + +#define SUN6I_ISP_WB_GAIN1_REG 0x144 +#define SUN6I_ISP_WB_GAIN1_GB(v) ((v) & GENMASK(11, 0)) +#define SUN6I_ISP_WB_GAIN1_B(v) (((v) << 16) & GENMASK(27, 16)) + +#define SUN6I_ISP_WB_CFG_REG 0x148 +#define SUN6I_ISP_WB_CFG_CLIP(v) ((v) & GENMASK(11, 0)) + +/* Global */ + +#define SUN6I_ISP_MCH_SIZE_CFG_REG 0x1e0 +#define SUN6I_ISP_MCH_SIZE_CFG_WIDTH(v) ((v) & GENMASK(12, 0)) +#define SUN6I_ISP_MCH_SIZE_CFG_HEIGHT(v) (((v) << 16) & GENMASK(28, 16)) + +#define SUN6I_ISP_MCH_SCALE_CFG_REG 0x1e4 +#define SUN6I_ISP_MCH_SCALE_CFG_X_RATIO(v) ((v) & GENMASK(11, 0)) +#define SUN6I_ISP_MCH_SCALE_CFG_Y_RATIO(v) (((v) << 16) & GENMASK(27, 16)) +#define SUN6I_ISP_MCH_SCALE_CFG_WEIGHT_SHIFT(v) (((v) << 28) & GENMASK(31, 28)) + +#define SUN6I_ISP_SCH_SIZE_CFG_REG 0x1e8 +#define SUN6I_ISP_SCH_SIZE_CFG_WIDTH(v) ((v) & GENMASK(12, 0)) +#define SUN6I_ISP_SCH_SIZE_CFG_HEIGHT(v) (((v) << 16) & GENMASK(28, 16)) + +#define SUN6I_ISP_SCH_SCALE_CFG_REG 0x1ec +#define SUN6I_ISP_SCH_SCALE_CFG_X_RATIO(v) ((v) & GENMASK(11, 0)) +#define SUN6I_ISP_SCH_SCALE_CFG_Y_RATIO(v) (((v) << 16) & GENMASK(27, 16)) +#define SUN6I_ISP_SCH_SCALE_CFG_WEIGHT_SHIFT(v) (((v) << 28) & GENMASK(31, 28)) + +#define SUN6I_ISP_MCH_CFG_REG 0x1f0 +#define SUN6I_ISP_MCH_CFG_EN BIT(0) +#define SUN6I_ISP_MCH_CFG_SCALE_EN BIT(1) +#define SUN6I_ISP_MCH_CFG_OUTPUT_FMT(v) (((v) << 2) & GENMASK(4, 2)) +#define SUN6I_ISP_MCH_CFG_MIRROR_EN BIT(5) +#define SUN6I_ISP_MCH_CFG_FLIP_EN BIT(6) +#define SUN6I_ISP_MCH_CFG_STRIDE_Y_DIV4(v) (((v) << 8) & GENMASK(18, 8)) +#define SUN6I_ISP_MCH_CFG_STRIDE_UV_DIV4(v) (((v) << 20) & GENMASK(30, 20)) + +#define SUN6I_ISP_OUTPUT_FMT_YUV420SP 0 +#define SUN6I_ISP_OUTPUT_FMT_YUV422SP 1 +#define SUN6I_ISP_OUTPUT_FMT_YVU420SP 2 +#define SUN6I_ISP_OUTPUT_FMT_YVU422SP 3 +#define SUN6I_ISP_OUTPUT_FMT_YUV420P 4 +#define SUN6I_ISP_OUTPUT_FMT_YUV422P 5 +#define SUN6I_ISP_OUTPUT_FMT_YVU420P 6 +#define SUN6I_ISP_OUTPUT_FMT_YVU422P 7 + +#define SUN6I_ISP_SCH_CFG_REG 0x1f4 + +#define SUN6I_ISP_MCH_Y_ADDR0_REG 0x1f8 +#define SUN6I_ISP_MCH_U_ADDR0_REG 0x1fc +#define SUN6I_ISP_MCH_V_ADDR0_REG 0x200 +#define SUN6I_ISP_MCH_Y_ADDR1_REG 0x204 +#define SUN6I_ISP_MCH_U_ADDR1_REG 0x208 +#define SUN6I_ISP_MCH_V_ADDR1_REG 0x20c +#define SUN6I_ISP_SCH_Y_ADDR0_REG 0x210 +#define SUN6I_ISP_SCH_U_ADDR0_REG 0x214 +#define SUN6I_ISP_SCH_V_ADDR0_REG 0x218 +#define SUN6I_ISP_SCH_Y_ADDR1_REG 0x21c +#define SUN6I_ISP_SCH_U_ADDR1_REG 0x220 +#define SUN6I_ISP_SCH_V_ADDR1_REG 0x224 + +#endif diff --git a/drivers/staging/media/sunxi/sun6i-isp/uapi/sun6i-isp-config.h b/drivers/staging/media/sunxi/sun6i-isp/uapi/sun6i-isp-config.h new file mode 100644 index 000000000000..19c24c5fd322 --- /dev/null +++ b/drivers/staging/media/sunxi/sun6i-isp/uapi/sun6i-isp-config.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: ((GPL-2.0+ WITH Linux-syscall-note) OR MIT) */ +/* + * Allwinner A31 ISP Configuration + */ + +#ifndef _UAPI_SUN6I_ISP_CONFIG_H +#define _UAPI_SUN6I_ISP_CONFIG_H + +#include <linux/types.h> + +#define V4L2_META_FMT_SUN6I_ISP_PARAMS v4l2_fourcc('S', '6', 'I', 'P') /* Allwinner A31 ISP Parameters */ + +#define SUN6I_ISP_MODULE_BAYER (1U << 0) +#define SUN6I_ISP_MODULE_BDNF (1U << 1) + +struct sun6i_isp_params_config_bayer { + __u16 offset_r; + __u16 offset_gr; + __u16 offset_gb; + __u16 offset_b; + + __u16 gain_r; + __u16 gain_gr; + __u16 gain_gb; + __u16 gain_b; +}; + +struct sun6i_isp_params_config_bdnf { + __u8 in_dis_min; + __u8 in_dis_max; + + __u8 coefficients_g[7]; + __u8 coefficients_rb[5]; +}; + +struct sun6i_isp_params_config { + __u32 modules_used; + + struct sun6i_isp_params_config_bayer bayer; + struct sun6i_isp_params_config_bdnf bdnf; +}; + +#endif /* _UAPI_SUN6I_ISP_CONFIG_H */ diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c index 9d46a36cc014..11dd142c98c5 100644 --- a/drivers/staging/media/tegra-video/vi.c +++ b/drivers/staging/media/tegra-video/vi.c @@ -1811,7 +1811,7 @@ static int tegra_vi_graph_parse_one(struct tegra_vi_channel *chan, } /* skip entities that are already processed */ - if (remote == dev_fwnode(vi->dev) || + if (device_match_fwnode(vi->dev, remote) || tegra_vi_graph_find_entity(chan, remote)) { fwnode_handle_put(remote); continue; diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index f91260e79ddc..e8c621ad8439 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -1781,6 +1781,8 @@ struct v4l2_ext_control { __u8 __user *p_u8; __u16 __user *p_u16; __u32 __user *p_u32; + __u32 __user *p_s32; + __u32 __user *p_s64; struct v4l2_area __user *p_area; struct v4l2_ctrl_h264_sps __user *p_h264_sps; struct v4l2_ctrl_h264_pps *p_h264_pps; |