diff --git a/Documentation/devicetree/bindings/media/i2c/maxim,max9296a.yaml b/Documentation/devicetree/bindings/media/i2c/maxim,max9296a.yaml new file mode 100644 index 00000000000000..4f2b3b5b69cf44 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/maxim,max9296a.yaml @@ -0,0 +1,242 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2024 Collabora Ltd. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/maxim,max9296a.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Maxim MAX9296A GMSL2 to CSI-2 Deserializer + +maintainers: + - Cosmin Tanislav + +description: > + The MAX9296A deserializer converts single or dual serial inputs to + MIPI CSI-2 outputs. The GMSL2 links operate at a fixed rate of 3Gbps + or 6Gbps in the forward direction and 187.5Mbps in the reverse + direction. In GMSL1 mode, each serial link can be paired with 3.12Gbps + or 1.5Gbps GMSL1 serializers or operate up to 4.5Gbps with GMSL2 + serializers with GMSL1 backward compatibility. The MAX9296A supports + mixed GMSL2 and GMSL1 links. The serial inputs operate independently, + allowing videos with different timings and resolutions to be received + on each input. + + MAX96716A supports both tunnel and pixel mode. + + MAX96792A supports both tunnel and pixel mode, and has two GMSL3 links. + +properties: + compatible: + enum: + - maxim,max9296a + - maxim,max96716a + - maxim,max96792a + + reg: + maxItems: 1 + + powerdown-gpios: + maxItems: 1 + description: Specifier for the GPIO connected to the PWDNB pin. + + port0-poc-supply: + description: Regulator providing Power over Coax for GMSL port 0 + + port1-poc-supply: + description: Regulator providing Power over Coax for GMSL port 1 + + i2c-alias-pool: + maxItems: 2 + + i2c-atr: + type: object + additionalProperties: false + + properties: + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + patternProperties: + '^i2c@[0-1]$': + $ref: /schemas/i2c/i2c-controller.yaml# + unevaluatedProperties: false + properties: + reg: + items: + minimum: 0 + maximum: 1 + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + patternProperties: + '^port@[0-1]$': + $ref: /schemas/graph.yaml#/properties/port + description: GMSL Input ports 0-1 + + '^port@[2-3]$': + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: CSI-2 Output ports 0-1 + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + minItems: 1 + maxItems: 4 + + lane-polarities: + minItems: 1 + maxItems: 5 + + link-frequencies: + maxItems: 1 + + required: + - data-lanes + + anyOf: + - required: + - port@2 + - required: + - port@3 + +required: + - compatible + - reg + - ports + +additionalProperties: false + +allOf: + - $ref: /schemas/i2c/i2c-atr.yaml# + +dependentRequired: + i2c-atr: [i2c-alias-pool] + i2c-alias-pool: [i2c-atr] + +examples: + - | + #include + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + deserializer@28 { + compatible = "maxim,max9296a"; + reg = <0x28>; + powerdown-gpios = <&main_gpio0 37 GPIO_ACTIVE_LOW>; + + i2c-alias-pool = <0x40 0x41>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + des_gmsl_in_0: endpoint { + remote-endpoint = <&ser_0_gmsl_out>; + }; + }; + + port@1 { + reg = <1>; + des_gmsl_in_1: endpoint { + remote-endpoint = <&ser_1_gmsl_out>; + }; + }; + + port@2 { + reg = <2>; + des_csi_out: endpoint { + data-lanes = <1 2 3 4>; + link-frequencies = /bits/ 64 <400000000>; + remote-endpoint = <&csi_in>; + }; + }; + }; + + i2c-atr { + #address-cells = <1>; + #size-cells = <0>; + + i2c@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + serializer@40 { + compatible = "maxim,max96717", "maxim,max96717f"; + reg = <0x40>; + gpio-controller; + #gpio-cells = <2>; + #clock-cells = <0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + ser_0_csi_in: endpoint { + data-lanes = <1 2>; + remote-endpoint = <&sensor_0_out>; + }; + }; + + port@1 { + reg = <1>; + ser_0_gmsl_out: endpoint { + remote-endpoint = <&des_gmsl_in_0>; + }; + }; + }; + }; + }; + + i2c@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + + serializer@40 { + compatible = "maxim,max96717", "maxim,max96717f"; + reg = <0x40>; + gpio-controller; + #gpio-cells = <2>; + #clock-cells = <0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + ser_1_csi_in: endpoint { + data-lanes = <1 2>; + remote-endpoint = <&sensor_1_out>; + }; + }; + + port@1 { + reg = <1>; + ser_1_gmsl_out: endpoint { + remote-endpoint = <&des_gmsl_in_1>; + }; + }; + }; + }; + }; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/media/i2c/maxim,max96712.yaml b/Documentation/devicetree/bindings/media/i2c/maxim,max96712.yaml index 26f85151afbd3e..5dcafd46344ccd 100644 --- a/Documentation/devicetree/bindings/media/i2c/maxim,max96712.yaml +++ b/Documentation/devicetree/bindings/media/i2c/maxim,max96712.yaml @@ -9,6 +9,7 @@ title: Quad GMSL2 to CSI-2 Deserializer with GMSL1 Compatibility maintainers: - Niklas Söderlund + - Cosmin Tanislav description: | The MAX96712 deserializer converts GMSL2 or GMSL1 serial inputs into MIPI @@ -23,12 +24,17 @@ description: | MAX96712 can be paired with first-generation 3.12Gbps or 1.5Gbps GMSL1 serializers or operate up to 3.12Gbps with GMSL2 serializers in GMSL1 mode. + MAX96724F and MAX96724R only support a fixed rate of 3Gbps in the forward + direction. + properties: compatible: items: - enum: - maxim,max96712 - maxim,max96724 + - maxim,max96724f + - maxim,max96724r reg: description: I2C device address @@ -36,30 +42,42 @@ properties: enable-gpios: true - ports: - $ref: /schemas/graph.yaml#/properties/ports + i2c-alias-pool: + maxItems: 4 + + i2c-atr: + type: object + additionalProperties: false properties: - port@0: - $ref: /schemas/graph.yaml#/properties/port - description: GMSL Input 0 + '#address-cells': + const: 1 - port@1: - $ref: /schemas/graph.yaml#/properties/port - description: GMSL Input 1 + '#size-cells': + const: 0 - port@2: - $ref: /schemas/graph.yaml#/properties/port - description: GMSL Input 2 + patternProperties: + '^i2c@[0-3]$': + $ref: /schemas/i2c/i2c-controller.yaml# + unevaluatedProperties: false + properties: + reg: + items: + minimum: 0 + maximum: 3 + + ports: + $ref: /schemas/graph.yaml#/properties/ports - port@3: + patternProperties: + '^port@[0-3]$': $ref: /schemas/graph.yaml#/properties/port - description: GMSL Input 3 + description: GMSL Input ports 0-3 - port@4: + '^port@[4-7]$': $ref: /schemas/graph.yaml#/$defs/port-base unevaluatedProperties: false - description: CSI-2 Output + description: CSI-2 Output port 0-3 properties: endpoint: @@ -77,8 +95,15 @@ properties: - data-lanes - bus-type - required: - - port@4 + anyOf: + - required: [port@4] + - required: [port@5] + - required: [port@6] + - required: [port@7] + +patternProperties: + '^port[0-3]-poc-supply$': + description: Regulator providing Power over Coax for GMSL ports required: - compatible @@ -87,6 +112,13 @@ required: additionalProperties: false +allOf: + - $ref: /schemas/i2c/i2c-atr.yaml# + +dependentRequired: + i2c-atr: [i2c-alias-pool] + i2c-alias-pool: [i2c-atr] + examples: - | #include diff --git a/Documentation/devicetree/bindings/media/i2c/maxim,max96714.yaml b/Documentation/devicetree/bindings/media/i2c/maxim,max96714.yaml index 3ace50e11921b2..1c97624833ebd8 100644 --- a/Documentation/devicetree/bindings/media/i2c/maxim,max96714.yaml +++ b/Documentation/devicetree/bindings/media/i2c/maxim,max96714.yaml @@ -8,6 +8,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Maxim MAX96714 GMSL2 to CSI-2 Deserializer maintainers: + - Cosmin Tanislav - Julien Massot description: @@ -22,7 +23,9 @@ description: The GMSL2 serial link operates at a fixed rate of 3Gbps or 6Gbps in the forward direction and 187.5Mbps in the reverse direction. - MAX96714F only supports a fixed rate of 3Gbps in the forward direction. + + MAX96714F and MAX96714R only support a fixed rate of 3Gbps in the forward + direction. properties: compatible: @@ -31,6 +34,7 @@ properties: - items: - enum: - maxim,max96714 + - maxim,max96714r - const: maxim,max96714f reg: diff --git a/Documentation/devicetree/bindings/media/i2c/maxim,max96717.yaml b/Documentation/devicetree/bindings/media/i2c/maxim,max96717.yaml index d1e8ba6e368ec1..02a44db9828522 100644 --- a/Documentation/devicetree/bindings/media/i2c/maxim,max96717.yaml +++ b/Documentation/devicetree/bindings/media/i2c/maxim,max96717.yaml @@ -9,6 +9,7 @@ title: MAX96717 CSI-2 to GMSL2 Serializer maintainers: - Julien Massot + - Cosmin Tanislav description: The MAX96717 serializer converts MIPI CSI-2 D-PHY formatted input @@ -24,15 +25,23 @@ description: The GMSL2 serial link operates at a fixed rate of 3Gbps or 6Gbps in the forward direction and 187.5Mbps in the reverse direction. + MAX96717F only supports a fixed rate of 3Gbps in the forward direction. + MAX9295A only supports pixel mode. + + MAX96793 also supports GMSL3 mode. + properties: compatible: oneOf: - - const: maxim,max96717f + - enum: + - maxim,max9295a + - maxim,max96717f - items: - enum: - maxim,max96717 + - maxim,max96793 - const: maxim,max96717f '#gpio-cells': @@ -91,13 +100,157 @@ properties: incoming GMSL2 link. Therefore, it supports an i2c-gate subnode to configure a sensor. + i2c-alias-pool: + maxItems: 2 + + i2c-atr: + type: object + additionalProperties: false + + properties: + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + patternProperties: + '^i2c@[01]$': + $ref: /schemas/i2c/i2c-controller.yaml# + unevaluatedProperties: false + properties: + reg: + items: + minimum: 0 + maximum: 1 + required: - compatible - reg - ports +patternProperties: + '-pins$': + type: object + additionalProperties: false + + properties: + function: + enum: [gpio, rclkout] + + pins: true + drive-open-drain: true + drive-push-pull: true + bias-disable: true + output-disable: true + output-enable: true + output-low: true + output-high: true + input-enable: true + + slew-rate: + description: | + Slew rate. + Rise and fall times represent the time needed for a GPIO to go + from 20% to 80% of VDDIO. + 0 - Fastest + rise: 1.0ns @ 1.8V, 0.6ns @ 3.3V, + fall: 0.8ns @ 1.8V, 0.5ns @ 3.3V + 1 - Fast + rise: 2.1ns @ 1.8V, 1.1ns @ 3.3V, + fall: 2.0ns @ 1.8V, 1.1ns @ 3.3V + 2 - Slow + rise: 4.0ns @ 1.8V, 2.3ns @3.3V, + fall: 10.0ns @ 1.8V, 5.0ns @3.3V + 3 - Slowest + rise: 9.0ns @ 1.8V, 5.0ns @3.3V, + fall: 10.0ns @ 1.8V, 5.0ns @3.3V + maximum: 3 + + bias-pull-up: + oneOf: + - type: boolean + description: Enable regular 40kOhm pull-up + - enum: [ 40000, 1000000 ] + description: Enable either the 40kOhm or the 1MOhm pull-up + + bias-pull-down: + oneOf: + - type: boolean + description: Enable regular 40kOhm pull-down + - enum: [ 40000, 1000000 ] + description: Enable either the 40kOhm or the 1MOhm pull-down + + maxim,jitter-compensation: + type: boolean + description: | + Enables jitter compensation. + Jitter compensation is used to minimize the jitter of the + signals transmitted from the deserializer to the serializer + by adding a fixed delay to every transition on the serializer + side. This can be used for pulse generation where timing is + critical. + + maxim,tx-id: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Enable transmission of the pin state from the serializer to + the deserializer using the specified identifier. + maximum: 31 + + maxim,rx-id: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Enable transmission of the pin state from the deserializer to + the serializer using the specified identifier. + maximum: 31 + + required: + - pins + - function + + allOf: + - $ref: /schemas/pinctrl/pincfg-node.yaml# + - $ref: /schemas/pinctrl/pinmux-node.yaml# + + - if: + properties: + function: + const: gpio + then: + properties: + pins: + items: + enum: [mfp0, mfp1, mfp2, mfp3, mfp4, mfp5, mfp6, mfp7, + mfp8, mfp9, mfp10] + + - if: + properties: + function: + const: rclkout + then: + properties: + pins: + items: + enum: [mfp2, mfp4] + additionalProperties: false +allOf: + - $ref: /schemas/i2c/i2c-atr.yaml# + + - anyOf: + - oneOf: + - required: [i2c-atr] + - required: [i2c-gate] + + - not: + required: [i2c-atr, i2c-gate] + +dependentRequired: + i2c-atr: [i2c-alias-pool] + i2c-alias-pool: [i2c-atr] + examples: - | #include diff --git a/Documentation/userspace-api/media/mediactl/media-types.rst b/Documentation/userspace-api/media/mediactl/media-types.rst index 6332e8395263b0..f55ef055bcf859 100644 --- a/Documentation/userspace-api/media/mediactl/media-types.rst +++ b/Documentation/userspace-api/media/mediactl/media-types.rst @@ -361,6 +361,7 @@ Types and flags used to represent the media graph elements .. _MEDIA-PAD-FL-SINK: .. _MEDIA-PAD-FL-SOURCE: .. _MEDIA-PAD-FL-MUST-CONNECT: +.. _MEDIA-PAD-FL-INTERNAL: .. flat-table:: Media pad flags :header-rows: 0 @@ -381,6 +382,13 @@ Types and flags used to represent the media graph elements enabled links even when this flag isn't set; the absence of the flag doesn't imply there is none. + * - ``MEDIA_PAD_FL_INTERNAL`` + - The internal flag indicates an internal pad that has no external + connections. Such a pad shall not be connected with a link. + + The internal flag may currently be present only in a source pad where + it indicates that the :ref:``stream `` + originates from within the entity. One and only one of ``MEDIA_PAD_FL_SINK`` and ``MEDIA_PAD_FL_SOURCE`` must be set for every pad. diff --git a/MAINTAINERS b/MAINTAINERS index 97d958c945e4ff..8ea583d4a58b10 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14953,6 +14953,7 @@ F: drivers/media/i2c/max9286.c MAX96712 QUAD GMSL2 DESERIALIZER DRIVER M: Niklas Söderlund +M: Cosmin Tanislav L: linux-media@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/media/i2c/maxim,max96712.yaml @@ -14960,6 +14961,7 @@ F: drivers/staging/media/max96712/max96712.c MAX96714 GMSL2 DESERIALIZER DRIVER M: Julien Massot +M: Cosmin Tanislav L: linux-media@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/media/i2c/maxim,max96714.yaml @@ -14967,6 +14969,7 @@ F: drivers/media/i2c/max96714.c MAX96717 GMSL2 SERIALIZER DRIVER M: Julien Massot +M: Cosmin Tanislav L: linux-media@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/media/i2c/maxim,max96717.yaml @@ -14986,6 +14989,13 @@ S: Maintained F: Documentation/devicetree/bindings/iio/proximity/maxbotix,mb1232.yaml F: drivers/iio/proximity/mb1232.c +MAXIM GMSL2/3 SERIALIZERS AND DESERIALIZERS +M: Cosmin Tanislav +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/media/i2c/maxim,max9296a.yaml +F: drivers/media/i2c/maxim-serdes/ + MAXIM MAX11205 DRIVER M: Ramona Bolboaca L: linux-iio@vger.kernel.org diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 58f87d09366cd1..d3e8e35fd3021e 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -1311,7 +1311,6 @@ CONFIG_GREYBUS=m CONFIG_GREYBUS_BEAGLEPLAY=m CONFIG_STAGING=y CONFIG_STAGING_MEDIA=y -CONFIG_VIDEO_MAX96712=m CONFIG_VIDEO_MESON_VDEC=m CONFIG_SND_BCM2835=m CONFIG_CHROME_PLATFORMS=y diff --git a/drivers/i2c/i2c-atr.c b/drivers/i2c/i2c-atr.c index dd194476b11847..01b4a9448e7359 100644 --- a/drivers/i2c/i2c-atr.c +++ b/drivers/i2c/i2c-atr.c @@ -43,6 +43,17 @@ struct i2c_atr_alias_pair { u16 alias; }; +/** + * struct i2c_atr_adapter_pool - Pool of child adapter to be created. + * @size: Total number of adapters + * @adapters: Array of adapter IDs + */ + +struct i2c_atr_adapter_pool { + size_t size; + u16 *adapters; +}; + /** * struct i2c_atr_alias_pool - Pool of client aliases available for an ATR. * @size: Total number of aliases @@ -126,12 +137,36 @@ struct i2c_atr { u32 flags; struct i2c_atr_alias_pool *alias_pool; + struct i2c_atr_adapter_pool *adapter_pool; struct notifier_block i2c_nb; struct i2c_adapter *adapter[] __counted_by(max_adapters); }; +static struct i2c_atr_adapter_pool *i2c_atr_alloc_adapter_pool(size_t num_adapters, bool shared) +{ + struct i2c_atr_adapter_pool *adapter_pool; + int ret; + adapter_pool = kzalloc(sizeof(*adapter_pool), GFP_KERNEL); + if (!adapter_pool) + return ERR_PTR(-ENOMEM); + + adapter_pool->size = num_adapters; + + adapter_pool->adapters = kcalloc(num_adapters, sizeof(*adapter_pool->adapters), GFP_KERNEL); + if (!adapter_pool->adapters) { + ret = -ENOMEM; + goto err_free_adapter_pool; + } + + return adapter_pool; + + kfree(adapter_pool->adapters); +err_free_adapter_pool: + kfree(adapter_pool); + return ERR_PTR(ret); +} static struct i2c_atr_alias_pool *i2c_atr_alloc_alias_pool(size_t num_aliases, bool shared) { struct i2c_atr_alias_pool *alias_pool; @@ -167,7 +202,11 @@ static struct i2c_atr_alias_pool *i2c_atr_alloc_alias_pool(size_t num_aliases, b kfree(alias_pool); return ERR_PTR(ret); } - +static void i2c_atr_free_adapter_pool(struct i2c_atr_adapter_pool *adapter_pool) +{ + kfree(adapter_pool->adapters); + kfree(adapter_pool); +} static void i2c_atr_free_alias_pool(struct i2c_atr_alias_pool *alias_pool) { bitmap_free(alias_pool->use_mask); @@ -638,7 +677,78 @@ static int i2c_atr_bus_notifier_call(struct notifier_block *nb, return NOTIFY_DONE; } +static int i2c_atr_parse_adapter_pool(struct i2c_atr *atr) +{ + struct i2c_atr_adapter_pool *adapter_pool; + struct device *dev = atr->dev; + size_t num_adapters; + unsigned int i; + u32 *adapters32; + int ret; + + if (!fwnode_property_present(dev_fwnode(dev), "i2c-adapter-pool")) { + num_adapters = 0; + return 0; + } else { + ret = fwnode_property_count_u32(dev_fwnode(dev), "i2c-adapter-pool"); + if (ret < 0) { + dev_err(dev, "Failed to count 'i2c-adapter-pool' property: %d\n", + ret); + return ret; + } + + num_adapters = ret; + } + adapter_pool = i2c_atr_alloc_adapter_pool(num_adapters, true); + if (IS_ERR(adapter_pool)) { + ret = PTR_ERR(adapter_pool); + dev_err(dev, "Failed to allocate adapter pool, err %d\n", ret); + return ret; + } + + atr->adapter_pool = adapter_pool; + + if (!adapter_pool->size) + return 0; + + adapters32 = kcalloc(num_adapters, sizeof(*adapters32), GFP_KERNEL); + if (!adapters32) { + ret = -ENOMEM; + goto err_free_adapter_pool; + } + + ret = fwnode_property_read_u32_array(dev_fwnode(dev), "i2c-adapter-pool", + adapters32, num_adapters); + if (ret < 0) { + dev_err(dev, "Failed to read 'i2c-adapter-pool' property: %d\n", + ret); + goto err_free_adapters32; + } + + for (i = 0; i < num_adapters; i++) { + if (!(adapters32[i] & 0xffff0000)) { + adapter_pool->adapters[i] = adapters32[i]; + continue; + } + + dev_err(dev, "Failed to parse 'i2c-adapter-pool' property: I2C flags are not supported\n"); + ret = -EINVAL; + goto err_free_adapters32; + } + + kfree(adapters32); + + dev_dbg(dev, "i2c-adapter-pool has %zu adapters\n", adapter_pool->size); + + return 0; + +err_free_adapters32: + kfree(adapters32); +err_free_adapter_pool: + i2c_atr_free_adapter_pool(adapter_pool); + return ret; +} static int i2c_atr_parse_alias_pool(struct i2c_atr *atr) { struct i2c_atr_alias_pool *alias_pool; @@ -747,6 +857,10 @@ struct i2c_atr *i2c_atr_new(struct i2c_adapter *parent, struct device *dev, if (ret) goto err_destroy_mutex; + ret = i2c_atr_parse_adapter_pool(atr); + if (ret) + goto err_destroy_mutex; + atr->i2c_nb.notifier_call = i2c_atr_bus_notifier_call; ret = bus_register_notifier(&i2c_bus_type, &atr->i2c_nb); if (ret) @@ -825,6 +939,8 @@ int i2c_atr_add_adapter(struct i2c_atr *atr, struct i2c_atr_adap_desc *desc) chan->adap.timeout = parent->timeout; chan->adap.quirks = parent->quirks; chan->adap.lock_ops = &i2c_atr_lock_ops; + chan->adap.is_atr = true; + chan->adap.chan_id = chan_id; if (bus_handle) { device_set_node(&chan->adap.dev, fwnode_handle_get(bus_handle)); @@ -834,17 +950,23 @@ int i2c_atr_add_adapter(struct i2c_atr *atr, struct i2c_atr_adap_desc *desc) u32 reg; atr_node = device_get_named_child_node(dev, "i2c-atr"); - - fwnode_for_each_child_node(atr_node, child) { - ret = fwnode_property_read_u32(child, "reg", ®); - if (ret) - continue; - if (chan_id == reg) - break; + if (atr_node) { + + fwnode_for_each_child_node(atr_node, child) { + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret) + continue; + if (chan_id == reg) + break; + } + + device_set_node(&chan->adap.dev, child); + fwnode_handle_put(atr_node); + } + else { + if (dev_fwnode(dev)) + device_set_node(&chan->adap.dev, fwnode_handle_get(dev_fwnode(dev))); } - - device_set_node(&chan->adap.dev, child); - fwnode_handle_put(atr_node); } if (desc->num_aliases > 0) { @@ -862,6 +984,9 @@ int i2c_atr_add_adapter(struct i2c_atr *atr, struct i2c_atr_adap_desc *desc) atr->adapter[chan_id] = &chan->adap; + if (atr->adapter_pool) + atr->adapter[chan_id]->nr = atr->adapter_pool->adapters[chan_id]; + ret = i2c_add_adapter(&chan->adap); if (ret) { dev_err(dev, "failed to add atr-adapter %u (error=%d)\n", diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c index ed90858a27b7b9..3cb316769ed167 100644 --- a/drivers/i2c/i2c-core-acpi.c +++ b/drivers/i2c/i2c-core-acpi.c @@ -285,6 +285,32 @@ static void i2c_acpi_register_device(struct i2c_adapter *adapter, if (acpi_quirk_skip_i2c_client_enumeration(adev)) return; + /* Check if Device is on ATR or MUX adapter */ + if (adapter->is_atr || adapter->is_mux) { + u32 channel; + + if (fwnode_property_present(&adev->fwnode, "channel")) { + if (fwnode_property_read_u32(&adev->fwnode, "channel", &channel)) { + dev_err(&adev->dev, "failed to read channel property\n"); + return; + } + + if (adapter->chan_id != channel) { + dev_err(&adev->dev, "device is not on current adapter %d\n", adapter->chan_id); + return; + } else + acpi_dev_clear_dependencies(ACPI_COMPANION(adapter->dev.parent)); + + if (adev->dep_unmet) { + dev_err(&adev->dev, "device has unmet dependencies\n"); + return; + } + } else { + dev_err(&adev->dev, "channel property not present\n"); + return; + } + } + adev->power.flags.ignore_parent = true; acpi_device_set_enumerated(adev); @@ -323,7 +349,8 @@ void i2c_acpi_register_devices(struct i2c_adapter *adap) acpi_status status; if (!has_acpi_companion(&adap->dev)) - return; + if (!adap->dev.fwnode) + return; status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, I2C_ACPI_MAX_SCAN_DEPTH, diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index ecca8c006b0203..37475998b439ed 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -1664,6 +1664,9 @@ int i2c_add_adapter(struct i2c_adapter *adapter) return __i2c_add_numbered_adapter(adapter); } + if (adapter->is_atr && adapter->nr > 0) + return __i2c_add_numbered_adapter(adapter); + mutex_lock(&core_lock); id = idr_alloc(&i2c_adapter_idr, adapter, __i2c_first_dynamic_bus_num, 0, GFP_KERNEL); diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index 4d8690981a55dc..4d6b2dc34af000 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -317,10 +317,12 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc, priv->adap.owner = THIS_MODULE; priv->adap.algo = &priv->algo; priv->adap.algo_data = priv; - priv->adap.dev.parent = &parent->dev; + priv->adap.dev.parent = muxc->dev; priv->adap.retries = parent->retries; priv->adap.timeout = parent->timeout; priv->adap.quirks = parent->quirks; + priv->adap.is_mux = true; + priv->adap.chan_id = chan_id; if (muxc->mux_locked) priv->adap.lock_ops = &i2c_mux_lock_ops; else @@ -369,13 +371,8 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc, of_node_put(mux_node); } - /* - * Associate the mux channel with an ACPI node. - */ - if (has_acpi_companion(muxc->dev)) - acpi_preset_companion(&priv->adap.dev, - ACPI_COMPANION(muxc->dev), - chan_id); + if (dev_fwnode(muxc->dev)) + device_set_node(&priv->adap.dev, dev_fwnode(muxc->dev)); if (force_nr) { priv->adap.nr = force_nr; diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index d7e8a0b986a327..e8ee87d32877a0 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -1680,39 +1680,7 @@ config VIDEO_DS90UB960 Device driver for the Texas Instruments DS90UB960 FPD-Link III Deserializer and DS90UB9702 FPD-Link IV Deserializer. -config VIDEO_MAX96714 - tristate "Maxim MAX96714 GMSL2 deserializer" - depends on OF && I2C && VIDEO_DEV - select I2C_MUX - select MEDIA_CONTROLLER - select GPIOLIB - select V4L2_CCI_I2C - select V4L2_FWNODE - select VIDEO_V4L2_SUBDEV_API - help - Device driver for the Maxim MAX96714 GMSL2 Deserializer. - MAX96714 deserializers convert a GMSL2 input to MIPI CSI-2 - output. - - To compile this driver as a module, choose M here: the - module will be called max96714. - -config VIDEO_MAX96717 - tristate "Maxim MAX96717 GMSL2 Serializer support" - depends on I2C && VIDEO_DEV && COMMON_CLK - select I2C_MUX - select MEDIA_CONTROLLER - select GPIOLIB - select V4L2_CCI_I2C - select V4L2_FWNODE - select VIDEO_V4L2_SUBDEV_API - help - Device driver for the Maxim MAX96717 GMSL2 Serializer. - MAX96717 serializers convert video on a MIPI CSI-2 - input to a GMSL2 output. - - To compile this driver as a module, choose M here: the - module will be called max96717. +source "drivers/media/i2c/maxim-serdes/Kconfig" endmenu diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 085c0e0e15dd93..c676fc4b87a31d 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -70,8 +70,7 @@ obj-$(CONFIG_VIDEO_LT6911UXE) += lt6911uxe.o obj-$(CONFIG_VIDEO_M52790) += m52790.o obj-$(CONFIG_VIDEO_MAX9271_LIB) += max9271.o obj-$(CONFIG_VIDEO_MAX9286) += max9286.o -obj-$(CONFIG_VIDEO_MAX96714) += max96714.o -obj-$(CONFIG_VIDEO_MAX96717) += max96717.o +obj-$(CONFIG_VIDEO_MAXIM_SERDES) += maxim-serdes/ obj-$(CONFIG_VIDEO_ML86V7667) += ml86v7667.o obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o obj-$(CONFIG_VIDEO_MT9M001) += mt9m001.o diff --git a/drivers/media/i2c/isx031.c b/drivers/media/i2c/isx031.c index ddc3e512efa49d..9bf413a47c0dba 100644 --- a/drivers/media/i2c/isx031.c +++ b/drivers/media/i2c/isx031.c @@ -460,7 +460,6 @@ static void isx031_update_pad_format(const struct isx031_mode *mode, fmt->width = mode->width; fmt->height = mode->height; fmt->code = mode->code; - fmt->field = V4L2_FIELD_NONE; } static int isx031_start_streaming(struct isx031 *isx031) @@ -707,7 +706,50 @@ static const struct v4l2_subdev_internal_ops isx031_internal_ops = { .open = isx031_open, }; +static int isx031_parse_fwnode(struct isx031 *isx031, struct device *dev) +{ + struct fwnode_handle *endpoint; + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + int ret; + + endpoint = + fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!endpoint) { + dev_err(dev, "endpoint node not found"); + return -EPROBE_DEFER; + } + + ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg); + if (ret) { + dev_err(dev, "parsing endpoint node failed"); + goto out_err; + } + + /* Check the number of MIPI CSI2 data lanes */ + if (bus_cfg.bus.mipi_csi2.num_data_lanes != 2 && bus_cfg.bus.mipi_csi2.num_data_lanes != 4 ) { + dev_err(dev, "only 2 / 4 data lanes are currently supported"); + goto out_err; + } + isx031->lanes = bus_cfg.bus.mipi_csi2.num_data_lanes; + + v4l2_fwnode_endpoint_free(&bus_cfg); + fwnode_handle_put(endpoint); + return 0; + +out_err: + v4l2_fwnode_endpoint_free(&bus_cfg); + fwnode_handle_put(endpoint); + return -EINVAL; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) +static int isx031_remove(struct i2c_client *client) +#else static void isx031_remove(struct i2c_client *client) +#endif { struct v4l2_subdev *sd = i2c_get_clientdata(client); struct isx031 *isx031 = to_isx031(sd); @@ -731,6 +773,11 @@ static int isx031_probe(struct i2c_client *client) return -ENOMEM; isx031->client = client; + + ret = isx031_parse_fwnode(isx031, &client->dev); + if (ret) + dev_warn(&client->dev, "Parse fwnode failed\n"); + isx031->platform_data = client->dev.platform_data; if (isx031->platform_data == NULL) dev_warn(&client->dev, "no platform data provided\n"); @@ -788,7 +835,19 @@ static int isx031_probe(struct i2c_client *client) goto probe_error_media_entity_cleanup; } isx031->cur_mode = isx031->pre_mode; + + ret = v4l2_subdev_init_finalize(&isx031->sd); + if (ret < 0) { + dev_err(&client->dev, "failed to finalize V4L2 subdev: %d", + ret); + goto probe_error_media_entity_cleanup; + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 13, 0) + ret = v4l2_async_register_subdev_sensor_common(&isx031->sd); +#else ret = v4l2_async_register_subdev_sensor(&isx031->sd); +#endif if (ret < 0) { dev_err(&client->dev, "failed to register V4L2 subdev: %d", ret); @@ -813,6 +872,12 @@ static int isx031_probe(struct i2c_client *client) return ret; } +static const struct acpi_device_id isx031_acpi_ids[] = { + { "INTC1031" }, + {} +}; +MODULE_DEVICE_TABLE(acpi, isx031_acpi_ids); + static const struct dev_pm_ops isx031_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(isx031_suspend, isx031_resume) }; @@ -827,6 +892,7 @@ static struct i2c_driver isx031_i2c_driver = { .driver = { .name = "isx031", .pm = &isx031_pm_ops, + .acpi_match_table = ACPI_PTR(isx031_acpi_ids), }, .probe = isx031_probe, .remove = isx031_remove, diff --git a/drivers/media/i2c/max96714.c b/drivers/media/i2c/max96714.c deleted file mode 100644 index e3e625e6f11a81..00000000000000 --- a/drivers/media/i2c/max96714.c +++ /dev/null @@ -1,1017 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Maxim GMSL2 Deserializer Driver - * - * Copyright (C) 2024 Collabora Ltd. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#define MAX96714_DEVICE_ID 0xc9 -#define MAX96714F_DEVICE_ID 0xca -#define MAX96714_NPORTS 2 -#define MAX96714_PAD_SINK 0 -#define MAX96714_PAD_SOURCE 1 -#define MAX96714_CSI_NLANES 4 - -/* DEV */ -#define MAX96714_REG13 CCI_REG8(0x0d) -#define MAX96714_DEV_REV CCI_REG8(0x0e) -#define MAX96714_DEV_REV_MASK GENMASK(3, 0) -#define MAX96714_LINK_LOCK CCI_REG8(0x13) -#define MAX96714_LINK_LOCK_BIT BIT(3) -#define MAX96714_IO_CHK0 CCI_REG8(0x38) -#define MAX96714_PATTERN_CLK_FREQ GENMASK(1, 0) -/* VID_RX */ -#define MAX96714_VIDEO_RX8 CCI_REG8(0x11a) -#define MAX96714_VID_LOCK BIT(6) - -/* VRX_PATGEN_0 */ -#define MAX96714_PATGEN_0 CCI_REG8(0x240) -#define MAX96714_PATGEN_1 CCI_REG8(0x241) -#define MAX96714_PATGEN_MODE GENMASK(5, 4) -#define MAX96714_PATGEN_VS_DLY CCI_REG24(0x242) -#define MAX96714_PATGEN_VS_HIGH CCI_REG24(0x245) -#define MAX96714_PATGEN_VS_LOW CCI_REG24(0x248) -#define MAX96714_PATGEN_V2H CCI_REG24(0x24b) -#define MAX96714_PATGEN_HS_HIGH CCI_REG16(0x24e) -#define MAX96714_PATGEN_HS_LOW CCI_REG16(0x250) -#define MAX96714_PATGEN_HS_CNT CCI_REG16(0x252) -#define MAX96714_PATGEN_V2D CCI_REG24(0x254) -#define MAX96714_PATGEN_DE_HIGH CCI_REG16(0x257) -#define MAX96714_PATGEN_DE_LOW CCI_REG16(0x259) -#define MAX96714_PATGEN_DE_CNT CCI_REG16(0x25b) -#define MAX96714_PATGEN_GRAD_INC CCI_REG8(0x25d) -#define MAX96714_PATGEN_CHKB_COLOR_A CCI_REG24(0x25e) -#define MAX96714_PATGEN_CHKB_COLOR_B CCI_REG24(0x261) -#define MAX96714_PATGEN_CHKB_RPT_CNT_A CCI_REG8(0x264) -#define MAX96714_PATGEN_CHKB_RPT_CNT_B CCI_REG8(0x265) -#define MAX96714_PATGEN_CHKB_ALT CCI_REG8(0x266) -/* BACKTOP */ -#define MAX96714_BACKTOP25 CCI_REG8(0x320) -#define CSI_DPLL_FREQ_MASK GENMASK(4, 0) - -/* MIPI_PHY */ -#define MAX96714_MIPI_PHY0 CCI_REG8(0x330) -#define MAX96714_FORCE_CSI_OUT BIT(7) -#define MAX96714_MIPI_STDBY_N CCI_REG8(0x332) -#define MAX96714_MIPI_STDBY_MASK GENMASK(5, 4) -#define MAX96714_MIPI_LANE_MAP CCI_REG8(0x333) -#define MAX96714_MIPI_POLARITY CCI_REG8(0x335) -#define MAX96714_MIPI_POLARITY_MASK GENMASK(5, 0) - -/* MIPI_TX */ -#define MAX96714_MIPI_LANE_CNT CCI_REG8(0x44a) -#define MAX96714_CSI2_LANE_CNT_MASK GENMASK(7, 6) -#define MAX96714_MIPI_TX52 CCI_REG8(0x474) -#define MAX96714_TUN_EN BIT(0) - -#define MHZ(v) ((u32)((v) * 1000000U)) - -enum max96714_vpg_mode { - MAX96714_VPG_DISABLED = 0, - MAX96714_VPG_CHECKERBOARD = 1, - MAX96714_VPG_GRADIENT = 2, -}; - -struct max96714_rxport { - struct { - struct v4l2_subdev *sd; - u16 pad; - struct fwnode_handle *ep_fwnode; - } source; - struct regulator *poc; -}; - -struct max96714_txport { - struct v4l2_fwnode_endpoint vep; -}; - -struct max96714_priv { - struct i2c_client *client; - struct regmap *regmap; - struct gpio_desc *pd_gpio; - struct max96714_rxport rxport; - struct i2c_mux_core *mux; - u64 enabled_source_streams; - struct v4l2_subdev sd; - struct media_pad pads[MAX96714_NPORTS]; - struct v4l2_mbus_config_mipi_csi2 mipi_csi2; - struct v4l2_ctrl_handler ctrl_handler; - struct v4l2_async_notifier notifier; - s64 tx_link_freq; - enum max96714_vpg_mode pattern; -}; - -static inline struct max96714_priv *sd_to_max96714(struct v4l2_subdev *sd) -{ - return container_of(sd, struct max96714_priv, sd); -} - -static int max96714_enable_tx_port(struct max96714_priv *priv) -{ - return cci_update_bits(priv->regmap, MAX96714_MIPI_STDBY_N, - MAX96714_MIPI_STDBY_MASK, - MAX96714_MIPI_STDBY_MASK, NULL); -} - -static int max96714_disable_tx_port(struct max96714_priv *priv) -{ - return cci_update_bits(priv->regmap, MAX96714_MIPI_STDBY_N, - MAX96714_MIPI_STDBY_MASK, 0, NULL); -} - -static bool max96714_tx_port_enabled(struct max96714_priv *priv) -{ - u64 val; - - cci_read(priv->regmap, MAX96714_MIPI_STDBY_N, &val, NULL); - - return val & MAX96714_MIPI_STDBY_MASK; -} - -static int max96714_apply_patgen_timing(struct max96714_priv *priv, - struct v4l2_subdev_state *state) -{ - struct v4l2_mbus_framefmt *fmt = - v4l2_subdev_state_get_format(state, MAX96714_PAD_SOURCE); - const u32 h_active = fmt->width; - const u32 h_fp = 88; - const u32 h_sw = 44; - const u32 h_bp = 148; - u32 h_tot; - const u32 v_active = fmt->height; - const u32 v_fp = 4; - const u32 v_sw = 5; - const u32 v_bp = 36; - u32 v_tot; - int ret = 0; - - h_tot = h_active + h_fp + h_sw + h_bp; - v_tot = v_active + v_fp + v_sw + v_bp; - - /* 75 Mhz pixel clock */ - cci_update_bits(priv->regmap, MAX96714_IO_CHK0, - MAX96714_PATTERN_CLK_FREQ, 1, &ret); - - dev_info(&priv->client->dev, "height: %d width: %d\n", fmt->height, - fmt->width); - - cci_write(priv->regmap, MAX96714_PATGEN_VS_DLY, 0, &ret); - cci_write(priv->regmap, MAX96714_PATGEN_VS_HIGH, v_sw * h_tot, &ret); - cci_write(priv->regmap, MAX96714_PATGEN_VS_LOW, - (v_active + v_fp + v_bp) * h_tot, &ret); - cci_write(priv->regmap, MAX96714_PATGEN_HS_HIGH, h_sw, &ret); - cci_write(priv->regmap, MAX96714_PATGEN_HS_LOW, h_active + h_fp + h_bp, - &ret); - cci_write(priv->regmap, MAX96714_PATGEN_V2D, - h_tot * (v_sw + v_bp) + (h_sw + h_bp), &ret); - cci_write(priv->regmap, MAX96714_PATGEN_HS_CNT, v_tot, &ret); - cci_write(priv->regmap, MAX96714_PATGEN_DE_HIGH, h_active, &ret); - cci_write(priv->regmap, MAX96714_PATGEN_DE_LOW, h_fp + h_sw + h_bp, - &ret); - cci_write(priv->regmap, MAX96714_PATGEN_DE_CNT, v_active, &ret); - /* B G R */ - cci_write(priv->regmap, MAX96714_PATGEN_CHKB_COLOR_A, 0xfecc00, &ret); - /* B G R */ - cci_write(priv->regmap, MAX96714_PATGEN_CHKB_COLOR_B, 0x006aa7, &ret); - cci_write(priv->regmap, MAX96714_PATGEN_CHKB_RPT_CNT_A, 0x3c, &ret); - cci_write(priv->regmap, MAX96714_PATGEN_CHKB_RPT_CNT_B, 0x3c, &ret); - cci_write(priv->regmap, MAX96714_PATGEN_CHKB_ALT, 0x3c, &ret); - cci_write(priv->regmap, MAX96714_PATGEN_GRAD_INC, 0x10, &ret); - - return ret; -} - -static int max96714_apply_patgen(struct max96714_priv *priv, - struct v4l2_subdev_state *state) -{ - unsigned int val; - int ret = 0; - - if (priv->pattern) - ret = max96714_apply_patgen_timing(priv, state); - - cci_write(priv->regmap, MAX96714_PATGEN_0, priv->pattern ? 0xfb : 0, - &ret); - - val = FIELD_PREP(MAX96714_PATGEN_MODE, priv->pattern); - cci_update_bits(priv->regmap, MAX96714_PATGEN_1, MAX96714_PATGEN_MODE, - val, &ret); - return ret; -} - -static int max96714_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct max96714_priv *priv = - container_of(ctrl->handler, struct max96714_priv, ctrl_handler); - int ret; - - switch (ctrl->id) { - case V4L2_CID_TEST_PATTERN: - if (priv->enabled_source_streams) - return -EBUSY; - priv->pattern = ctrl->val; - break; - default: - return -EINVAL; - } - - ret = cci_update_bits(priv->regmap, MAX96714_MIPI_PHY0, - MAX96714_FORCE_CSI_OUT, - priv->pattern ? MAX96714_FORCE_CSI_OUT : 0, NULL); - - /* Pattern generator doesn't work with tunnel mode */ - return cci_update_bits(priv->regmap, MAX96714_MIPI_TX52, - MAX96714_TUN_EN, - priv->pattern ? 0 : MAX96714_TUN_EN, &ret); -} - -static const char * const max96714_test_pattern[] = { - "Disabled", - "Checkerboard", - "Gradient" -}; - -static const struct v4l2_ctrl_ops max96714_ctrl_ops = { - .s_ctrl = max96714_s_ctrl, -}; - -static int max96714_enable_streams(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state, - u32 source_pad, u64 streams_mask) -{ - struct max96714_priv *priv = sd_to_max96714(sd); - u64 sink_streams; - int ret; - - if (!priv->enabled_source_streams) - max96714_enable_tx_port(priv); - - ret = max96714_apply_patgen(priv, state); - if (ret) - goto err; - - if (!priv->pattern) { - if (!priv->rxport.source.sd) { - ret = -ENODEV; - goto err; - } - - sink_streams = - v4l2_subdev_state_xlate_streams(state, - MAX96714_PAD_SOURCE, - MAX96714_PAD_SINK, - &streams_mask); - - ret = v4l2_subdev_enable_streams(priv->rxport.source.sd, - priv->rxport.source.pad, - sink_streams); - if (ret) - goto err; - } - - priv->enabled_source_streams |= streams_mask; - - return 0; - -err: - if (!priv->enabled_source_streams) - max96714_disable_tx_port(priv); - - return ret; -} - -static int max96714_disable_streams(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state, - u32 source_pad, u64 streams_mask) -{ - struct max96714_priv *priv = sd_to_max96714(sd); - u64 sink_streams; - - if (!priv->pattern) { - int ret; - - sink_streams = - v4l2_subdev_state_xlate_streams(state, - MAX96714_PAD_SOURCE, - MAX96714_PAD_SINK, - &streams_mask); - - ret = v4l2_subdev_disable_streams(priv->rxport.source.sd, - priv->rxport.source.pad, - sink_streams); - if (ret) - return ret; - } - - priv->enabled_source_streams &= ~streams_mask; - - if (!priv->enabled_source_streams) - max96714_disable_tx_port(priv); - - return 0; -} - -static int max96714_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state, - struct v4l2_subdev_format *format) -{ - struct max96714_priv *priv = sd_to_max96714(sd); - struct v4l2_mbus_framefmt *fmt; - - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && - priv->enabled_source_streams) - return -EBUSY; - - /* No transcoding, source and sink formats must match. */ - if (format->pad == MAX96714_PAD_SOURCE) - return v4l2_subdev_get_fmt(sd, state, format); - - fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); - if (!fmt) - return -EINVAL; - - *fmt = format->format; - - fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, - format->stream); - if (!fmt) - return -EINVAL; - - *fmt = format->format; - - return 0; -} - -static int _max96714_set_routing(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state, - enum v4l2_subdev_format_whence which, - struct v4l2_subdev_krouting *routing) -{ - static const struct v4l2_mbus_framefmt format = { - .width = 1280, - .height = 1080, - .code = MEDIA_BUS_FMT_Y8_1X8, - .field = V4L2_FIELD_NONE, - }; - int ret; - - ret = v4l2_subdev_routing_validate(sd, routing, - V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); - if (ret) - return ret; - - return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format); -} - -static int max96714_set_routing(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state, - enum v4l2_subdev_format_whence which, - struct v4l2_subdev_krouting *routing) -{ - struct max96714_priv *priv = sd_to_max96714(sd); - - if (which == V4L2_SUBDEV_FORMAT_ACTIVE && priv->enabled_source_streams) - return -EBUSY; - - return _max96714_set_routing(sd, state, which, routing); -} - -static int max96714_init_state(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) -{ - struct v4l2_subdev_route routes[] = { - { - .sink_pad = MAX96714_PAD_SINK, - .sink_stream = 0, - .source_pad = MAX96714_PAD_SOURCE, - .source_stream = 0, - .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, - } - }; - struct v4l2_subdev_krouting routing = { - .num_routes = ARRAY_SIZE(routes), - .routes = routes, - }; - - return _max96714_set_routing(sd, state, V4L2_SUBDEV_FORMAT_ACTIVE, - &routing); -} - -static const struct v4l2_subdev_pad_ops max96714_pad_ops = { - .enable_streams = max96714_enable_streams, - .disable_streams = max96714_disable_streams, - - .set_routing = max96714_set_routing, - .get_fmt = v4l2_subdev_get_fmt, - .set_fmt = max96714_set_fmt, -}; - -static bool max96714_link_locked(struct max96714_priv *priv) -{ - u64 val = 0; - - cci_read(priv->regmap, MAX96714_LINK_LOCK, &val, NULL); - - return val & MAX96714_LINK_LOCK_BIT; -} - -static void max96714_link_status(struct max96714_priv *priv) -{ - struct device *dev = &priv->client->dev; - - dev_info(dev, "Link locked:%d\n", max96714_link_locked(priv)); -} - -static bool max96714_pipe_locked(struct max96714_priv *priv) -{ - u64 val; - - cci_read(priv->regmap, MAX96714_VIDEO_RX8, &val, NULL); - - return val & MAX96714_VID_LOCK; -} - -static void max96714_pipe_status(struct max96714_priv *priv) -{ - struct device *dev = &priv->client->dev; - - dev_info(dev, "Pipe vidlock:%d\n", max96714_pipe_locked(priv)); -} - -static void max96714_csi_status(struct max96714_priv *priv) -{ - struct device *dev = &priv->client->dev; - u64 freq = 0; - - cci_read(priv->regmap, MAX96714_BACKTOP25, &freq, NULL); - freq = FIELD_GET(CSI_DPLL_FREQ_MASK, freq); - - dev_info(dev, "CSI controller DPLL freq:%u00MHz CSIPHY enabled:%d\n", - (u8)freq, max96714_tx_port_enabled(priv)); -} - -static int max96714_log_status(struct v4l2_subdev *sd) -{ - struct max96714_priv *priv = sd_to_max96714(sd); - struct device *dev = &priv->client->dev; - - dev_info(dev, "Deserializer: max96714\n"); - - max96714_link_status(priv); - max96714_pipe_status(priv); - max96714_csi_status(priv); - - return 0; -} - -static const struct v4l2_subdev_core_ops max96714_subdev_core_ops = { - .log_status = max96714_log_status, -}; - -static const struct v4l2_subdev_video_ops max96714_video_ops = { - .s_stream = v4l2_subdev_s_stream_helper, -}; - -static const struct v4l2_subdev_internal_ops max96714_internal_ops = { - .init_state = max96714_init_state, -}; - -static const struct v4l2_subdev_ops max96714_subdev_ops = { - .video = &max96714_video_ops, - .core = &max96714_subdev_core_ops, - .pad = &max96714_pad_ops, -}; - -static const struct media_entity_operations max96714_entity_ops = { - .link_validate = v4l2_subdev_link_validate, -}; - -static int max96714_notify_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *subdev, - struct v4l2_async_connection *asd) -{ - struct max96714_priv *priv = sd_to_max96714(notifier->sd); - struct device *dev = &priv->client->dev; - int ret; - - ret = media_entity_get_fwnode_pad(&subdev->entity, - priv->rxport.source.ep_fwnode, - MEDIA_PAD_FL_SOURCE); - if (ret < 0) { - dev_err(dev, "Failed to find pad for %s\n", subdev->name); - return ret; - } - - priv->rxport.source.sd = subdev; - priv->rxport.source.pad = ret; - - ret = media_create_pad_link(&priv->rxport.source.sd->entity, - priv->rxport.source.pad, &priv->sd.entity, - MAX96714_PAD_SINK, - MEDIA_LNK_FL_ENABLED | - MEDIA_LNK_FL_IMMUTABLE); - if (ret) { - dev_err(dev, "Unable to link %s:%u -> %s:%u\n", - priv->rxport.source.sd->name, priv->rxport.source.pad, - priv->sd.name, MAX96714_PAD_SINK); - return ret; - } - - return 0; -} - -static const struct v4l2_async_notifier_operations max96714_notify_ops = { - .bound = max96714_notify_bound, -}; - -static int max96714_v4l2_notifier_register(struct max96714_priv *priv) -{ - struct device *dev = &priv->client->dev; - struct max96714_rxport *rxport = &priv->rxport; - struct v4l2_async_connection *asd; - int ret; - - if (!rxport->source.ep_fwnode) - return 0; - - v4l2_async_subdev_nf_init(&priv->notifier, &priv->sd); - - asd = v4l2_async_nf_add_fwnode(&priv->notifier, - rxport->source.ep_fwnode, - struct v4l2_async_connection); - if (IS_ERR(asd)) { - dev_err(dev, "Failed to add subdev: %pe", asd); - v4l2_async_nf_cleanup(&priv->notifier); - return PTR_ERR(asd); - } - - priv->notifier.ops = &max96714_notify_ops; - - ret = v4l2_async_nf_register(&priv->notifier); - if (ret) { - dev_err(dev, "Failed to register subdev_notifier"); - v4l2_async_nf_cleanup(&priv->notifier); - return ret; - } - - return 0; -} - -static int max96714_create_subdev(struct max96714_priv *priv) -{ - struct device *dev = &priv->client->dev; - int ret; - - v4l2_i2c_subdev_init(&priv->sd, priv->client, &max96714_subdev_ops); - priv->sd.internal_ops = &max96714_internal_ops; - - v4l2_ctrl_handler_init(&priv->ctrl_handler, 1); - priv->sd.ctrl_handler = &priv->ctrl_handler; - - v4l2_ctrl_new_int_menu(&priv->ctrl_handler, NULL, V4L2_CID_LINK_FREQ, - 0, 0, &priv->tx_link_freq); - v4l2_ctrl_new_std_menu_items(&priv->ctrl_handler, - &max96714_ctrl_ops, - V4L2_CID_TEST_PATTERN, - ARRAY_SIZE(max96714_test_pattern) - 1, - 0, 0, max96714_test_pattern); - if (priv->ctrl_handler.error) { - ret = priv->ctrl_handler.error; - goto err_free_ctrl; - } - - priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; - priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; - priv->sd.entity.ops = &max96714_entity_ops; - - priv->pads[MAX96714_PAD_SINK].flags = MEDIA_PAD_FL_SINK; - priv->pads[MAX96714_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; - - ret = media_entity_pads_init(&priv->sd.entity, - MAX96714_NPORTS, - priv->pads); - if (ret) - goto err_free_ctrl; - - priv->sd.state_lock = priv->sd.ctrl_handler->lock; - - ret = v4l2_subdev_init_finalize(&priv->sd); - if (ret) - goto err_entity_cleanup; - - ret = max96714_v4l2_notifier_register(priv); - if (ret) { - dev_err(dev, "v4l2 subdev notifier register failed: %d\n", ret); - goto err_subdev_cleanup; - } - - ret = v4l2_async_register_subdev(&priv->sd); - if (ret) { - dev_err(dev, "v4l2_async_register_subdev error: %d\n", ret); - goto err_unreg_notif; - } - - return 0; - -err_unreg_notif: - v4l2_async_nf_unregister(&priv->notifier); - v4l2_async_nf_cleanup(&priv->notifier); -err_subdev_cleanup: - v4l2_subdev_cleanup(&priv->sd); -err_entity_cleanup: - media_entity_cleanup(&priv->sd.entity); -err_free_ctrl: - v4l2_ctrl_handler_free(&priv->ctrl_handler); - - return ret; -}; - -static void max96714_destroy_subdev(struct max96714_priv *priv) -{ - v4l2_async_nf_unregister(&priv->notifier); - v4l2_async_nf_cleanup(&priv->notifier); - v4l2_async_unregister_subdev(&priv->sd); - - v4l2_subdev_cleanup(&priv->sd); - - media_entity_cleanup(&priv->sd.entity); - v4l2_ctrl_handler_free(&priv->ctrl_handler); -} - -static int max96714_i2c_mux_select(struct i2c_mux_core *mux, u32 chan) -{ - return 0; -} - -static int max96714_i2c_mux_init(struct max96714_priv *priv) -{ - priv->mux = i2c_mux_alloc(priv->client->adapter, &priv->client->dev, - 1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE, - max96714_i2c_mux_select, NULL); - if (!priv->mux) - return -ENOMEM; - - return i2c_mux_add_adapter(priv->mux, 0, 0); -} - -static int max96714_init_tx_port(struct max96714_priv *priv) -{ - struct v4l2_mbus_config_mipi_csi2 *mipi; - unsigned long lanes_used = 0; - unsigned int val, lane; - int ret; - - ret = max96714_disable_tx_port(priv); - - mipi = &priv->mipi_csi2; - val = div_u64(priv->tx_link_freq * 2, MHZ(100)); - - cci_update_bits(priv->regmap, MAX96714_BACKTOP25, - CSI_DPLL_FREQ_MASK, val, &ret); - - val = FIELD_PREP(MAX96714_CSI2_LANE_CNT_MASK, mipi->num_data_lanes - 1); - cci_update_bits(priv->regmap, MAX96714_MIPI_LANE_CNT, - MAX96714_CSI2_LANE_CNT_MASK, val, &ret); - - /* lanes polarity */ - val = 0; - for (lane = 0; lane < mipi->num_data_lanes + 1; lane++) { - if (!mipi->lane_polarities[lane]) - continue; - if (lane == 0) - /* clock lane */ - val |= BIT(5); - else if (lane < 3) - /* Lane D0 and D1 */ - val |= BIT(lane - 1); - else - /* D2 and D3 */ - val |= BIT(lane); - } - - cci_update_bits(priv->regmap, MAX96714_MIPI_POLARITY, - MAX96714_MIPI_POLARITY_MASK, val, &ret); - - /* lanes mapping */ - val = 0; - for (lane = 0; lane < mipi->num_data_lanes; lane++) { - val |= (mipi->data_lanes[lane] - 1) << (lane * 2); - lanes_used |= BIT(mipi->data_lanes[lane] - 1); - } - - /* - * Unused lanes need to be mapped as well to not have - * the same lanes mapped twice. - */ - for (; lane < MAX96714_CSI_NLANES; lane++) { - unsigned int idx = find_first_zero_bit(&lanes_used, - MAX96714_CSI_NLANES); - - val |= idx << (lane * 2); - lanes_used |= BIT(idx); - } - - return cci_write(priv->regmap, MAX96714_MIPI_LANE_MAP, val, &ret); -} - -static int max96714_rxport_enable_poc(struct max96714_priv *priv) -{ - struct max96714_rxport *rxport = &priv->rxport; - - if (!rxport->poc) - return 0; - - return regulator_enable(rxport->poc); -} - -static int max96714_rxport_disable_poc(struct max96714_priv *priv) -{ - struct max96714_rxport *rxport = &priv->rxport; - - if (!rxport->poc) - return 0; - - return regulator_disable(rxport->poc); -} - -static int max96714_parse_dt_txport(struct max96714_priv *priv) -{ - struct device *dev = &priv->client->dev; - struct v4l2_fwnode_endpoint vep = { .bus_type = V4L2_MBUS_CSI2_DPHY }; - struct fwnode_handle *ep_fwnode; - u32 num_data_lanes; - int ret; - - ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), - MAX96714_PAD_SOURCE, 0, 0); - if (!ep_fwnode) - return -EINVAL; - - ret = v4l2_fwnode_endpoint_alloc_parse(ep_fwnode, &vep); - fwnode_handle_put(ep_fwnode); - if (ret) { - dev_err(dev, "tx: failed to parse endpoint data\n"); - return -EINVAL; - } - - if (vep.nr_of_link_frequencies != 1) { - ret = -EINVAL; - goto err_free_vep; - } - - priv->tx_link_freq = vep.link_frequencies[0]; - /* Min 50MHz, Max 1250MHz, 50MHz step */ - if (priv->tx_link_freq < MHZ(50) || priv->tx_link_freq > MHZ(1250) || - (u32)priv->tx_link_freq % MHZ(50)) { - dev_err(dev, "tx: invalid link frequency\n"); - ret = -EINVAL; - goto err_free_vep; - } - - num_data_lanes = vep.bus.mipi_csi2.num_data_lanes; - if (num_data_lanes < 1 || num_data_lanes > MAX96714_CSI_NLANES) { - dev_err(dev, - "tx: invalid number of data lanes must be 1 to 4\n"); - ret = -EINVAL; - goto err_free_vep; - } - - priv->mipi_csi2 = vep.bus.mipi_csi2; - -err_free_vep: - v4l2_fwnode_endpoint_free(&vep); - - return ret; -} - -static int max96714_parse_dt_rxport(struct max96714_priv *priv) -{ - static const char *poc_name = "port0-poc"; - struct max96714_rxport *rxport = &priv->rxport; - struct device *dev = &priv->client->dev; - struct fwnode_handle *ep_fwnode; - int ret; - - ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), - MAX96714_PAD_SINK, 0, 0); - if (!ep_fwnode) - return -ENOENT; - - rxport->source.ep_fwnode = fwnode_graph_get_remote_endpoint(ep_fwnode); - fwnode_handle_put(ep_fwnode); - - if (!rxport->source.ep_fwnode) { - dev_err(dev, "rx: no remote endpoint\n"); - return -EINVAL; - } - - rxport->poc = devm_regulator_get_optional(dev, poc_name); - if (IS_ERR(rxport->poc)) { - ret = PTR_ERR(rxport->poc); - if (ret == -ENODEV) { - rxport->poc = NULL; - } else { - dev_err(dev, "rx: failed to get POC supply: %d\n", ret); - goto err_put_source_ep_fwnode; - } - } - - return 0; - -err_put_source_ep_fwnode: - fwnode_handle_put(rxport->source.ep_fwnode); - return ret; -} - -static int max96714_parse_dt(struct max96714_priv *priv) -{ - int ret; - - ret = max96714_parse_dt_txport(priv); - if (ret) - return ret; - - ret = max96714_parse_dt_rxport(priv); - /* - * The deserializer can create a test pattern even if the - * rx port is not connected to a serializer. - */ - if (ret && ret == -ENOENT) - ret = 0; - - return ret; -} - -static int max96714_enable_core_hw(struct max96714_priv *priv) -{ - struct device *dev = &priv->client->dev; - u64 val; - int ret; - - if (priv->pd_gpio) { - /* wait min 2 ms for reset to complete */ - gpiod_set_value_cansleep(priv->pd_gpio, 1); - fsleep(2000); - gpiod_set_value_cansleep(priv->pd_gpio, 0); - /* wait min 2 ms for power up to finish */ - fsleep(2000); - } - - ret = cci_read(priv->regmap, MAX96714_REG13, &val, NULL); - if (ret) { - dev_err_probe(dev, ret, "Cannot read first register, abort\n"); - goto err_pd_gpio; - } - - if (val != MAX96714_DEVICE_ID && val != MAX96714F_DEVICE_ID) { - dev_err(dev, "Unsupported device id expected %x got %x\n", - MAX96714F_DEVICE_ID, (u8)val); - ret = -EOPNOTSUPP; - goto err_pd_gpio; - } - - ret = cci_read(priv->regmap, MAX96714_DEV_REV, &val, NULL); - if (ret) - goto err_pd_gpio; - - dev_dbg(dev, "Found %x (rev %lx)\n", MAX96714F_DEVICE_ID, - (u8)val & MAX96714_DEV_REV_MASK); - - ret = cci_read(priv->regmap, MAX96714_MIPI_TX52, &val, NULL); - if (ret) - goto err_pd_gpio; - - if (!(val & MAX96714_TUN_EN)) { - dev_err(dev, "Only supporting tunnel mode"); - ret = -EOPNOTSUPP; - goto err_pd_gpio; - } - - return 0; - -err_pd_gpio: - gpiod_set_value_cansleep(priv->pd_gpio, 1); - return ret; -} - -static void max96714_disable_core_hw(struct max96714_priv *priv) -{ - gpiod_set_value_cansleep(priv->pd_gpio, 1); -} - -static int max96714_get_hw_resources(struct max96714_priv *priv) -{ - struct device *dev = &priv->client->dev; - - priv->regmap = devm_cci_regmap_init_i2c(priv->client, 16); - if (IS_ERR(priv->regmap)) - return PTR_ERR(priv->regmap); - - priv->pd_gpio = - devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); - if (IS_ERR(priv->pd_gpio)) - return dev_err_probe(dev, PTR_ERR(priv->pd_gpio), - "Cannot get powerdown GPIO\n"); - return 0; -} - -static int max96714_probe(struct i2c_client *client) -{ - struct device *dev = &client->dev; - struct max96714_priv *priv; - int ret; - - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - priv->client = client; - - ret = max96714_get_hw_resources(priv); - if (ret) - return ret; - - ret = max96714_enable_core_hw(priv); - if (ret) - return ret; - - ret = max96714_parse_dt(priv); - if (ret) - goto err_disable_core_hw; - - max96714_init_tx_port(priv); - - ret = max96714_rxport_enable_poc(priv); - if (ret) - goto err_free_ports; - - ret = max96714_i2c_mux_init(priv); - if (ret) - goto err_disable_poc; - - ret = max96714_create_subdev(priv); - if (ret) - goto err_del_mux; - - return 0; - -err_del_mux: - i2c_mux_del_adapters(priv->mux); -err_disable_poc: - max96714_rxport_disable_poc(priv); -err_free_ports: - fwnode_handle_put(priv->rxport.source.ep_fwnode); -err_disable_core_hw: - max96714_disable_core_hw(priv); - - return ret; -} - -static void max96714_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct max96714_priv *priv = sd_to_max96714(sd); - - max96714_destroy_subdev(priv); - i2c_mux_del_adapters(priv->mux); - max96714_rxport_disable_poc(priv); - fwnode_handle_put(priv->rxport.source.ep_fwnode); - max96714_disable_core_hw(priv); - gpiod_set_value_cansleep(priv->pd_gpio, 1); -} - -static const struct of_device_id max96714_of_ids[] = { - { .compatible = "maxim,max96714f" }, - { } -}; -MODULE_DEVICE_TABLE(of, max96714_of_ids); - -static struct i2c_driver max96714_i2c_driver = { - .driver = { - .name = "max96714", - .of_match_table = max96714_of_ids, - }, - .probe = max96714_probe, - .remove = max96714_remove, -}; - -module_i2c_driver(max96714_i2c_driver); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Maxim Integrated GMSL2 Deserializers Driver"); -MODULE_AUTHOR("Julien Massot "); diff --git a/drivers/media/i2c/max96717.c b/drivers/media/i2c/max96717.c deleted file mode 100644 index c8ae7890d9fa87..00000000000000 --- a/drivers/media/i2c/max96717.c +++ /dev/null @@ -1,1102 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Maxim GMSL2 Serializer Driver - * - * Copyright (C) 2024 Collabora Ltd. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#define MAX96717_DEVICE_ID 0xbf -#define MAX96717F_DEVICE_ID 0xc8 -#define MAX96717_PORTS 2 -#define MAX96717_PAD_SINK 0 -#define MAX96717_PAD_SOURCE 1 -#define MAX96717_CSI_NLANES 4 - -#define MAX96717_DEFAULT_CLKOUT_RATE 24000000UL - -/* DEV */ -#define MAX96717_REG3 CCI_REG8(0x3) -#define MAX96717_RCLKSEL GENMASK(1, 0) -#define RCLKSEL_REF_PLL CCI_REG8(0x3) -#define MAX96717_REG6 CCI_REG8(0x6) -#define RCLKEN BIT(5) -#define MAX96717_DEV_ID CCI_REG8(0xd) -#define MAX96717_DEV_REV CCI_REG8(0xe) -#define MAX96717_DEV_REV_MASK GENMASK(3, 0) - -/* VID_TX Z */ -#define MAX96717_VIDEO_TX0 CCI_REG8(0x110) -#define MAX96717_VIDEO_AUTO_BPP BIT(3) -#define MAX96717_VIDEO_TX2 CCI_REG8(0x112) -#define MAX96717_VIDEO_PCLKDET BIT(7) - -/* VTX_Z */ -#define MAX96717_VTX0 CCI_REG8(0x24e) -#define MAX96717_VTX1 CCI_REG8(0x24f) -#define MAX96717_PATTERN_CLK_FREQ GENMASK(3, 1) -#define MAX96717_VTX_VS_DLY CCI_REG24(0x250) -#define MAX96717_VTX_VS_HIGH CCI_REG24(0x253) -#define MAX96717_VTX_VS_LOW CCI_REG24(0x256) -#define MAX96717_VTX_V2H CCI_REG24(0x259) -#define MAX96717_VTX_HS_HIGH CCI_REG16(0x25c) -#define MAX96717_VTX_HS_LOW CCI_REG16(0x25e) -#define MAX96717_VTX_HS_CNT CCI_REG16(0x260) -#define MAX96717_VTX_V2D CCI_REG24(0x262) -#define MAX96717_VTX_DE_HIGH CCI_REG16(0x265) -#define MAX96717_VTX_DE_LOW CCI_REG16(0x267) -#define MAX96717_VTX_DE_CNT CCI_REG16(0x269) -#define MAX96717_VTX29 CCI_REG8(0x26b) -#define MAX96717_VTX_MODE GENMASK(1, 0) -#define MAX96717_VTX_GRAD_INC CCI_REG8(0x26c) -#define MAX96717_VTX_CHKB_COLOR_A CCI_REG24(0x26d) -#define MAX96717_VTX_CHKB_COLOR_B CCI_REG24(0x270) -#define MAX96717_VTX_CHKB_RPT_CNT_A CCI_REG8(0x273) -#define MAX96717_VTX_CHKB_RPT_CNT_B CCI_REG8(0x274) -#define MAX96717_VTX_CHKB_ALT CCI_REG8(0x275) - -/* GPIO */ -#define MAX96717_NUM_GPIO 11 -#define MAX96717_GPIO_REG_A(gpio) CCI_REG8(0x2be + (gpio) * 3) -#define MAX96717_GPIO_OUT BIT(4) -#define MAX96717_GPIO_IN BIT(3) -#define MAX96717_GPIO_RX_EN BIT(2) -#define MAX96717_GPIO_TX_EN BIT(1) -#define MAX96717_GPIO_OUT_DIS BIT(0) - -/* FRONTTOP */ -/* MAX96717 only have CSI port 'B' */ -#define MAX96717_FRONTOP0 CCI_REG8(0x308) -#define MAX96717_START_PORT_B BIT(5) - -/* MIPI_RX */ -#define MAX96717_MIPI_RX1 CCI_REG8(0x331) -#define MAX96717_MIPI_LANES_CNT GENMASK(5, 4) -#define MAX96717_MIPI_RX2 CCI_REG8(0x332) /* phy1 Lanes map */ -#define MAX96717_PHY2_LANES_MAP GENMASK(7, 4) -#define MAX96717_MIPI_RX3 CCI_REG8(0x333) /* phy2 Lanes map */ -#define MAX96717_PHY1_LANES_MAP GENMASK(3, 0) -#define MAX96717_MIPI_RX4 CCI_REG8(0x334) /* phy1 lane polarities */ -#define MAX96717_PHY1_LANES_POL GENMASK(6, 4) -#define MAX96717_MIPI_RX5 CCI_REG8(0x335) /* phy2 lane polarities */ -#define MAX96717_PHY2_LANES_POL GENMASK(2, 0) - -/* MIPI_RX_EXT */ -#define MAX96717_MIPI_RX_EXT11 CCI_REG8(0x383) -#define MAX96717_TUN_MODE BIT(7) - -/* REF_VTG */ -#define REF_VTG0 CCI_REG8(0x3f0) -#define REFGEN_PREDEF_EN BIT(6) -#define REFGEN_PREDEF_FREQ_MASK GENMASK(5, 4) -#define REFGEN_PREDEF_FREQ_ALT BIT(3) -#define REFGEN_RST BIT(1) -#define REFGEN_EN BIT(0) - -/* MISC */ -#define PIO_SLEW_1 CCI_REG8(0x570) - -enum max96717_vpg_mode { - MAX96717_VPG_DISABLED = 0, - MAX96717_VPG_CHECKERBOARD = 1, - MAX96717_VPG_GRADIENT = 2, -}; - -struct max96717_priv { - struct i2c_client *client; - struct regmap *regmap; - struct i2c_mux_core *mux; - struct v4l2_mbus_config_mipi_csi2 mipi_csi2; - struct v4l2_subdev sd; - struct media_pad pads[MAX96717_PORTS]; - struct v4l2_ctrl_handler ctrl_handler; - struct v4l2_async_notifier notifier; - struct v4l2_subdev *source_sd; - u16 source_sd_pad; - u64 enabled_source_streams; - u8 pll_predef_index; - struct clk_hw clk_hw; - struct gpio_chip gpio_chip; - enum max96717_vpg_mode pattern; -}; - -static inline struct max96717_priv *sd_to_max96717(struct v4l2_subdev *sd) -{ - return container_of(sd, struct max96717_priv, sd); -} - -static inline struct max96717_priv *clk_hw_to_max96717(struct clk_hw *hw) -{ - return container_of(hw, struct max96717_priv, clk_hw); -} - -static int max96717_i2c_mux_select(struct i2c_mux_core *mux, u32 chan) -{ - return 0; -} - -static int max96717_i2c_mux_init(struct max96717_priv *priv) -{ - priv->mux = i2c_mux_alloc(priv->client->adapter, &priv->client->dev, - 1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE, - max96717_i2c_mux_select, NULL); - if (!priv->mux) - return -ENOMEM; - - return i2c_mux_add_adapter(priv->mux, 0, 0); -} - -static inline int max96717_start_csi(struct max96717_priv *priv, bool start) -{ - return cci_update_bits(priv->regmap, MAX96717_FRONTOP0, - MAX96717_START_PORT_B, - start ? MAX96717_START_PORT_B : 0, NULL); -} - -static int max96717_apply_patgen_timing(struct max96717_priv *priv, - struct v4l2_subdev_state *state) -{ - struct v4l2_mbus_framefmt *fmt = - v4l2_subdev_state_get_format(state, MAX96717_PAD_SOURCE); - const u32 h_active = fmt->width; - const u32 h_fp = 88; - const u32 h_sw = 44; - const u32 h_bp = 148; - u32 h_tot; - const u32 v_active = fmt->height; - const u32 v_fp = 4; - const u32 v_sw = 5; - const u32 v_bp = 36; - u32 v_tot; - int ret = 0; - - h_tot = h_active + h_fp + h_sw + h_bp; - v_tot = v_active + v_fp + v_sw + v_bp; - - /* 75 Mhz pixel clock */ - cci_update_bits(priv->regmap, MAX96717_VTX1, - MAX96717_PATTERN_CLK_FREQ, 0xa, &ret); - - dev_info(&priv->client->dev, "height: %d width: %d\n", fmt->height, - fmt->width); - - cci_write(priv->regmap, MAX96717_VTX_VS_DLY, 0, &ret); - cci_write(priv->regmap, MAX96717_VTX_VS_HIGH, v_sw * h_tot, &ret); - cci_write(priv->regmap, MAX96717_VTX_VS_LOW, - (v_active + v_fp + v_bp) * h_tot, &ret); - cci_write(priv->regmap, MAX96717_VTX_HS_HIGH, h_sw, &ret); - cci_write(priv->regmap, MAX96717_VTX_HS_LOW, h_active + h_fp + h_bp, - &ret); - cci_write(priv->regmap, MAX96717_VTX_V2D, - h_tot * (v_sw + v_bp) + (h_sw + h_bp), &ret); - cci_write(priv->regmap, MAX96717_VTX_HS_CNT, v_tot, &ret); - cci_write(priv->regmap, MAX96717_VTX_DE_HIGH, h_active, &ret); - cci_write(priv->regmap, MAX96717_VTX_DE_LOW, h_fp + h_sw + h_bp, - &ret); - cci_write(priv->regmap, MAX96717_VTX_DE_CNT, v_active, &ret); - /* B G R */ - cci_write(priv->regmap, MAX96717_VTX_CHKB_COLOR_A, 0xfecc00, &ret); - /* B G R */ - cci_write(priv->regmap, MAX96717_VTX_CHKB_COLOR_B, 0x006aa7, &ret); - cci_write(priv->regmap, MAX96717_VTX_CHKB_RPT_CNT_A, 0x3c, &ret); - cci_write(priv->regmap, MAX96717_VTX_CHKB_RPT_CNT_B, 0x3c, &ret); - cci_write(priv->regmap, MAX96717_VTX_CHKB_ALT, 0x3c, &ret); - cci_write(priv->regmap, MAX96717_VTX_GRAD_INC, 0x10, &ret); - - return ret; -} - -static int max96717_apply_patgen(struct max96717_priv *priv, - struct v4l2_subdev_state *state) -{ - unsigned int val; - int ret = 0; - - if (priv->pattern) - ret = max96717_apply_patgen_timing(priv, state); - - cci_write(priv->regmap, MAX96717_VTX0, priv->pattern ? 0xfb : 0, - &ret); - - val = FIELD_PREP(MAX96717_VTX_MODE, priv->pattern); - cci_update_bits(priv->regmap, MAX96717_VTX29, MAX96717_VTX_MODE, - val, &ret); - return ret; -} - -static int max96717_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct max96717_priv *priv = - container_of(ctrl->handler, struct max96717_priv, ctrl_handler); - int ret; - - switch (ctrl->id) { - case V4L2_CID_TEST_PATTERN: - if (priv->enabled_source_streams) - return -EBUSY; - priv->pattern = ctrl->val; - break; - default: - return -EINVAL; - } - - /* Use bpp from bpp register */ - ret = cci_update_bits(priv->regmap, MAX96717_VIDEO_TX0, - MAX96717_VIDEO_AUTO_BPP, - priv->pattern ? 0 : MAX96717_VIDEO_AUTO_BPP, - NULL); - - /* - * Pattern generator doesn't work with tunnel mode. - * Needs RGB color format and deserializer tunnel mode must be disabled. - */ - return cci_update_bits(priv->regmap, MAX96717_MIPI_RX_EXT11, - MAX96717_TUN_MODE, - priv->pattern ? 0 : MAX96717_TUN_MODE, &ret); -} - -static const char * const max96717_test_pattern[] = { - "Disabled", - "Checkerboard", - "Gradient" -}; - -static const struct v4l2_ctrl_ops max96717_ctrl_ops = { - .s_ctrl = max96717_s_ctrl, -}; - -static int max96717_gpiochip_get(struct gpio_chip *gpiochip, - unsigned int offset) -{ - struct max96717_priv *priv = gpiochip_get_data(gpiochip); - u64 val; - int ret; - - ret = cci_read(priv->regmap, MAX96717_GPIO_REG_A(offset), - &val, NULL); - if (ret) - return ret; - - if (val & MAX96717_GPIO_OUT_DIS) - return !!(val & MAX96717_GPIO_IN); - else - return !!(val & MAX96717_GPIO_OUT); -} - -static int max96717_gpiochip_set(struct gpio_chip *gpiochip, - unsigned int offset, int value) -{ - struct max96717_priv *priv = gpiochip_get_data(gpiochip); - - return cci_update_bits(priv->regmap, MAX96717_GPIO_REG_A(offset), - MAX96717_GPIO_OUT, MAX96717_GPIO_OUT, NULL); -} - -static int max96717_gpio_get_direction(struct gpio_chip *gpiochip, - unsigned int offset) -{ - struct max96717_priv *priv = gpiochip_get_data(gpiochip); - u64 val; - int ret; - - ret = cci_read(priv->regmap, MAX96717_GPIO_REG_A(offset), &val, NULL); - if (ret < 0) - return ret; - - return !!(val & MAX96717_GPIO_OUT_DIS); -} - -static int max96717_gpio_direction_out(struct gpio_chip *gpiochip, - unsigned int offset, int value) -{ - struct max96717_priv *priv = gpiochip_get_data(gpiochip); - - return cci_update_bits(priv->regmap, MAX96717_GPIO_REG_A(offset), - MAX96717_GPIO_OUT_DIS | MAX96717_GPIO_OUT, - value ? MAX96717_GPIO_OUT : 0, NULL); -} - -static int max96717_gpio_direction_in(struct gpio_chip *gpiochip, - unsigned int offset) -{ - struct max96717_priv *priv = gpiochip_get_data(gpiochip); - - return cci_update_bits(priv->regmap, MAX96717_GPIO_REG_A(offset), - MAX96717_GPIO_OUT_DIS, MAX96717_GPIO_OUT_DIS, - NULL); -} - -static int max96717_gpiochip_probe(struct max96717_priv *priv) -{ - struct device *dev = &priv->client->dev; - struct gpio_chip *gc = &priv->gpio_chip; - int i, ret = 0; - - gc->label = dev_name(dev); - gc->parent = dev; - gc->owner = THIS_MODULE; - gc->ngpio = MAX96717_NUM_GPIO; - gc->base = -1; - gc->can_sleep = true; - gc->get_direction = max96717_gpio_get_direction; - gc->direction_input = max96717_gpio_direction_in; - gc->direction_output = max96717_gpio_direction_out; - gc->set = max96717_gpiochip_set; - gc->get = max96717_gpiochip_get; - - /* Disable GPIO forwarding */ - for (i = 0; i < gc->ngpio; i++) - cci_update_bits(priv->regmap, MAX96717_GPIO_REG_A(i), - MAX96717_GPIO_RX_EN | MAX96717_GPIO_TX_EN, - 0, &ret); - - if (ret) - return ret; - - ret = devm_gpiochip_add_data(dev, gc, priv); - if (ret) { - dev_err(dev, "Unable to create gpio_chip\n"); - return ret; - } - - return 0; -} - -static int _max96717_set_routing(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state, - struct v4l2_subdev_krouting *routing) -{ - static const struct v4l2_mbus_framefmt format = { - .width = 1280, - .height = 1080, - .code = MEDIA_BUS_FMT_Y8_1X8, - .field = V4L2_FIELD_NONE, - }; - int ret; - - ret = v4l2_subdev_routing_validate(sd, routing, - V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); - if (ret) - return ret; - - ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format); - if (ret) - return ret; - - return 0; -} - -static int max96717_set_routing(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state, - enum v4l2_subdev_format_whence which, - struct v4l2_subdev_krouting *routing) -{ - struct max96717_priv *priv = sd_to_max96717(sd); - - if (which == V4L2_SUBDEV_FORMAT_ACTIVE && priv->enabled_source_streams) - return -EBUSY; - - return _max96717_set_routing(sd, state, routing); -} - -static int max96717_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state, - struct v4l2_subdev_format *format) -{ - struct max96717_priv *priv = sd_to_max96717(sd); - struct v4l2_mbus_framefmt *fmt; - u64 stream_source_mask; - - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && - priv->enabled_source_streams) - return -EBUSY; - - /* No transcoding, source and sink formats must match. */ - if (format->pad == MAX96717_PAD_SOURCE) - return v4l2_subdev_get_fmt(sd, state, format); - - /* Set sink format */ - fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); - if (!fmt) - return -EINVAL; - - *fmt = format->format; - - /* Propagate to source format */ - fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, - format->stream); - if (!fmt) - return -EINVAL; - *fmt = format->format; - - stream_source_mask = BIT(format->stream); - - return v4l2_subdev_state_xlate_streams(state, MAX96717_PAD_SOURCE, - MAX96717_PAD_SINK, - &stream_source_mask); -} - -static int max96717_init_state(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state) -{ - struct v4l2_subdev_route routes[] = { - { - .sink_pad = MAX96717_PAD_SINK, - .sink_stream = 0, - .source_pad = MAX96717_PAD_SOURCE, - .source_stream = 0, - .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, - }, - }; - struct v4l2_subdev_krouting routing = { - .num_routes = ARRAY_SIZE(routes), - .routes = routes, - }; - - return _max96717_set_routing(sd, state, &routing); -} - -static bool max96717_pipe_pclkdet(struct max96717_priv *priv) -{ - u64 val = 0; - - cci_read(priv->regmap, MAX96717_VIDEO_TX2, &val, NULL); - - return val & MAX96717_VIDEO_PCLKDET; -} - -static int max96717_log_status(struct v4l2_subdev *sd) -{ - struct max96717_priv *priv = sd_to_max96717(sd); - struct device *dev = &priv->client->dev; - - dev_info(dev, "Serializer: max96717\n"); - dev_info(dev, "Pipe: pclkdet:%d\n", max96717_pipe_pclkdet(priv)); - - return 0; -} - -static int max96717_enable_streams(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state, u32 pad, - u64 streams_mask) -{ - struct max96717_priv *priv = sd_to_max96717(sd); - u64 sink_streams; - int ret; - - if (!priv->enabled_source_streams) - max96717_start_csi(priv, true); - - ret = max96717_apply_patgen(priv, state); - if (ret) - goto stop_csi; - - if (!priv->pattern) { - sink_streams = - v4l2_subdev_state_xlate_streams(state, - MAX96717_PAD_SOURCE, - MAX96717_PAD_SINK, - &streams_mask); - - ret = v4l2_subdev_enable_streams(priv->source_sd, - priv->source_sd_pad, - sink_streams); - if (ret) - goto stop_csi; - } - - priv->enabled_source_streams |= streams_mask; - - return 0; - -stop_csi: - if (!priv->enabled_source_streams) - max96717_start_csi(priv, false); - - return ret; -} - -static int max96717_disable_streams(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state, u32 pad, - u64 streams_mask) -{ - struct max96717_priv *priv = sd_to_max96717(sd); - u64 sink_streams; - - /* - * Stop the CSI receiver first then the source, - * otherwise the device may become unresponsive - * while holding the I2C bus low. - */ - priv->enabled_source_streams &= ~streams_mask; - if (!priv->enabled_source_streams) - max96717_start_csi(priv, false); - - if (!priv->pattern) { - int ret; - - sink_streams = - v4l2_subdev_state_xlate_streams(state, - MAX96717_PAD_SOURCE, - MAX96717_PAD_SINK, - &streams_mask); - - ret = v4l2_subdev_disable_streams(priv->source_sd, - priv->source_sd_pad, - sink_streams); - if (ret) - return ret; - } - - return 0; -} - -static const struct v4l2_subdev_pad_ops max96717_pad_ops = { - .enable_streams = max96717_enable_streams, - .disable_streams = max96717_disable_streams, - .set_routing = max96717_set_routing, - .get_fmt = v4l2_subdev_get_fmt, - .set_fmt = max96717_set_fmt, -}; - -static const struct v4l2_subdev_core_ops max96717_subdev_core_ops = { - .log_status = max96717_log_status, -}; - -static const struct v4l2_subdev_internal_ops max96717_internal_ops = { - .init_state = max96717_init_state, -}; - -static const struct v4l2_subdev_ops max96717_subdev_ops = { - .core = &max96717_subdev_core_ops, - .pad = &max96717_pad_ops, -}; - -static const struct media_entity_operations max96717_entity_ops = { - .link_validate = v4l2_subdev_link_validate, -}; - -static int max96717_notify_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *source_subdev, - struct v4l2_async_connection *asd) -{ - struct max96717_priv *priv = sd_to_max96717(notifier->sd); - struct device *dev = &priv->client->dev; - int ret; - - ret = media_entity_get_fwnode_pad(&source_subdev->entity, - source_subdev->fwnode, - MEDIA_PAD_FL_SOURCE); - if (ret < 0) { - dev_err(dev, "Failed to find pad for %s\n", - source_subdev->name); - return ret; - } - - priv->source_sd = source_subdev; - priv->source_sd_pad = ret; - - ret = media_create_pad_link(&source_subdev->entity, priv->source_sd_pad, - &priv->sd.entity, 0, - MEDIA_LNK_FL_ENABLED | - MEDIA_LNK_FL_IMMUTABLE); - if (ret) { - dev_err(dev, "Unable to link %s:%u -> %s:0\n", - source_subdev->name, priv->source_sd_pad, - priv->sd.name); - return ret; - } - - return 0; -} - -static const struct v4l2_async_notifier_operations max96717_notify_ops = { - .bound = max96717_notify_bound, -}; - -static int max96717_v4l2_notifier_register(struct max96717_priv *priv) -{ - struct device *dev = &priv->client->dev; - struct v4l2_async_connection *asd; - struct fwnode_handle *ep_fwnode; - int ret; - - ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), - MAX96717_PAD_SINK, 0, 0); - if (!ep_fwnode) { - dev_err(dev, "No graph endpoint\n"); - return -ENODEV; - } - - v4l2_async_subdev_nf_init(&priv->notifier, &priv->sd); - - asd = v4l2_async_nf_add_fwnode_remote(&priv->notifier, ep_fwnode, - struct v4l2_async_connection); - - fwnode_handle_put(ep_fwnode); - - if (IS_ERR(asd)) { - dev_err(dev, "Failed to add subdev: %ld", PTR_ERR(asd)); - v4l2_async_nf_cleanup(&priv->notifier); - return PTR_ERR(asd); - } - - priv->notifier.ops = &max96717_notify_ops; - - ret = v4l2_async_nf_register(&priv->notifier); - if (ret) { - dev_err(dev, "Failed to register subdev_notifier"); - v4l2_async_nf_cleanup(&priv->notifier); - return ret; - } - - return 0; -} - -static int max96717_subdev_init(struct max96717_priv *priv) -{ - struct device *dev = &priv->client->dev; - int ret; - - v4l2_i2c_subdev_init(&priv->sd, priv->client, &max96717_subdev_ops); - priv->sd.internal_ops = &max96717_internal_ops; - - v4l2_ctrl_handler_init(&priv->ctrl_handler, 1); - priv->sd.ctrl_handler = &priv->ctrl_handler; - - v4l2_ctrl_new_std_menu_items(&priv->ctrl_handler, - &max96717_ctrl_ops, - V4L2_CID_TEST_PATTERN, - ARRAY_SIZE(max96717_test_pattern) - 1, - 0, 0, max96717_test_pattern); - if (priv->ctrl_handler.error) { - ret = priv->ctrl_handler.error; - goto err_free_ctrl; - } - - priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; - priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; - priv->sd.entity.ops = &max96717_entity_ops; - - priv->pads[MAX96717_PAD_SINK].flags = MEDIA_PAD_FL_SINK; - priv->pads[MAX96717_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; - - ret = media_entity_pads_init(&priv->sd.entity, 2, priv->pads); - if (ret) { - dev_err_probe(dev, ret, "Failed to init pads\n"); - goto err_free_ctrl; - } - - ret = v4l2_subdev_init_finalize(&priv->sd); - if (ret) { - dev_err_probe(dev, ret, - "v4l2 subdev init finalized failed\n"); - goto err_entity_cleanup; - } - ret = max96717_v4l2_notifier_register(priv); - if (ret) { - dev_err_probe(dev, ret, - "v4l2 subdev notifier register failed\n"); - goto err_free_state; - } - - ret = v4l2_async_register_subdev(&priv->sd); - if (ret) { - dev_err_probe(dev, ret, "v4l2_async_register_subdev error\n"); - goto err_unreg_notif; - } - - return 0; - -err_unreg_notif: - v4l2_async_nf_unregister(&priv->notifier); - v4l2_async_nf_cleanup(&priv->notifier); -err_free_state: - v4l2_subdev_cleanup(&priv->sd); -err_entity_cleanup: - media_entity_cleanup(&priv->sd.entity); -err_free_ctrl: - v4l2_ctrl_handler_free(&priv->ctrl_handler); - - return ret; -} - -static void max96717_subdev_uninit(struct max96717_priv *priv) -{ - v4l2_async_unregister_subdev(&priv->sd); - v4l2_async_nf_unregister(&priv->notifier); - v4l2_async_nf_cleanup(&priv->notifier); - v4l2_subdev_cleanup(&priv->sd); - media_entity_cleanup(&priv->sd.entity); - v4l2_ctrl_handler_free(&priv->ctrl_handler); -} - -struct max96717_pll_predef_freq { - unsigned long freq; - bool is_alt; - u8 val; -}; - -static const struct max96717_pll_predef_freq max96717_predef_freqs[] = { - { 13500000, true, 0 }, { 19200000, false, 0 }, - { 24000000, true, 1 }, { 27000000, false, 1 }, - { 37125000, false, 2 }, { 74250000, false, 3 }, -}; - -static unsigned long -max96717_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) -{ - struct max96717_priv *priv = clk_hw_to_max96717(hw); - - return max96717_predef_freqs[priv->pll_predef_index].freq; -} - -static unsigned int max96717_clk_find_best_index(struct max96717_priv *priv, - unsigned long rate) -{ - unsigned int i, idx = 0; - unsigned long diff_new, diff_old = U32_MAX; - - for (i = 0; i < ARRAY_SIZE(max96717_predef_freqs); i++) { - diff_new = abs(rate - max96717_predef_freqs[i].freq); - if (diff_new < diff_old) { - diff_old = diff_new; - idx = i; - } - } - - return idx; -} - -static long max96717_clk_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) -{ - struct max96717_priv *priv = clk_hw_to_max96717(hw); - struct device *dev = &priv->client->dev; - unsigned int idx; - - idx = max96717_clk_find_best_index(priv, rate); - - if (rate != max96717_predef_freqs[idx].freq) { - dev_warn(dev, "Request CLK freq:%lu, found CLK freq:%lu\n", - rate, max96717_predef_freqs[idx].freq); - } - - return max96717_predef_freqs[idx].freq; -} - -static int max96717_clk_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) -{ - struct max96717_priv *priv = clk_hw_to_max96717(hw); - unsigned int val, idx; - int ret = 0; - - idx = max96717_clk_find_best_index(priv, rate); - - val = FIELD_PREP(REFGEN_PREDEF_FREQ_MASK, - max96717_predef_freqs[idx].val); - - if (max96717_predef_freqs[idx].is_alt) - val |= REFGEN_PREDEF_FREQ_ALT; - - val |= REFGEN_RST | REFGEN_PREDEF_EN; - - cci_write(priv->regmap, REF_VTG0, val, &ret); - cci_update_bits(priv->regmap, REF_VTG0, REFGEN_RST | REFGEN_EN, - REFGEN_EN, &ret); - if (ret) - return ret; - - priv->pll_predef_index = idx; - - return 0; -} - -static int max96717_clk_prepare(struct clk_hw *hw) -{ - struct max96717_priv *priv = clk_hw_to_max96717(hw); - - return cci_update_bits(priv->regmap, MAX96717_REG6, RCLKEN, - RCLKEN, NULL); -} - -static void max96717_clk_unprepare(struct clk_hw *hw) -{ - struct max96717_priv *priv = clk_hw_to_max96717(hw); - - cci_update_bits(priv->regmap, MAX96717_REG6, RCLKEN, 0, NULL); -} - -static const struct clk_ops max96717_clk_ops = { - .prepare = max96717_clk_prepare, - .unprepare = max96717_clk_unprepare, - .set_rate = max96717_clk_set_rate, - .recalc_rate = max96717_clk_recalc_rate, - .round_rate = max96717_clk_round_rate, -}; - -static int max96717_register_clkout(struct max96717_priv *priv) -{ - struct device *dev = &priv->client->dev; - struct clk_init_data init = { .ops = &max96717_clk_ops }; - int ret; - - init.name = kasprintf(GFP_KERNEL, "max96717.%s.clk_out", dev_name(dev)); - if (!init.name) - return -ENOMEM; - - /* RCLKSEL Reference PLL output */ - ret = cci_update_bits(priv->regmap, MAX96717_REG3, MAX96717_RCLKSEL, - MAX96717_RCLKSEL, NULL); - /* MFP4 fastest slew rate */ - cci_update_bits(priv->regmap, PIO_SLEW_1, BIT(5) | BIT(4), 0, &ret); - if (ret) - goto free_init_name; - - priv->clk_hw.init = &init; - - /* Initialize to 24 MHz */ - ret = max96717_clk_set_rate(&priv->clk_hw, - MAX96717_DEFAULT_CLKOUT_RATE, 0); - if (ret < 0) - goto free_init_name; - - ret = devm_clk_hw_register(dev, &priv->clk_hw); - kfree(init.name); - if (ret) - return dev_err_probe(dev, ret, "Cannot register clock HW\n"); - - ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, - &priv->clk_hw); - if (ret) - return dev_err_probe(dev, ret, - "Cannot add OF clock provider\n"); - - return 0; - -free_init_name: - kfree(init.name); - return ret; -} - -static int max96717_init_csi_lanes(struct max96717_priv *priv) -{ - struct v4l2_mbus_config_mipi_csi2 *mipi = &priv->mipi_csi2; - unsigned long lanes_used = 0; - unsigned int nlanes, lane, val = 0; - int ret; - - nlanes = mipi->num_data_lanes; - - ret = cci_update_bits(priv->regmap, MAX96717_MIPI_RX1, - MAX96717_MIPI_LANES_CNT, - FIELD_PREP(MAX96717_MIPI_LANES_CNT, - nlanes - 1), NULL); - - /* lanes polarity */ - for (lane = 0; lane < nlanes + 1; lane++) { - if (!mipi->lane_polarities[lane]) - continue; - /* Clock lane */ - if (lane == 0) - val |= BIT(2); - else if (lane < 3) - val |= BIT(lane - 1); - else - val |= BIT(lane); - } - - cci_update_bits(priv->regmap, MAX96717_MIPI_RX5, - MAX96717_PHY2_LANES_POL, - FIELD_PREP(MAX96717_PHY2_LANES_POL, val), &ret); - - cci_update_bits(priv->regmap, MAX96717_MIPI_RX4, - MAX96717_PHY1_LANES_POL, - FIELD_PREP(MAX96717_PHY1_LANES_POL, - val >> 3), &ret); - /* lanes mapping */ - for (lane = 0, val = 0; lane < nlanes; lane++) { - val |= (mipi->data_lanes[lane] - 1) << (lane * 2); - lanes_used |= BIT(mipi->data_lanes[lane] - 1); - } - - /* - * Unused lanes need to be mapped as well to not have - * the same lanes mapped twice. - */ - for (; lane < MAX96717_CSI_NLANES; lane++) { - unsigned int idx = find_first_zero_bit(&lanes_used, - MAX96717_CSI_NLANES); - - val |= idx << (lane * 2); - lanes_used |= BIT(idx); - } - - cci_update_bits(priv->regmap, MAX96717_MIPI_RX3, - MAX96717_PHY1_LANES_MAP, - FIELD_PREP(MAX96717_PHY1_LANES_MAP, val), &ret); - - return cci_update_bits(priv->regmap, MAX96717_MIPI_RX2, - MAX96717_PHY2_LANES_MAP, - FIELD_PREP(MAX96717_PHY2_LANES_MAP, val >> 4), - &ret); -} - -static int max96717_hw_init(struct max96717_priv *priv) -{ - struct device *dev = &priv->client->dev; - u64 dev_id, val; - int ret; - - ret = cci_read(priv->regmap, MAX96717_DEV_ID, &dev_id, NULL); - if (ret) - return dev_err_probe(dev, ret, - "Fail to read the device id\n"); - - if (dev_id != MAX96717_DEVICE_ID && dev_id != MAX96717F_DEVICE_ID) - return dev_err_probe(dev, -EOPNOTSUPP, - "Unsupported device id got %x\n", (u8)dev_id); - - ret = cci_read(priv->regmap, MAX96717_DEV_REV, &val, NULL); - if (ret) - return dev_err_probe(dev, ret, - "Fail to read device revision"); - - dev_dbg(dev, "Found %x (rev %lx)\n", (u8)dev_id, - (u8)val & MAX96717_DEV_REV_MASK); - - ret = cci_read(priv->regmap, MAX96717_MIPI_RX_EXT11, &val, NULL); - if (ret) - return dev_err_probe(dev, ret, - "Fail to read mipi rx extension"); - - if (!(val & MAX96717_TUN_MODE)) - return dev_err_probe(dev, -EOPNOTSUPP, - "Only supporting tunnel mode"); - - return max96717_init_csi_lanes(priv); -} - -static int max96717_parse_dt(struct max96717_priv *priv) -{ - struct device *dev = &priv->client->dev; - struct v4l2_fwnode_endpoint vep = { .bus_type = V4L2_MBUS_CSI2_DPHY }; - struct fwnode_handle *ep_fwnode; - unsigned char num_data_lanes; - int ret; - - ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), - MAX96717_PAD_SINK, 0, 0); - if (!ep_fwnode) - return dev_err_probe(dev, -ENOENT, "no endpoint found\n"); - - ret = v4l2_fwnode_endpoint_parse(ep_fwnode, &vep); - - fwnode_handle_put(ep_fwnode); - - if (ret < 0) - return dev_err_probe(dev, ret, "Failed to parse sink endpoint"); - - num_data_lanes = vep.bus.mipi_csi2.num_data_lanes; - if (num_data_lanes < 1 || num_data_lanes > MAX96717_CSI_NLANES) - return dev_err_probe(dev, -EINVAL, - "Invalid data lanes must be 1 to 4\n"); - - priv->mipi_csi2 = vep.bus.mipi_csi2; - - return 0; -} - -static int max96717_probe(struct i2c_client *client) -{ - struct device *dev = &client->dev; - struct max96717_priv *priv; - int ret; - - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - priv->client = client; - priv->regmap = devm_cci_regmap_init_i2c(client, 16); - if (IS_ERR(priv->regmap)) { - ret = PTR_ERR(priv->regmap); - return dev_err_probe(dev, ret, "Failed to init regmap\n"); - } - - ret = max96717_parse_dt(priv); - if (ret) - return dev_err_probe(dev, ret, "Failed to parse the dt\n"); - - ret = max96717_hw_init(priv); - if (ret) - return dev_err_probe(dev, ret, - "Failed to initialize the hardware\n"); - - ret = max96717_gpiochip_probe(priv); - if (ret) - return dev_err_probe(&client->dev, ret, - "Failed to init gpiochip\n"); - - ret = max96717_register_clkout(priv); - if (ret) - return dev_err_probe(dev, ret, "Failed to register clkout\n"); - - ret = max96717_subdev_init(priv); - if (ret) - return dev_err_probe(dev, ret, - "Failed to initialize v4l2 subdev\n"); - - ret = max96717_i2c_mux_init(priv); - if (ret) { - dev_err_probe(dev, ret, "failed to add remote i2c adapter\n"); - max96717_subdev_uninit(priv); - } - - return ret; -} - -static void max96717_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct max96717_priv *priv = sd_to_max96717(sd); - - max96717_subdev_uninit(priv); - i2c_mux_del_adapters(priv->mux); -} - -static const struct of_device_id max96717_of_ids[] = { - { .compatible = "maxim,max96717f" }, - { } -}; -MODULE_DEVICE_TABLE(of, max96717_of_ids); - -static struct i2c_driver max96717_i2c_driver = { - .driver = { - .name = "max96717", - .of_match_table = max96717_of_ids, - }, - .probe = max96717_probe, - .remove = max96717_remove, -}; - -module_i2c_driver(max96717_i2c_driver); - -MODULE_DESCRIPTION("Maxim GMSL2 MAX96717 Serializer Driver"); -MODULE_AUTHOR("Julien Massot "); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/maxim-serdes/Kconfig b/drivers/media/i2c/maxim-serdes/Kconfig new file mode 100644 index 00000000000000..bbfe2d530dbc6d --- /dev/null +++ b/drivers/media/i2c/maxim-serdes/Kconfig @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: GPL-2.0 + +config VIDEO_MAXIM_SERDES + tristate "Maxim GMSL2/3 Serializer and Deserializer support" + depends on VIDEO_DEV + depends on I2C + select I2C_ATR + select I2C_MUX + select MEDIA_CONTROLLER + select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + help + This driver supports the Maxim GMSL2/3 common Serializer and + Deserializer framework. + + To compile this driver as a module, choose M here: the module + will be called max_serdes. + +config VIDEO_MAX96717 + tristate "Maxim MAX96717 Serializer support" + depends on COMMON_CLK + depends on I2C + depends on PINCTRL + select VIDEO_MAXIM_SERDES + select GENERIC_PINCONF + select GENERIC_PINCTRL_GROUPS + select GENERIC_PINMUX_FUNCTIONS + select GPIOLIB + help + This driver supports the Maxim MAX9295A, MAX96717, MAX96717F, + MAX96793 Serializers, which receive video on a MIPI CSI-2 + interface and output it on a GMSL2/3 link. + + To compile this driver as a module, choose M here: the module + will be called max96717. + +config VIDEO_MAX96724 + tristate "Maxim MAX96724 Quad Deserializer support" + depends on I2C + select VIDEO_MAXIM_SERDES + help + This driver supports the Maxim MAX96712, MAX96724, MAX96724F, + MAX96724R Quad Deserializers, which convert from four GMSL2 + links to up to four MIPI D-PHY or C-PHY outputs. + + To compile this driver as a module, choose M here: the module + will be called max96724. + +config VIDEO_MAX9296A + tristate "Maxim MAX9296A Dual Deserializer support" + depends on I2C + select VIDEO_MAXIM_SERDES + help + This driver supports the Maxim MAX9296A, MAX96716A, MAX96792A + Dual Deserializers, and the MAX96714, MAX96714F, MAX96714R + Single Deserializers, which convert from up to two GMSL2/3 + links to up to two MIPI D-PHY outputs. + + To compile this driver as a module, choose M here: the module + will be called max9296a. diff --git a/drivers/media/i2c/maxim-serdes/Makefile b/drivers/media/i2c/maxim-serdes/Makefile new file mode 100644 index 00000000000000..ae306bc33bfb98 --- /dev/null +++ b/drivers/media/i2c/maxim-serdes/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +max-serdes-objs := max_serdes.o max_ser.o max_des.o +obj-$(CONFIG_VIDEO_MAXIM_SERDES) += max-serdes.o +obj-$(CONFIG_VIDEO_MAX96717) += max96717.o +obj-$(CONFIG_VIDEO_MAX96724) += max96724.o +obj-$(CONFIG_VIDEO_MAX9296A) += max9296a.o diff --git a/drivers/media/i2c/maxim-serdes/max9296a.c b/drivers/media/i2c/maxim-serdes/max9296a.c new file mode 100644 index 00000000000000..0d9ef4c552a395 --- /dev/null +++ b/drivers/media/i2c/maxim-serdes/max9296a.c @@ -0,0 +1,1352 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Maxim MAX9296A Quad GMSL2 Deserializer Driver + * + * Copyright (C) 2025 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include + +#include + +#include "max_des.h" + +#define MAX9296A_REG0 0x0 + +#define MAX9296A_REG1 0x1 +#define MAX9296A_REG1_RX_RATE_A GENMASK(1, 0) +#define MAX9296A_REG1_RX_RATE_3GBPS 0b01 +#define MAX9296A_REG1_RX_RATE_6GBPS 0b10 +#define MAX9296A_REG1_RX_RATE_12GBPS 0b11 + +#define MAX9296A_REG2 0x2 +#define MAX9296A_REG2_VID_EN(p) BIT((p) + 4) + +#define MAX9296A_REG4 0x4 +#define MAX9296A_REG4_GMSL3_X(x) BIT((x) + 6) +#define MAX9296A_REG4_RX_RATE_B GENMASK(1, 0) + +#define MAX9296A_REG6 0x6 +#define MAX9296A_REG6_GMSL2_X(x) BIT((x) + 6) + +#define MAX9296A_CTRL0 0x10 +#define MAX9296A_CTRL0_LINK_CFG GENMASK(1, 0) +#define MAX9296A_CTRL0_AUTO_LINK BIT(4) +#define MAX9296A_CTRL0_RESET_ONESHOT BIT(5) +#define MAX9296A_CTRL0_RESET_ALL BIT(7) + +#define MAX9296A_CTRL2 0x12 +#define MAX9296A_CTRL2_RESET_ONESHOT_B BIT(5) + +#define MAX9296A_MIPI_TX0(x) (0x28 + (x) * 0x5000) +#define MAX9296A_MIPI_TX0_RX_FEC_EN BIT(1) + +#define MAX9296A_IO_CHK0 0x38 +#define MAX9296A_IO_CHK0_PIN_DRV_EN_0 GENMASK(1, 0) +#define MAX9296A_IO_CHK0_PIN_DRV_EN_0_25MHZ 0b00 +#define MAX9296A_IO_CHK0_PIN_DRV_EN_0_75MHZ 0b01 +#define MAX9296A_IO_CHK0_PIN_DRV_EN_0_USE_PIPE 0b10 + +#define MAX9296A_RX50(p) (0x50 + (p)) +#define MAX9296A_RX50_STR_SEL GENMASK(1, 0) + +#define MAX9296A_VIDEO_PIPE_EN 0x160 +#define MAX9296A_VIDEO_PIPE_EN_MASK(p) BIT(p) + +#define MAX9296A_VIDEO_PIPE_SEL 0x161 +#define MAX9296A_VIDEO_PIPE_SEL_STREAM(p) (GENMASK(1, 0) << ((p) * 3)) +#define MAX9296A_VIDEO_PIPE_SEL_LINK(p) BIT(2 + (p) * 3) + +#define MAX9296A_VPRBS(p) (0x1dc + (p) * 0x20) +#define MAX9296A_VPRBS_VIDEO_LOCK BIT(0) +#define MAX9296A_VPRBS_PATGEN_CLK_SRC BIT(7) +#define MAX9296A_VPRBS_PATGEN_CLK_SRC_150MHZ 0b0 +#define MAX9296A_VPRBS_PATGEN_CLK_SRC_600MHZ 0b1 + +#define MAX9296A_PATGEN_0 0x240 +#define MAX9296A_PATGEN_0_VTG_MODE GENMASK(1, 0) +#define MAX9296A_PATGEN_0_VTG_MODE_FREE_RUNNING 0b11 +#define MAX9296A_PATGEN_0_DE_INV BIT(2) +#define MAX9296A_PATGEN_0_HS_INV BIT(3) +#define MAX9296A_PATGEN_0_VS_INV BIT(4) +#define MAX9296A_PATGEN_0_GEN_DE BIT(5) +#define MAX9296A_PATGEN_0_GEN_HS BIT(6) +#define MAX9296A_PATGEN_0_GEN_VS BIT(7) + +#define MAX9296A_PATGEN_1 0x241 +#define MAX9296A_PATGEN_1_PATGEN_MODE GENMASK(5, 4) +#define MAX9296A_PATGEN_1_PATGEN_MODE_DISABLED 0b00 +#define MAX9296A_PATGEN_1_PATGEN_MODE_CHECKER 0b11 +#define MAX9296A_PATGEN_1_PATGEN_MODE_GRADIENT 0b10 + +#define MAX9296A_VS_DLY_2 0x242 +#define MAX9296A_VS_HIGH_2 0x245 +#define MAX9296A_VS_LOW_2 0x248 +#define MAX9296A_V2H_2 0x24b +#define MAX9296A_HS_HIGH_1 0x24e +#define MAX9296A_HS_LOW_1 0x250 +#define MAX9296A_HS_CNT_1 0x252 +#define MAX9296A_V2D_2 0x254 +#define MAX9296A_DE_HIGH_1 0x257 +#define MAX9296A_DE_LOW_1 0x259 +#define MAX9296A_DE_CNT_1 0x25b +#define MAX9296A_GRAD_INCR 0x25d +#define MAX9296A_CHKR_COLOR_A_L 0x25e +#define MAX9296A_CHKR_COLOR_B_L 0x261 +#define MAX9296A_CHKR_RPT_A 0x264 +#define MAX9296A_CHKR_RPT_B 0x265 +#define MAX9296A_CHKR_ALT 0x266 + +#define MAX9296A_BACKTOP12 0x313 +#define MAX9296A_BACKTOP12_CSI_OUT_EN BIT(1) + +#define MAX9296A_BACKTOP21 0x31c +#define MAX9296A_BACKTOP21_BPP8DBL(p) BIT(4 + (p)) + +#define MAX9296A_BACKTOP22(x) (0x31d + (x) * 0x3) +#define MAX9296A_BACKTOP22_PHY_CSI_TX_DPLL GENMASK(4, 0) +#define MAX9296A_BACKTOP22_PHY_CSI_TX_DPLL_EN BIT(5) + +#define MAX9296A_BACKTOP24 0x31f +#define MAX9296A_BACKTOP24_BPP8DBL_MODE(p) BIT(4 + (p)) + +#define MAX9296A_BACKTOP32 0x327 +#define MAX9296A_BACKTOP32_BPP10DBL(p) BIT(p) +#define MAX9296A_BACKTOP32_BPP10DBL_MODE(p) BIT(4 + (p)) + +#define MAX9296A_BACKTOP33 0x328 +#define MAX9296A_BACKTOP32_BPP12DBL(p) BIT(p) + +#define MAX9296A_MIPI_PHY0 0x330 +#define MAX9296A_MIPI_PHY0_FORCE_CSI_OUT_EN BIT(7) + +#define MAX9296A_MIPI_PHY2 0x332 +#define MAX9296A_MIPI_PHY2_PHY_STDBY_N(x) (GENMASK(5, 4) << ((x) * 2)) + +#define MAX9296A_MIPI_PHY3(x) (0x333 + (x)) +#define MAX9296A_MIPI_PHY3_PHY_LANE_MAP_4 GENMASK(7, 0) + +#define MAX9296A_MIPI_PHY5(x) (0x335 + (x)) +#define MAX9296A_MIPI_PHY5_PHY_POL_MAP_0_1 GENMASK(1, 0) +#define MAX9296A_MIPI_PHY5_PHY_POL_MAP_2_3 GENMASK(4, 3) +#define MAX9296A_MIPI_PHY5_PHY_POL_MAP_CLK(x) BIT((x) == 0 ? 5 : 2) + +#define MAX9296A_MIPI_PHY18 0x342 +#define MAX9296A_MIPI_PHY18_CSI2_TX_PKT_CNT(x) (GENMASK(3, 0) << (4 * (x))) + +#define MAX9296A_MIPI_PHY20(x) (0x344 + (x)) + +#define MAX9296A_MIPI_TX3(x) (0x403 + (x) * 0x40) +#define MAX9296A_MIPI_TX3_DESKEW_INIT_8X32K FIELD_PREP(GENMASK(2, 0), 0b001) +#define MAX9296A_MIPI_TX3_DESKEW_INIT_AUTO BIT(7) + +#define MAX9296A_MIPI_TX4(x) (0x404 + (x) * 0x40) +#define MAX9296A_MIPI_TX4_DESKEW_PER_2K FIELD_PREP(GENMASK(2, 0), 0b001) +#define MAX9296A_MIPI_TX4_DESKEW_PER_AUTO BIT(7) + +#define MAX9296A_MIPI_TX10(x) (0x40a + (x) * 0x40) +#define MAX9296A_MIPI_TX10_CSI2_LANE_CNT GENMASK(7, 6) +#define MAX9296A_MIPI_TX10_CSI2_CPHY_EN BIT(5) + +#define MAX9296A_MIPI_TX11(p) (0x40b + (p) * 0x40) +#define MAX9296A_MIPI_TX12(p) (0x40c + (p) * 0x40) + +#define MAX9296A_MIPI_TX13(p, x) (0x40d + (p) * 0x40 + (x) * 0x2) +#define MAX9296A_MIPI_TX13_MAP_SRC_DT GENMASK(5, 0) +#define MAX9296A_MIPI_TX13_MAP_SRC_VC GENMASK(7, 6) + +#define MAX9296A_MIPI_TX14(p, x) (0x40e + (p) * 0x40 + (x) * 0x2) +#define MAX9296A_MIPI_TX14_MAP_DST_DT GENMASK(5, 0) +#define MAX9296A_MIPI_TX14_MAP_DST_VC GENMASK(7, 6) + +#define MAX9296A_MIPI_TX45(p, x) (0x42d + (p) * 0x40 + (x) / 4) +#define MAX9296A_MIPI_TX45_MAP_DPHY_DEST(x) (GENMASK(1, 0) << (2 * ((x) % 4))) + +#define MAX9296A_MIPI_TX51(x) (0x433 + (x) * 0x40) +#define MAX9296A_MIPI_TX51_ALT_MEM_MAP_12 BIT(0) +#define MAX9296A_MIPI_TX51_ALT_MEM_MAP_8 BIT(1) +#define MAX9296A_MIPI_TX51_ALT_MEM_MAP_10 BIT(2) +#define MAX9296A_MIPI_TX51_ALT2_MEM_MAP_8 BIT(4) + +#define MAX9296A_MIPI_TX52(x) (0x434 + (x) * 0x40) +#define MAX9296A_MIPI_TX52_TUN_DEST BIT(1) +#define MAX9296A_MIPI_TX52_TUN_EN BIT(0) + +#define MAX9296A_GMSL1_EN 0xf00 +#define MAX9296A_GMSL1_EN_LINK_EN GENMASK(1, 0) + +#define MAX9296A_RLMS3E(x) (0x143e + (x) * 0x100) +#define MAX9296A_RLMS3F(x) (0x143f + (x) * 0x100) +#define MAX9296A_RLMS49(x) (0x1449 + (x) * 0x100) +#define MAX9296A_RLMS7E(x) (0x147e + (x) * 0x100) +#define MAX9296A_RLMS7F(x) (0x147f + (x) * 0x100) +#define MAX9296A_RLMSA3(x) (0x14a3 + (x) * 0x100) +#define MAX9296A_RLMSA5(x) (0x14a5 + (x) * 0x100) +#define MAX9296A_RLMSD8(x) (0x14d8 + (x) * 0x100) + +#define MAX9296A_DPLL_0(x) (0x1c00 + (x) * 0x100) +#define MAX9296A_DPLL_0_CONFIG_SOFT_RST_N BIT(0) + +#define MAX9296A_PIPES_NUM 4 +#define MAX9296A_PHYS_NUM 2 + +static const struct regmap_config max9296a_i2c_regmap = { + .reg_bits = 16, + .val_bits = 8, +}; + +struct max9296a_priv { + struct max_des des; + const struct max9296a_chip_info *info; + + struct device *dev; + struct i2c_client *client; + struct regmap *regmap; + + struct gpio_desc *gpiod_pwdn; +}; + +struct max9296a_chip_info { + const struct max_des_ops *ops; + const struct reg_sequence *rlms_adjust_sequence; + unsigned int rlms_adjust_sequence_len; + unsigned int max_register; + unsigned int pipe_hw_ids[MAX9296A_PIPES_NUM]; + unsigned int phy_hw_ids[MAX9296A_PHYS_NUM]; + bool use_atr; + bool has_per_link_reset; + bool phy0_lanes_0_1_on_second_phy; + bool polarity_on_physical_lanes; + bool supports_cphy; + bool supports_phy_log; +}; + +#define des_to_priv(_des) \ + container_of(_des, struct max9296a_priv, des) + +static int max9296a_wait_for_device(struct max9296a_priv *priv) +{ + unsigned int i; + int ret; + + for (i = 0; i < 10; i++) { + unsigned int val; + + ret = regmap_read(priv->regmap, MAX9296A_REG0, &val); + if (!ret && val) + return 0; + + msleep(100); + + dev_err(priv->dev, "Retry %u waiting for deserializer: %d\n", i, ret); + } + + return ret; +} + +static int max9296a_reset(struct max9296a_priv *priv) +{ + int ret; + + ret = max9296a_wait_for_device(priv); + if (ret) + return ret; + + ret = regmap_set_bits(priv->regmap, MAX9296A_CTRL0, + MAX9296A_CTRL0_RESET_ALL); + if (ret) + return ret; + + msleep(100); + + return max9296a_wait_for_device(priv); +} + +static unsigned int max9296a_pipe_id(struct max9296a_priv *priv, + struct max_des_pipe *pipe) +{ + return priv->info->pipe_hw_ids[pipe->index]; +} + +static unsigned int max9296a_phy_id(struct max9296a_priv *priv, + struct max_des_phy *phy) +{ + return priv->info->phy_hw_ids[phy->index]; +} + +static int __maybe_unused max9296a_reg_read(struct max_des *des, unsigned int reg, + unsigned int *val) +{ + struct max9296a_priv *priv = des_to_priv(des); + + return regmap_read(priv->regmap, reg, val); +} + +static int __maybe_unused max9296a_reg_write(struct max_des *des, unsigned int reg, + unsigned int val) +{ + struct max9296a_priv *priv = des_to_priv(des); + + return regmap_write(priv->regmap, reg, val); +} + +static int max9626a_log_pipe_status(struct max_des *des, + struct max_des_pipe *pipe) +{ + struct max9296a_priv *priv = des_to_priv(des); + unsigned int index = max9296a_pipe_id(priv, pipe); + unsigned int val; + int ret; + + ret = regmap_read(priv->regmap, MAX9296A_VPRBS(index), &val); + if (ret) + return ret; + + dev_info(priv->dev, "\tvideo_lock: %u\n", + !!(val & MAX9296A_VPRBS_VIDEO_LOCK)); + + return 0; +} + +static int max9296a_log_phy_status(struct max_des *des, + struct max_des_phy *phy) +{ + struct max9296a_priv *priv = des_to_priv(des); + unsigned int index = phy->index; + unsigned int val; + int ret; + + if (!priv->info->supports_phy_log) + return 0; + + ret = regmap_read(priv->regmap, MAX9296A_MIPI_PHY18, &val); + if (ret) + return ret; + + dev_info(priv->dev, "\tcsi2_pkt_cnt: %lu\n", + field_get(MAX9296A_MIPI_PHY18_CSI2_TX_PKT_CNT(index), val)); + + ret = regmap_read(priv->regmap, MAX9296A_MIPI_PHY20(index), &val); + if (ret) + return ret; + + dev_info(priv->dev, "\tphy_pkt_cnt: %u\n", val); + + return 0; +} + +static int max9296a_set_enable(struct max_des *des, bool enable) +{ + struct max9296a_priv *priv = des_to_priv(des); + + return regmap_assign_bits(priv->regmap, MAX9296A_BACKTOP12, + MAX9296A_BACKTOP12_CSI_OUT_EN, enable); +} + +static int max9296a_init_tpg(struct max_des *des) +{ + const struct reg_sequence regs[] = { + { MAX9296A_GRAD_INCR, MAX_SERDES_GRAD_INCR }, + REG_SEQUENCE_3_LE(MAX9296A_CHKR_COLOR_A_L, + MAX_SERDES_CHECKER_COLOR_A), + REG_SEQUENCE_3_LE(MAX9296A_CHKR_COLOR_B_L, + MAX_SERDES_CHECKER_COLOR_B), + { MAX9296A_CHKR_RPT_A, MAX_SERDES_CHECKER_SIZE }, + { MAX9296A_CHKR_RPT_B, MAX_SERDES_CHECKER_SIZE }, + { MAX9296A_CHKR_ALT, MAX_SERDES_CHECKER_SIZE }, + }; + struct max9296a_priv *priv = des_to_priv(des); + + return regmap_multi_reg_write(priv->regmap, regs, ARRAY_SIZE(regs)); +} + +static int max9296a_init(struct max_des *des) +{ + struct max9296a_priv *priv = des_to_priv(des); + int ret; + + if (priv->info->rlms_adjust_sequence) { + ret = regmap_multi_reg_write(priv->regmap, + priv->info->rlms_adjust_sequence, + priv->info->rlms_adjust_sequence_len); + if (ret) + return ret; + } + + return max9296a_init_tpg(des); +} + +static int max9296a_init_phy(struct max_des *des, struct max_des_phy *phy) +{ + struct max9296a_priv *priv = des_to_priv(des); + bool is_cphy = phy->bus_type == V4L2_MBUS_CSI2_CPHY; + unsigned int num_data_lanes = phy->mipi.num_data_lanes; + unsigned int dpll_freq = phy->link_frequency * 2; + unsigned int num_hw_data_lanes; + unsigned int hw_index = max9296a_phy_id(priv, phy); + unsigned int index = phy->index; + unsigned int used_data_lanes = 0; + unsigned int val; + unsigned int i; + int ret; + + if (is_cphy && !priv->info->supports_cphy) { + dev_err(priv->dev, "CPHY not supported\n"); + return -EINVAL; + } + + num_hw_data_lanes = max_des_phy_hw_data_lanes(des, phy); + + /* + * MAX9296A has four PHYs, but does not support single-PHY configurations, + * only double-PHY configurations, even when only using two lanes. + * For PHY 0 + PHY 1, PHY 1 is the master PHY. + * For PHY 2 + PHY 3, PHY 2 is the master PHY. + * Clock is always on the master PHY. + * For first pair of PHYs, first lanes are on the master PHY. + * For second pair of PHYs, first lanes are on the master PHY too. + * + * PHY 0 + 1 + * CLK = PHY 1 + * PHY1 Lane 0 = D0 + * PHY1 Lane 1 = D1 + * PHY0 Lane 0 = D2 + * PHY0 Lane 1 = D3 + * + * PHY 2 + 3 + * CLK = PHY 2 + * PHY2 Lane 0 = D0 + * PHY2 Lane 1 = D1 + * PHY3 Lane 0 = D2 + * PHY3 Lane 1 = D3 + * + * MAX96714 only has two PHYs which cannot support single-PHY configurations. + * Clock is always on the master PHY, first lanes are on PHY 0, even if + * PHY 1 is the master PHY. + * + * PHY 0 + 1 + * CLK = PHY 1 + * PHY0 Lane 0 = D0 + * PHY0 Lane 1 = D1 + * PHY1 Lane 0 = D2 + * PHY1 Lane 1 = D3 + */ + + /* Configure a lane count. */ + ret = regmap_update_bits(priv->regmap, MAX9296A_MIPI_TX10(hw_index), + MAX9296A_MIPI_TX10_CSI2_LANE_CNT, + FIELD_PREP(MAX9296A_MIPI_TX10_CSI2_LANE_CNT, + num_data_lanes - 1)); + if (ret) + return ret; + + ret = regmap_assign_bits(priv->regmap, MAX9296A_MIPI_TX10(hw_index), + MAX9296A_MIPI_TX10_CSI2_CPHY_EN, is_cphy); + if (ret) + return ret; + + /* Configure lane mapping. */ + /* + * The lane of each PHY can be mapped to physical lanes 0, 1, 2, and 3. + * This mapping is exclusive, multiple lanes, even if unused cannot be + * mapped to the same physical lane. + * Each lane mapping is represented as two bits. + */ + val = 0; + for (i = 0; i < num_hw_data_lanes ; i++) { + unsigned int map; + + if (i < num_data_lanes) + map = phy->mipi.data_lanes[i] - 1; + else + map = ffz(used_data_lanes); + + val |= map << (i * 2); + used_data_lanes |= BIT(map); + } + + if (phy->index == 0 && priv->info->phy0_lanes_0_1_on_second_phy) + val = ((val & 0xf) << 4) | ((val >> 4) & 0xf); + + ret = regmap_update_bits(priv->regmap, MAX9296A_MIPI_PHY3(index), + MAX9296A_MIPI_PHY3_PHY_LANE_MAP_4, + FIELD_PREP(MAX9296A_MIPI_PHY3_PHY_LANE_MAP_4, val)); + if (ret) + return ret; + + /* + * Configure lane polarity. + * + * PHY 0 and 1 are on register 0x335. + * PHY 2 and 3 are on register 0x336. + * + * Each PHY has 3 bits of polarity configuration. + * + * On MAX9296A, each bit represents the lane polarity of logical lanes. + * Each of these lanes can be mapped to any physical lane. + * 0th bit is for lane 0. + * 1st bit is for lane 1. + * 2nd bit is for clock lane. + * + * On MAX96714, each bit represents the lane polarity of physical lanes. + * 0th bit for physical lane 0. + * 1st bit for physical lane 1. + * 2nd bit for clock lane of PHY 0, the slave PHY, which is unused. + * + * 3rd bit for physical lane 2. + * 4th bit for physical lane 3. + * 5th bit for clock lane of PHY 1, the master PHY. + */ + + for (i = 0, val = 0; i < num_data_lanes; i++) { + unsigned int map; + + if (!phy->mipi.lane_polarities[i + 1]) + continue; + + /* + * The numbers inside the data_lanes array specify the hardware + * lane each logical lane maps to. + * If polarity is set for the physical lanes, retrieve the + * physical lane matching the logical lane from data_lanes. + * Otherwise, when polarity is set for the logical lanes + * the index of the polarity can be used. + */ + + if (priv->info->polarity_on_physical_lanes) + map = phy->mipi.data_lanes[i] - 1; + else + map = i; + + val |= BIT(map); + } + + if (phy->index == 0 && priv->info->phy0_lanes_0_1_on_second_phy) + val = ((val & 0x3) << 2) | ((val >> 2) & 0x3); + + ret = regmap_update_bits(priv->regmap, MAX9296A_MIPI_PHY5(index), + MAX9296A_MIPI_PHY5_PHY_POL_MAP_0_1 | + MAX9296A_MIPI_PHY5_PHY_POL_MAP_2_3, + FIELD_PREP(MAX9296A_MIPI_PHY5_PHY_POL_MAP_0_1, val) | + FIELD_PREP(MAX9296A_MIPI_PHY5_PHY_POL_MAP_2_3, val >> 2)); + if (ret) + return ret; + + ret = regmap_assign_bits(priv->regmap, MAX9296A_MIPI_PHY5(index), + MAX9296A_MIPI_PHY5_PHY_POL_MAP_CLK(index), + phy->mipi.lane_polarities[0]); + if (ret) + return ret; + + /* Put DPLL block into reset. */ + ret = regmap_clear_bits(priv->regmap, MAX9296A_DPLL_0(hw_index), + MAX9296A_DPLL_0_CONFIG_SOFT_RST_N); + if (ret) + return ret; + + /* Set DPLL frequency. */ + ret = regmap_update_bits(priv->regmap, MAX9296A_BACKTOP22(hw_index), + MAX9296A_BACKTOP22_PHY_CSI_TX_DPLL, + FIELD_PREP(MAX9296A_BACKTOP22_PHY_CSI_TX_DPLL, + div_u64(dpll_freq, 100000000))); + if (ret) + return ret; + + /* Enable DPLL frequency. */ + ret = regmap_set_bits(priv->regmap, MAX9296A_BACKTOP22(hw_index), + MAX9296A_BACKTOP22_PHY_CSI_TX_DPLL_EN); + if (ret) + return ret; + + /* Pull DPLL block out of reset. */ + ret = regmap_set_bits(priv->regmap, MAX9296A_DPLL_0(hw_index), + MAX9296A_DPLL_0_CONFIG_SOFT_RST_N); + if (ret) + return ret; + + if (dpll_freq > 1500000000ull) { + /* Enable initial deskew with 2 x 32k UI. */ + ret = regmap_write(priv->regmap, MAX9296A_MIPI_TX3(hw_index), + MAX9296A_MIPI_TX3_DESKEW_INIT_AUTO | + MAX9296A_MIPI_TX3_DESKEW_INIT_8X32K); + if (ret) + return ret; + + /* Enable periodic deskew with 2 x 1k UI.. */ + ret = regmap_write(priv->regmap, MAX9296A_MIPI_TX4(hw_index), + MAX9296A_MIPI_TX4_DESKEW_PER_AUTO | + MAX9296A_MIPI_TX4_DESKEW_PER_2K); + if (ret) + return ret; + } else { + /* Disable initial deskew. */ + ret = regmap_write(priv->regmap, MAX9296A_MIPI_TX3(hw_index), 0x0); + if (ret) + return ret; + + /* Disable periodic deskew. */ + ret = regmap_write(priv->regmap, MAX9296A_MIPI_TX4(hw_index), 0x0); + if (ret) + return ret; + } + + return 0; +} + +static int max9296a_set_phy_mode(struct max_des *des, struct max_des_phy *phy, + struct max_des_phy_mode *mode) +{ + struct max9296a_priv *priv = des_to_priv(des); + unsigned int phy_id = max9296a_phy_id(priv, phy); + int ret; + + /* Set alternate memory map modes. */ + ret = regmap_assign_bits(priv->regmap, MAX9296A_MIPI_TX51(phy_id), + MAX9296A_MIPI_TX51_ALT_MEM_MAP_12, + mode->alt_mem_map12); + if (ret) + return ret; + + ret = regmap_assign_bits(priv->regmap, MAX9296A_MIPI_TX51(phy_id), + MAX9296A_MIPI_TX51_ALT_MEM_MAP_8, + mode->alt_mem_map8); + if (ret) + return ret; + + ret = regmap_assign_bits(priv->regmap, MAX9296A_MIPI_TX51(phy_id), + MAX9296A_MIPI_TX51_ALT_MEM_MAP_10, + mode->alt_mem_map10); + if (ret) + return ret; + + return regmap_assign_bits(priv->regmap, MAX9296A_MIPI_TX51(phy_id), + MAX9296A_MIPI_TX51_ALT2_MEM_MAP_8, + mode->alt2_mem_map8); +} + +static int max9296a_set_phy_enable(struct max_des *des, struct max_des_phy *phy, + bool enable) +{ + struct max9296a_priv *priv = des_to_priv(des); + + return regmap_assign_bits(priv->regmap, MAX9296A_MIPI_PHY2, + MAX9296A_MIPI_PHY2_PHY_STDBY_N(phy->index), enable); +} + +static int max9296a_set_pipe_remap(struct max_des *des, + struct max_des_pipe *pipe, + unsigned int i, + struct max_des_remap *remap) +{ + struct max9296a_priv *priv = des_to_priv(des); + struct max_des_phy *phy = &des->phys[remap->phy]; + unsigned int phy_id = max9296a_phy_id(priv, phy); + unsigned int index = max9296a_pipe_id(priv, pipe); + int ret; + + /* Set source Data Type and Virtual Channel. */ + /* TODO: implement extended Virtual Channel. */ + ret = regmap_write(priv->regmap, MAX9296A_MIPI_TX13(index, i), + FIELD_PREP(MAX9296A_MIPI_TX13_MAP_SRC_DT, + remap->from_dt) | + FIELD_PREP(MAX9296A_MIPI_TX13_MAP_SRC_VC, + remap->from_vc)); + if (ret) + return ret; + + /* Set destination Data Type and Virtual Channel. */ + /* TODO: implement extended Virtual Channel. */ + ret = regmap_write(priv->regmap, MAX9296A_MIPI_TX14(index, i), + FIELD_PREP(MAX9296A_MIPI_TX14_MAP_DST_DT, + remap->to_dt) | + FIELD_PREP(MAX9296A_MIPI_TX14_MAP_DST_VC, + remap->to_vc)); + if (ret) + return ret; + + /* Set destination PHY. */ + return regmap_update_bits(priv->regmap, MAX9296A_MIPI_TX45(index, i), + MAX9296A_MIPI_TX45_MAP_DPHY_DEST(i), + field_prep(MAX9296A_MIPI_TX45_MAP_DPHY_DEST(i), + phy_id)); +} + +static int max9296a_set_pipe_remaps_enable(struct max_des *des, + struct max_des_pipe *pipe, + unsigned int mask) +{ + struct max9296a_priv *priv = des_to_priv(des); + unsigned int index = max9296a_pipe_id(priv, pipe); + int ret; + + ret = regmap_write(priv->regmap, MAX9296A_MIPI_TX11(index), mask); + if (ret) + return ret; + + return regmap_write(priv->regmap, MAX9296A_MIPI_TX12(index), mask >> 8); +} + +static int max9296a_set_pipe_enable(struct max_des *des, struct max_des_pipe *pipe, + bool enable) +{ + struct max9296a_priv *priv = des_to_priv(des); + unsigned int index = max9296a_pipe_id(priv, pipe); + + return regmap_assign_bits(priv->regmap, MAX9296A_REG2, + MAX9296A_REG2_VID_EN(index), enable); +} + +static int max96714_set_pipe_enable(struct max_des *des, struct max_des_pipe *pipe, + bool enable) +{ + struct max9296a_priv *priv = des_to_priv(des); + unsigned int index = max9296a_pipe_id(priv, pipe); + + return regmap_assign_bits(priv->regmap, MAX9296A_VIDEO_PIPE_EN, + MAX9296A_VIDEO_PIPE_EN_MASK(index - 1), enable); +} + +static int max96714_set_pipe_tunnel_enable(struct max_des *des, + struct max_des_pipe *pipe, bool enable) +{ + struct max9296a_priv *priv = des_to_priv(des); + unsigned int index = max9296a_pipe_id(priv, pipe); + + return regmap_assign_bits(priv->regmap, MAX9296A_MIPI_TX52(index), + MAX9296A_MIPI_TX52_TUN_EN, enable); +} + +static int max9296a_set_pipe_stream_id(struct max_des *des, struct max_des_pipe *pipe, + unsigned int stream_id) +{ + struct max9296a_priv *priv = des_to_priv(des); + unsigned int index = max9296a_pipe_id(priv, pipe); + + return regmap_update_bits(priv->regmap, MAX9296A_RX50(index), MAX9296A_RX50_STR_SEL, + FIELD_PREP(MAX9296A_RX50_STR_SEL, stream_id)); +} + +static int max96714_set_pipe_stream_id(struct max_des *des, struct max_des_pipe *pipe, + unsigned int stream_id) +{ + struct max9296a_priv *priv = des_to_priv(des); + unsigned int index = pipe->index; + + return regmap_update_bits(priv->regmap, MAX9296A_VIDEO_PIPE_SEL, + MAX9296A_VIDEO_PIPE_SEL_STREAM(index), + field_prep(MAX9296A_VIDEO_PIPE_SEL_STREAM(index), + stream_id)); +} + +static int max96716a_set_pipe_link(struct max_des *des, struct max_des_pipe *pipe, + struct max_des_link *link) +{ + struct max9296a_priv *priv = des_to_priv(des); + unsigned int index = pipe->index; + + return regmap_update_bits(priv->regmap, MAX9296A_VIDEO_PIPE_SEL, + MAX9296A_VIDEO_PIPE_SEL_LINK(index), + field_prep(MAX9296A_VIDEO_PIPE_SEL_LINK(index), + link->index)); +} + +static int max96716a_set_pipe_tunnel_phy(struct max_des *des, + struct max_des_pipe *pipe, + struct max_des_phy *phy) +{ + struct max9296a_priv *priv = des_to_priv(des); + unsigned int index = max9296a_pipe_id(priv, pipe); + + return regmap_assign_bits(priv->regmap, MAX9296A_MIPI_TX52(index), + MAX9296A_MIPI_TX52_TUN_DEST, phy->index); +} + +static int max9296a_set_pipe_mode(struct max_des *des, + struct max_des_pipe *pipe, + struct max_des_pipe_mode *mode) +{ + struct max9296a_priv *priv = des_to_priv(des); + unsigned int index = max9296a_pipe_id(priv, pipe); + int ret; + + /* Set 8bit double mode. */ + ret = regmap_assign_bits(priv->regmap, MAX9296A_BACKTOP21, + MAX9296A_BACKTOP21_BPP8DBL(index), mode->dbl8); + if (ret) + return ret; + + ret = regmap_assign_bits(priv->regmap, MAX9296A_BACKTOP24, + MAX9296A_BACKTOP24_BPP8DBL_MODE(index), + mode->dbl8mode); + if (ret) + return ret; + + /* Set 10bit double mode. */ + ret = regmap_assign_bits(priv->regmap, MAX9296A_BACKTOP32, + MAX9296A_BACKTOP32_BPP10DBL(index), mode->dbl10); + if (ret) + return ret; + + ret = regmap_assign_bits(priv->regmap, MAX9296A_BACKTOP32, + MAX9296A_BACKTOP32_BPP10DBL_MODE(index), + mode->dbl10mode); + if (ret) + return ret; + + /* Set 12bit double mode. */ + /* TODO: check support for double mode on MAX96714. */ + return regmap_assign_bits(priv->regmap, MAX9296A_BACKTOP33, + MAX9296A_BACKTOP32_BPP12DBL(index), mode->dbl12); +} + +static int max9296a_reset_link(struct max9296a_priv *priv, unsigned int index) +{ + unsigned int reg, mask; + + if (index == 0) { + reg = MAX9296A_CTRL0; + mask = MAX9296A_CTRL0_RESET_ONESHOT; + } else { + reg = MAX9296A_CTRL2; + mask = MAX9296A_CTRL2_RESET_ONESHOT_B; + } + + return regmap_set_bits(priv->regmap, reg, mask); +} + +static int max9296a_select_links(struct max_des *des, unsigned int mask) +{ + struct max9296a_priv *priv = des_to_priv(des); + int ret; + + if (des->ops->num_links == 1) + return 0; + + if (!mask) { + dev_err(priv->dev, "Disable all links unsupported\n"); + return -EINVAL; + } + + ret = regmap_update_bits(priv->regmap, MAX9296A_GMSL1_EN, + MAX9296A_GMSL1_EN_LINK_EN, + FIELD_PREP(MAX9296A_GMSL1_EN_LINK_EN, mask)); + if (ret) + return ret; + + ret = regmap_update_bits(priv->regmap, MAX9296A_CTRL0, + MAX9296A_CTRL0_AUTO_LINK | + MAX9296A_CTRL0_LINK_CFG | + MAX9296A_CTRL0_RESET_ONESHOT, + FIELD_PREP(MAX9296A_CTRL0_LINK_CFG, mask) | + FIELD_PREP(MAX9296A_CTRL0_RESET_ONESHOT, 1)); + if (ret) + return ret; + + if (priv->info->has_per_link_reset) { + ret = max9296a_reset_link(priv, 1); + if (ret) + return ret; + } + + msleep(200); + + return 0; +} + +static int max9296a_set_link_version(struct max_des *des, + struct max_des_link *link, + enum max_serdes_gmsl_version version) +{ + struct max9296a_priv *priv = des_to_priv(des); + unsigned int index = link->index; + bool gmsl3_en = version == MAX_SERDES_GMSL_3_12GBPS; + unsigned int reg, mask, val; + int ret; + + if (des->ops->needs_single_link_version) + index = 0; + + if (index == 0) { + reg = MAX9296A_REG1; + mask = MAX9296A_REG1_RX_RATE_A; + } else { + reg = MAX9296A_REG4; + mask = MAX9296A_REG4_RX_RATE_B; + } + + if (version == MAX_SERDES_GMSL_3_12GBPS) + val = MAX9296A_REG1_RX_RATE_12GBPS; + else if (version == MAX_SERDES_GMSL_2_6GBPS) + val = MAX9296A_REG1_RX_RATE_6GBPS; + else + val = MAX9296A_REG1_RX_RATE_3GBPS; + + ret = regmap_update_bits(priv->regmap, reg, mask, field_prep(mask, val)); + if (ret) + return ret; + + if (!(des->ops->versions & BIT(MAX_SERDES_GMSL_3_12GBPS))) + return 0; + + ret = regmap_assign_bits(priv->regmap, MAX9296A_MIPI_TX0(index), + MAX9296A_MIPI_TX0_RX_FEC_EN, gmsl3_en); + if (ret) + return ret; + + ret = regmap_assign_bits(priv->regmap, MAX9296A_REG6, + MAX9296A_REG6_GMSL2_X(index), !gmsl3_en); + if (ret) + return ret; + + return regmap_assign_bits(priv->regmap, MAX9296A_REG4, + MAX9296A_REG4_GMSL3_X(index), gmsl3_en); +} + +static int max9296a_set_tpg_timings(struct max9296a_priv *priv, + const struct max_serdes_tpg_timings *tm) +{ + const struct reg_sequence regs[] = { + REG_SEQUENCE_3(MAX9296A_VS_DLY_2, tm->vs_dly), + REG_SEQUENCE_3(MAX9296A_VS_HIGH_2, tm->vs_high), + REG_SEQUENCE_3(MAX9296A_VS_LOW_2, tm->vs_low), + REG_SEQUENCE_3(MAX9296A_V2H_2, tm->v2h), + REG_SEQUENCE_2(MAX9296A_HS_HIGH_1, tm->hs_high), + REG_SEQUENCE_2(MAX9296A_HS_LOW_1, tm->hs_low), + REG_SEQUENCE_2(MAX9296A_HS_CNT_1, tm->hs_cnt), + REG_SEQUENCE_3(MAX9296A_V2D_2, tm->v2d), + REG_SEQUENCE_2(MAX9296A_DE_HIGH_1, tm->de_high), + REG_SEQUENCE_2(MAX9296A_DE_LOW_1, tm->de_low), + REG_SEQUENCE_2(MAX9296A_DE_CNT_1, tm->de_cnt), + }; + int ret; + + ret = regmap_multi_reg_write(priv->regmap, regs, ARRAY_SIZE(regs)); + if (ret) + return ret; + + return regmap_write(priv->regmap, MAX9296A_PATGEN_0, + FIELD_PREP(MAX9296A_PATGEN_0_VTG_MODE, + MAX9296A_PATGEN_0_VTG_MODE_FREE_RUNNING) | + FIELD_PREP(MAX9296A_PATGEN_0_DE_INV, tm->de_inv) | + FIELD_PREP(MAX9296A_PATGEN_0_HS_INV, tm->hs_inv) | + FIELD_PREP(MAX9296A_PATGEN_0_VS_INV, tm->vs_inv) | + FIELD_PREP(MAX9296A_PATGEN_0_GEN_DE, tm->gen_de) | + FIELD_PREP(MAX9296A_PATGEN_0_GEN_HS, tm->gen_hs) | + FIELD_PREP(MAX9296A_PATGEN_0_GEN_VS, tm->gen_vs)); +} + +static int max9296a_set_tpg_clk(struct max9296a_priv *priv, u32 clock) +{ + bool patgen_clk_src = 0; + u8 pin_drv_en; + int ret; + + switch (clock) { + case 25000000: + pin_drv_en = MAX9296A_IO_CHK0_PIN_DRV_EN_0_25MHZ; + break; + case 75000000: + pin_drv_en = MAX9296A_IO_CHK0_PIN_DRV_EN_0_75MHZ; + break; + case 150000000: + pin_drv_en = MAX9296A_IO_CHK0_PIN_DRV_EN_0_USE_PIPE; + patgen_clk_src = MAX9296A_VPRBS_PATGEN_CLK_SRC_150MHZ; + break; + case 600000000: + pin_drv_en = MAX9296A_IO_CHK0_PIN_DRV_EN_0_USE_PIPE; + patgen_clk_src = MAX9296A_VPRBS_PATGEN_CLK_SRC_600MHZ; + break; + case 0: + return 0; + default: + return -EINVAL; + } + + /* + * TPG data is always injected on link 0, which is always routed to + * pipe 0. + */ + ret = regmap_update_bits(priv->regmap, MAX9296A_VPRBS(0), + MAX9296A_VPRBS_PATGEN_CLK_SRC, + FIELD_PREP(MAX9296A_VPRBS_PATGEN_CLK_SRC, + patgen_clk_src)); + if (ret) + return ret; + + return regmap_update_bits(priv->regmap, MAX9296A_IO_CHK0, + MAX9296A_IO_CHK0_PIN_DRV_EN_0, + FIELD_PREP(MAX9296A_IO_CHK0_PIN_DRV_EN_0, + pin_drv_en)); +} + +static int max9296a_set_tpg_mode(struct max9296a_priv *priv, bool enable) +{ + unsigned int patgen_mode; + + switch (priv->des.tpg_pattern) { + case MAX_SERDES_TPG_PATTERN_GRADIENT: + patgen_mode = MAX9296A_PATGEN_1_PATGEN_MODE_GRADIENT; + break; + case MAX_SERDES_TPG_PATTERN_CHECKERBOARD: + patgen_mode = MAX9296A_PATGEN_1_PATGEN_MODE_CHECKER; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(priv->regmap, MAX9296A_PATGEN_1, + MAX9296A_PATGEN_1_PATGEN_MODE, + FIELD_PREP(MAX9296A_PATGEN_1_PATGEN_MODE, + enable ? patgen_mode + : MAX9296A_PATGEN_1_PATGEN_MODE_DISABLED)); +} + +static int max9296a_set_tpg(struct max_des *des, + const struct max_serdes_tpg_entry *entry) +{ + struct max9296a_priv *priv = des_to_priv(des); + struct max_serdes_tpg_timings timings = { 0 }; + int ret; + + ret = max_serdes_get_tpg_timings(entry, &timings); + if (ret) + return ret; + + ret = max9296a_set_tpg_timings(priv, &timings); + if (ret) + return ret; + + ret = max9296a_set_tpg_clk(priv, timings.clock); + if (ret) + return ret; + + ret = max9296a_set_tpg_mode(priv, entry); + if (ret) + return ret; + + return regmap_assign_bits(priv->regmap, MAX9296A_MIPI_PHY0, + MAX9296A_MIPI_PHY0_FORCE_CSI_OUT_EN, !!entry); +} + +static const struct max_serdes_tpg_entry max9296a_tpg_entries[] = { + MAX_TPG_ENTRY_640X480P60_RGB888, + MAX_TPG_ENTRY_1920X1080P30_RGB888, + MAX_TPG_ENTRY_1920X1080P60_RGB888, +}; + +static const struct max_des_ops max9296a_common_ops = { + .num_remaps_per_pipe = 16, + .tpg_entries = { + .num_entries = ARRAY_SIZE(max9296a_tpg_entries), + .entries = max9296a_tpg_entries, + }, + .tpg_patterns = BIT(MAX_SERDES_TPG_PATTERN_CHECKERBOARD) | + BIT(MAX_SERDES_TPG_PATTERN_GRADIENT), +#ifdef CONFIG_VIDEO_ADV_DEBUG + .reg_read = max9296a_reg_read, + .reg_write = max9296a_reg_write, +#endif + .log_pipe_status = max9626a_log_pipe_status, + .log_phy_status = max9296a_log_phy_status, + .set_enable = max9296a_set_enable, + .init = max9296a_init, + .init_phy = max9296a_init_phy, + .set_phy_mode = max9296a_set_phy_mode, + .set_phy_enable = max9296a_set_phy_enable, + .set_pipe_remap = max9296a_set_pipe_remap, + .set_pipe_remaps_enable = max9296a_set_pipe_remaps_enable, + .set_pipe_mode = max9296a_set_pipe_mode, + .set_tpg = max9296a_set_tpg, + .select_links = max9296a_select_links, + .set_link_version = max9296a_set_link_version, +}; + +static int max9296a_probe(struct i2c_client *client) +{ + struct regmap_config i2c_regmap = max9296a_i2c_regmap; + struct device *dev = &client->dev; + struct max9296a_priv *priv; + struct max_des_ops *ops; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL); + if (!ops) + return -ENOMEM; + + priv->info = device_get_match_data(dev); + if (!priv->info) { + dev_err(dev, "Failed to get match data\n"); + return -ENODEV; + } + + priv->dev = dev; + priv->client = client; + i2c_set_clientdata(client, priv); + + i2c_regmap.max_register = priv->info->max_register; + priv->regmap = devm_regmap_init_i2c(client, &i2c_regmap); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + priv->gpiod_pwdn = devm_gpiod_get_optional(&client->dev, "powerdown", + GPIOD_OUT_HIGH); + if (IS_ERR(priv->gpiod_pwdn)) + return PTR_ERR(priv->gpiod_pwdn); + + if (priv->gpiod_pwdn) { + /* PWDN must be held for 1us for reset */ + udelay(1); + + gpiod_set_value_cansleep(priv->gpiod_pwdn, 0); + /* Maximum power-up time (tLOCK) 4ms */ + usleep_range(4000, 5000); + } + + *ops = max9296a_common_ops; + + ops->versions = priv->info->ops->versions; + ops->modes = priv->info->ops->modes; + ops->needs_single_link_version = priv->info->ops->needs_single_link_version; + ops->needs_unique_stream_id = priv->info->ops->needs_unique_stream_id; + ops->fix_tx_ids = priv->info->ops->fix_tx_ids; + ops->num_phys = priv->info->ops->num_phys; + ops->num_pipes = priv->info->ops->num_pipes; + ops->num_links = priv->info->ops->num_links; + ops->phys_configs = priv->info->ops->phys_configs; + ops->set_pipe_enable = priv->info->ops->set_pipe_enable; + ops->set_pipe_stream_id = priv->info->ops->set_pipe_stream_id; + ops->set_pipe_tunnel_phy = priv->info->ops->set_pipe_tunnel_phy; + ops->set_pipe_tunnel_enable = priv->info->ops->set_pipe_tunnel_enable; + ops->use_atr = priv->info->ops->use_atr; + ops->tpg_mode = priv->info->ops->tpg_mode; + priv->des.ops = ops; + + ret = max9296a_reset(priv); + if (ret) + return ret; + + return max_des_probe(client, &priv->des); +} + +static void max9296a_remove(struct i2c_client *client) +{ + struct max9296a_priv *priv = i2c_get_clientdata(client); + + max_des_remove(&priv->des); + + gpiod_set_value_cansleep(priv->gpiod_pwdn, 1); +} + +static const struct max_serdes_phys_config max9296a_phys_configs[] = { + { { 4, 4 } }, +}; + +static const struct max_serdes_phys_config max96714_phys_configs[] = { + { { 4 } }, +}; + +static const struct max_des_ops max9296a_ops = { + .versions = BIT(MAX_SERDES_GMSL_2_3GBPS) | + BIT(MAX_SERDES_GMSL_2_6GBPS), + .modes = BIT(MAX_SERDES_GMSL_PIXEL_MODE), + .set_pipe_stream_id = max9296a_set_pipe_stream_id, + .set_pipe_enable = max9296a_set_pipe_enable, + .needs_single_link_version = true, + .needs_unique_stream_id = true, + .phys_configs = { + .num_configs = ARRAY_SIZE(max9296a_phys_configs), + .configs = max9296a_phys_configs, + }, + .fix_tx_ids = true, + .num_pipes = 4, + .num_phys = 2, + .num_links = 2, + .use_atr = true, +}; + +static const struct max9296a_chip_info max9296a_info = { + .ops = &max9296a_ops, + .max_register = 0x1f00, + .phy0_lanes_0_1_on_second_phy = true, + .pipe_hw_ids = { 0, 1, 2, 3 }, + .phy_hw_ids = { 1, 2 }, +}; + +static const struct max_des_ops max96714_ops = { + .versions = BIT(MAX_SERDES_GMSL_2_3GBPS) | + BIT(MAX_SERDES_GMSL_2_6GBPS), + .modes = BIT(MAX_SERDES_GMSL_PIXEL_MODE) | + BIT(MAX_SERDES_GMSL_TUNNEL_MODE), + .set_pipe_stream_id = max96714_set_pipe_stream_id, + .set_pipe_enable = max96714_set_pipe_enable, + .set_pipe_tunnel_enable = max96714_set_pipe_tunnel_enable, + .phys_configs = { + .num_configs = ARRAY_SIZE(max96714_phys_configs), + .configs = max96714_phys_configs, + }, + .tpg_mode = MAX_SERDES_GMSL_PIXEL_MODE, + .num_pipes = 1, + .num_phys = 1, + .num_links = 1, +}; + +/* + * These register writes are described as required in MAX96714 datasheet + * Page 53, Section Register Map, to optimize link performance in 6Gbps + * and 3Gbps links for all cable lengths. + */ +static const struct reg_sequence max96714_rlms_reg_sequence[] = { + { MAX9296A_RLMS3E(0), 0xfd }, + { MAX9296A_RLMS3F(0), 0x3d }, + { MAX9296A_RLMS49(0), 0xf5 }, + { MAX9296A_RLMS7E(0), 0xa8 }, + { MAX9296A_RLMS7F(0), 0x68 }, + { MAX9296A_RLMSA3(0), 0x30 }, + { MAX9296A_RLMSA5(0), 0x70 }, + { MAX9296A_RLMSD8(0), 0x07 }, +}; + +static const struct max9296a_chip_info max96714_info = { + .ops = &max96714_ops, + .max_register = 0x5011, + .polarity_on_physical_lanes = true, + .supports_phy_log = true, + .rlms_adjust_sequence = max96714_rlms_reg_sequence, + .rlms_adjust_sequence_len = ARRAY_SIZE(max96714_rlms_reg_sequence), + .pipe_hw_ids = { 1 }, + .phy_hw_ids = { 1 }, +}; + +static const struct max_des_ops max96714f_ops = { + .versions = BIT(MAX_SERDES_GMSL_2_3GBPS), + .modes = BIT(MAX_SERDES_GMSL_PIXEL_MODE) | + BIT(MAX_SERDES_GMSL_TUNNEL_MODE), + .set_pipe_stream_id = max96714_set_pipe_stream_id, + .set_pipe_enable = max96714_set_pipe_enable, + .set_pipe_tunnel_enable = max96714_set_pipe_tunnel_enable, + .phys_configs = { + .num_configs = ARRAY_SIZE(max96714_phys_configs), + .configs = max96714_phys_configs, + }, + .tpg_mode = MAX_SERDES_GMSL_PIXEL_MODE, + .num_pipes = 1, + .num_phys = 1, + .num_links = 1, +}; + +static const struct max9296a_chip_info max96714f_info = { + .ops = &max96714f_ops, + .max_register = 0x5011, + .polarity_on_physical_lanes = true, + .supports_phy_log = true, + .rlms_adjust_sequence = max96714_rlms_reg_sequence, + .rlms_adjust_sequence_len = ARRAY_SIZE(max96714_rlms_reg_sequence), + .pipe_hw_ids = { 1 }, + .phy_hw_ids = { 1 }, +}; + +static const struct max_des_ops max96716a_ops = { + .versions = BIT(MAX_SERDES_GMSL_2_3GBPS) | + BIT(MAX_SERDES_GMSL_2_6GBPS), + .modes = BIT(MAX_SERDES_GMSL_PIXEL_MODE) | + BIT(MAX_SERDES_GMSL_TUNNEL_MODE), + .set_pipe_stream_id = max96714_set_pipe_stream_id, + .set_pipe_link = max96716a_set_pipe_link, + .set_pipe_enable = max96714_set_pipe_enable, + .set_pipe_tunnel_phy = max96716a_set_pipe_tunnel_phy, + .set_pipe_tunnel_enable = max96714_set_pipe_tunnel_enable, + .use_atr = true, + .phys_configs = { + .num_configs = ARRAY_SIZE(max9296a_phys_configs), + .configs = max9296a_phys_configs, + }, + .tpg_mode = MAX_SERDES_GMSL_PIXEL_MODE, + .num_pipes = 2, + .num_phys = 2, + .num_links = 2, +}; + +static const struct max9296a_chip_info max96716a_info = { + .ops = &max96716a_ops, + .max_register = 0x52d6, + .has_per_link_reset = true, + .phy0_lanes_0_1_on_second_phy = true, + .supports_cphy = true, + .supports_phy_log = true, + .pipe_hw_ids = { 1, 2 }, + .phy_hw_ids = { 1, 2 }, +}; + +static const struct max_des_ops max96792a_ops = { + .versions = BIT(MAX_SERDES_GMSL_2_3GBPS) | + BIT(MAX_SERDES_GMSL_2_6GBPS) | + BIT(MAX_SERDES_GMSL_3_12GBPS), + .modes = BIT(MAX_SERDES_GMSL_PIXEL_MODE) | + BIT(MAX_SERDES_GMSL_TUNNEL_MODE), + .set_pipe_stream_id = max96714_set_pipe_stream_id, + .set_pipe_enable = max96714_set_pipe_enable, + .set_pipe_tunnel_phy = max96716a_set_pipe_tunnel_phy, + .set_pipe_tunnel_enable = max96714_set_pipe_tunnel_enable, + .use_atr = true, + .phys_configs = { + .num_configs = ARRAY_SIZE(max9296a_phys_configs), + .configs = max9296a_phys_configs, + }, + .tpg_mode = MAX_SERDES_GMSL_PIXEL_MODE, + .num_pipes = 2, + .num_phys = 2, + .num_links = 2, +}; + +static const struct max9296a_chip_info max96792a_info = { + .ops = &max96792a_ops, + .max_register = 0x52d6, + .has_per_link_reset = true, + .phy0_lanes_0_1_on_second_phy = true, + .supports_cphy = true, + .supports_phy_log = true, + .pipe_hw_ids = { 1, 2 }, + .phy_hw_ids = { 1, 2 }, +}; + +static const struct acpi_device_id max9296a_acpi_ids[] = { + { "INTC1137", (kernel_ulong_t)&max9296a_info }, + {} +}; +MODULE_DEVICE_TABLE(acpi, max9296a_acpi_ids); + +static const struct of_device_id max9296a_of_table[] = { + { .compatible = "maxim,max9296a", .data = &max9296a_info }, + { .compatible = "maxim,max96714", .data = &max96714_info }, + { .compatible = "maxim,max96714f", .data = &max96714f_info }, + { .compatible = "maxim,max96714r", .data = &max96714f_info }, + { .compatible = "maxim,max96716a", .data = &max96716a_info }, + { .compatible = "maxim,max96792a", .data = &max96792a_info }, + { }, +}; +MODULE_DEVICE_TABLE(of, max9296a_of_table); + +static struct i2c_driver max9296a_i2c_driver = { + .driver = { + .name = "max9296a", + .of_match_table = max9296a_of_table, + .acpi_match_table = ACPI_PTR(max9296a_acpi_ids), + }, + .probe = max9296a_probe, + .remove = max9296a_remove, +}; + +module_i2c_driver(max9296a_i2c_driver); + +MODULE_IMPORT_NS("MAX_SERDES"); +MODULE_DESCRIPTION("Maxim MAX9296A Quad GMSL2 Deserializer Driver"); +MODULE_AUTHOR("Cosmin Tanislav "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/maxim-serdes/max96717.c b/drivers/media/i2c/maxim-serdes/max96717.c new file mode 100644 index 00000000000000..1a47d532f7d90c --- /dev/null +++ b/drivers/media/i2c/maxim-serdes/max96717.c @@ -0,0 +1,1702 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Maxim MAX96717 GMSL2 Serializer Driver + * + * Copyright (C) 2025 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "max_ser.h" + +#define MAX96717_REG0 0x0 + +#define MAX96717_REG2 0x2 +#define MAX96717_REG2_VID_TX_EN_P(p) BIT(4 + (p)) + +#define MAX96717_REG3 0x3 +#define MAX96717_REG3_RCLKSEL GENMASK(1, 0) +#define MAX96717_REG3_RCLK_ALT BIT(2) + +#define MAX96717_REG6 0x6 +#define MAX96717_REG6_RCLKEN BIT(5) + +#define MAX96717_I2C_2(x) (0x42 + (x) * 0x2) +#define MAX96717_I2C_2_SRC GENMASK(7, 1) + +#define MAX96717_I2C_3(x) (0x43 + (x) * 0x2) +#define MAX96717_I2C_3_DST GENMASK(7, 1) + +#define MAX96717_TX3(p) (0x53 + (p) * 0x4) +#define MAX96717_TX3_TX_STR_SEL GENMASK(1, 0) + +#define MAX96717_VIDEO_TX0(p) (0x100 + (p) * 0x8) +#define MAX96717_VIDEO_TX0_AUTO_BPP BIT(3) + +#define MAX96717_VIDEO_TX1(p) (0x101 + (p) * 0x8) +#define MAX96717_VIDEO_TX1_BPP GENMASK(5, 0) + +#define MAX96717_VIDEO_TX2(p) (0x102 + (p) * 0x8) +#define MAX96717_VIDEO_TX2_PCLKDET BIT(7) +#define MAX96717_VIDEO_TX2_DRIFT_DET_EN BIT(1) + +#define MAX96717_VTX0(p) (0x1c8 + (p) * 0x43) +#define MAX96717_VTX0_VTG_MODE GENMASK(1, 0) +#define MAX96717_VTX0_VTG_MODE_FREE_RUNNING 0b11 +#define MAX96717_VTX0_DE_INV BIT(2) +#define MAX96717_VTX0_HS_INV BIT(3) +#define MAX96717_VTX0_VS_INV BIT(4) +#define MAX96717_VTX0_GEN_DE BIT(5) +#define MAX96717_VTX0_GEN_HS BIT(6) +#define MAX96717_VTX0_GEN_VS BIT(7) + +#define MAX96717_VTX1(p) (0x1c9 + (p) * 0x43) +#define MAX96717_VTX1_PATGEN_CLK_SRC GENMASK(3, 1) +#define MAX96717_VTX1_PATGEN_CLK_SRC_25MHZ 0b100 +#define MAX96717_VTX1_PATGEN_CLK_SRC_75MHZ 0b101 +#define MAX96717_VTX1_PATGEN_CLK_SRC_150MHZ 0b110 +#define MAX96717_VTX1_PATGEN_CLK_SRC_375MHZ 0b111 + +#define MAX96717_VTX2_VS_DLY_2(p) (0x1ca + (p) * 0x43) +#define MAX96717_VTX5_VS_HIGH_2(p) (0x1cd + (p) * 0x43) +#define MAX96717_VTX8_VS_LOW_2(p) (0x1d0 + (p) * 0x43) +#define MAX96717_VTX11_V2H_2(p) (0x1d3 + (p) * 0x43) +#define MAX96717_VTX14_HS_HIGH_1(p) (0x1d6 + (p) * 0x43) +#define MAX96717_VTX16_HS_LOW_1(p) (0x1d8 + (p) * 0x43) +#define MAX96717_VTX18_HS_CNT_1(p) (0x1da + (p) * 0x43) +#define MAX96717_VTX20_V2D_2(p) (0x1dc + (p) * 0x43) +#define MAX96717_VTX23_DE_HIGH_1(p) (0x1df + (p) * 0x43) +#define MAX96717_VTX25_DE_LOW_1(p) (0x1e1 + (p) * 0x43) +#define MAX96717_VTX27_DE_CNT_1(p) (0x1e3 + (p) * 0x43) +#define MAX96717_VTX29(p) (0x1e5 + (p) * 0x43) + +#define MAX96717_VTX29_PATGEN_MODE GENMASK(1, 0) +#define MAX96717_VTX29_PATGEN_MODE_DISABLED 0b00 +#define MAX96717_VTX29_PATGEN_MODE_CHECKER 0b01 +#define MAX96717_VTX29_PATGEN_MODE_GRADIENT 0b10 + +#define MAX96717_VTX30_GRAD_INCR(p) (0x1e6 + (p) * 0x43) +#define MAX96717_VTX31_CHKR_A_L(p) (0x1e7 + (p) * 0x43) +#define MAX96717_VTX34_CHKR_B_L(p) (0x1ea + (p) * 0x43) +#define MAX96717_VTX37_CHKR_RPT_A(p) (0x1ed + (p) * 0x43) +#define MAX96717_VTX38_CHKR_RPT_B(p) (0x1ee + (p) * 0x43) +#define MAX96717_VTX39_CHKR_ALT(p) (0x1ef + (p) * 0x43) + +#define MAX96717_GPIO_A(x) (0x2be + (x) * 0x3) +#define MAX96717_GPIO_A_GPIO_OUT_DIS BIT(0) +#define MAX96717_GPIO_A_GPIO_TX_EN BIT(1) +#define MAX96717_GPIO_A_GPIO_RX_EN BIT(2) +#define MAX96717_GPIO_A_GPIO_IN BIT(3) +#define MAX96717_GPIO_A_GPIO_OUT BIT(4) +#define MAX96717_GPIO_A_TX_COMP_EN BIT(5) +#define MAX96717_GPIO_A_RES_CFG BIT(7) + +#define MAX96717_GPIO_B(x) (0x2bf + (x) * 0x3) +#define MAX96717_GPIO_B_GPIO_TX_ID GENMASK(4, 0) +#define MAX96717_GPIO_B_OUT_TYPE BIT(5) +#define MAX96717_GPIO_B_PULL_UPDN_SEL GENMASK(7, 6) +#define MAX96717_GPIO_B_PULL_UPDN_SEL_NONE 0b00 +#define MAX96717_GPIO_B_PULL_UPDN_SEL_PU 0b01 +#define MAX96717_GPIO_B_PULL_UPDN_SEL_PD 0b10 + +#define MAX96717_GPIO_C(x) (0x2c0 + (x) * 0x3) +#define MAX96717_GPIO_C_GPIO_RX_ID GENMASK(4, 0) + +#define MAX96717_CMU2 0x302 +#define MAX96717_CMU2_PFDDIV_RSHORT GENMASK(6, 4) +#define MAX96717_CMU2_PFDDIV_RSHORT_1_1V 0b001 + +#define MAX96717_FRONTTOP_0 0x308 +#define MAX96717_FRONTTOP_0_CLK_SEL_P(x) BIT(x) +#define MAX96717_FRONTTOP_0_START_PORT(x) BIT((x) + 4) + +#define MAX96717_FRONTTOP_1(p) (0x309 + (p) * 0x2) +#define MAX96717_FRONTTOP_2(p) (0x30a + (p) * 0x2) + +#define MAX96717_FRONTTOP_9 0x311 +#define MAX96717_FRONTTOP_9_START_PORT(p, x) BIT((p) + (x) * 4) + +#define MAX96717_FRONTTOP_10 0x312 +#define MAX96717_FRONTTOP_10_BPP8DBL(p) BIT(p) + +#define MAX96717_FRONTTOP_11 0x313 +#define MAX96717_FRONTTOP_11_BPP10DBL(p) BIT(p) +#define MAX96717_FRONTTOP_11_BPP12DBL(p) BIT((p) + 4) + +#define MAX96717_FRONTTOP_12(p, x) (0x314 + (p) * 0x2 + (x)) +#define MAX96717_MEM_DT_SEL GENMASK(5, 0) +#define MAX96717_MEM_DT_EN BIT(6) + +#define MAX96717_FRONTTOP_20(p) (0x31c + (p) * 0x1) +#define MAX96717_FRONTTOP_20_SOFT_BPP_EN BIT(5) +#define MAX96717_FRONTTOP_20_SOFT_BPP GENMASK(4, 0) + +#define MAX96717_MIPI_RX0 0x330 +#define MAX96717_MIPI_RX0_NONCONTCLK_EN BIT(6) + +#define MAX96717_MIPI_RX1 0x331 +#define MAX96717_MIPI_RX1_CTRL_NUM_LANES GENMASK(5, 4) + +#define MAX96717_MIPI_RX2 0x332 +#define MAX96717_MIPI_RX2_PHY1_LANE_MAP GENMASK(7, 4) + +#define MAX96717_MIPI_RX3 0x333 +#define MAX96717_MIPI_RX3_PHY2_LANE_MAP GENMASK(3, 0) + +#define MAX96717_MIPI_RX4 0x334 +#define MAX96717_MIPI_RX4_PHY1_POL_MAP GENMASK(5, 4) + +#define MAX96717_MIPI_RX5 0x335 +#define MAX96717_MIPI_RX5_PHY2_POL_MAP GENMASK(1, 0) +#define MAX96717_MIPI_RX5_PHY2_POL_MAP_CLK BIT(2) + +#define MAX96717_EXTA(x) (0x3dc + (x)) + +#define MAX96717_EXT11 0x383 +#define MAX96717_EXT11_TUN_MODE BIT(7) + +#define MAX96717_EXT21 0x38d +#define MAX96717_EXT22 0x38e +#define MAX96717_EXT23 0x38f +#define MAX96717_EXT24 0x390 + +#define MAX96717_REF_VTG0 0x3f0 +#define MAX96717_REF_VTG0_REFGEN_EN BIT(0) +#define MAX96717_REF_VTG0_REFGEN_RST BIT(1) +#define MAX96717_REF_VTG0_REFGEN_PREDEF_FREQ_ALT\ + BIT(3) +#define MAX96717_REF_VTG0_REFGEN_PREDEF_FREQ GENMASK(5, 4) + +#define MAX96717_PIO_SLEW_0 0x56f +#define MAX96717_PIO_SLEW_0_PIO00_SLEW GENMASK(1, 0) +#define MAX96717_PIO_SLEW_0_PIO01_SLEW GENMASK(3, 2) +#define MAX96717_PIO_SLEW_0_PIO02_SLEW GENMASK(5, 4) + +#define MAX96717_PIO_SLEW_1 0x570 +#define MAX96717_PIO_SLEW_1_PIO05_SLEW GENMASK(3, 2) +#define MAX96717_PIO_SLEW_1_PIO06_SLEW GENMASK(5, 4) + +#define MAX96717_PIO_SLEW_2 0x571 +#define MAX96717_PIO_SLEW_2_PIO010_SLEW GENMASK(5, 4) +#define MAX96717_PIO_SLEW_2_PIO011_SLEW GENMASK(7, 6) + +#define MAX96717_PIO_SLEW_FASTEST 0b00 + +#define MAX96717_BIAS_PULL_STRENGTH_1000000_OHM 1000000U +#define MAX96717_BIAS_PULL_STRENGTH_40000_OHM 40000U + +#define MAX96717_DEFAULT_CLKOUT_RATE 24000000UL + +#define MAX96717_NAME "max96717" +#define MAX96717_PINCTRL_NAME MAX96717_NAME "-pinctrl" +#define MAX96717_GPIOCHIP_NAME MAX96717_NAME "-gpiochip" +#define MAX96717_GPIO_NUM 11 +#define MAX96717_RCLK_ALT_MFP 2 +#define MAX96717_RCLK_MFP 4 +#define MAX96717_PIPES_NUM 4 +#define MAX96717_PHYS_NUM 2 + +struct max96717_priv { + struct max_ser ser; + struct pinctrl_desc pctldesc; + struct gpio_chip gc; + const struct max96717_chip_info *info; + + struct device *dev; + struct i2c_client *client; + struct regmap *regmap; + struct pinctrl_dev *pctldev; + + struct clk_hw clk_hw; + u8 pll_predef_index; +}; + +struct max96717_chip_info { + bool supports_3_data_lanes; + bool supports_noncontinuous_clock; + bool supports_pkt_cnt; + unsigned int modes; + unsigned int num_pipes; + unsigned int num_dts_per_pipe; + unsigned int pipe_hw_ids[MAX96717_PIPES_NUM]; + unsigned int num_phys; + unsigned int phy_hw_ids[MAX96717_PHYS_NUM]; +}; + +#define ser_to_priv(_ser) \ + container_of(_ser, struct max96717_priv, ser) + +static inline struct max96717_priv *clk_hw_to_priv(struct clk_hw *hw) +{ + return container_of(hw, struct max96717_priv, clk_hw); +} + +static const struct regmap_config max96717_i2c_regmap = { + .reg_bits = 16, + .val_bits = 8, + .max_register = 0x1f00, +}; + +static int max96717_wait_for_device(struct max96717_priv *priv) +{ + unsigned int i; + int ret; + + for (i = 0; i < 10; i++) { + unsigned int val; + + ret = regmap_read(priv->regmap, MAX96717_REG0, &val); + if (!ret && val) + return 0; + + msleep(100); + + dev_err(priv->dev, "Retry %u waiting for serializer: %d\n", i, ret); + } + + return ret; +} + +#define MAX96717_PIN(n) \ + PINCTRL_PIN(n, "mfp" __stringify(n)) + +static const struct pinctrl_pin_desc max96717_pins[] = { + MAX96717_PIN(0), + MAX96717_PIN(1), + MAX96717_PIN(2), + MAX96717_PIN(3), + MAX96717_PIN(4), + MAX96717_PIN(5), + MAX96717_PIN(6), + MAX96717_PIN(7), + MAX96717_PIN(8), + MAX96717_PIN(9), + MAX96717_PIN(10), +}; + +#define MAX96717_GROUP_PINS(name, ...) \ + static const unsigned int name ## _pins[] = { __VA_ARGS__ } + +MAX96717_GROUP_PINS(mfp0, 0); +MAX96717_GROUP_PINS(mfp1, 1); +MAX96717_GROUP_PINS(mfp2, 2); +MAX96717_GROUP_PINS(mfp3, 3); +MAX96717_GROUP_PINS(mfp4, 4); +MAX96717_GROUP_PINS(mfp5, 5); +MAX96717_GROUP_PINS(mfp6, 6); +MAX96717_GROUP_PINS(mfp7, 7); +MAX96717_GROUP_PINS(mfp8, 8); +MAX96717_GROUP_PINS(mfp9, 9); +MAX96717_GROUP_PINS(mfp10, 10); + +#define MAX96717_GROUP(name) \ + PINCTRL_PINGROUP(__stringify(name), name ## _pins, ARRAY_SIZE(name ## _pins)) + +static const struct pingroup max96717_ctrl_groups[] = { + MAX96717_GROUP(mfp0), + MAX96717_GROUP(mfp1), + MAX96717_GROUP(mfp2), + MAX96717_GROUP(mfp3), + MAX96717_GROUP(mfp4), + MAX96717_GROUP(mfp5), + MAX96717_GROUP(mfp6), + MAX96717_GROUP(mfp7), + MAX96717_GROUP(mfp8), + MAX96717_GROUP(mfp9), + MAX96717_GROUP(mfp10), +}; + +#define MAX96717_FUNC_GROUPS(name, ...) \ + static const char * const name ## _groups[] = { __VA_ARGS__ } + +MAX96717_FUNC_GROUPS(gpio, "mfp0", "mfp1", "mfp2", "mfp3", "mfp4", "mfp5", + "mfp6", "mfp7", "mfp8", "mfp9", "mfp10"); +MAX96717_FUNC_GROUPS(rclkout, "mfp2", "mfp4"); + +enum max96717_func { + max96717_func_gpio, + max96717_func_rclkout, +}; + +#define MAX96717_FUNC(name) \ + [max96717_func_ ## name] = \ + PINCTRL_PINFUNCTION(__stringify(name), name ## _groups, \ + ARRAY_SIZE(name ## _groups)) + +static const struct pinfunction max96717_functions[] = { + MAX96717_FUNC(gpio), + MAX96717_FUNC(rclkout), +}; + +#define MAX96717_PINCTRL_X(x) (PIN_CONFIG_END + (x)) +#define MAX96717_PINCTRL_JITTER_COMPENSATION_EN MAX96717_PINCTRL_X(1) +#define MAX96717_PINCTRL_TX_ID MAX96717_PINCTRL_X(2) +#define MAX96717_PINCTRL_RX_ID MAX96717_PINCTRL_X(3) +#define MAX96717_PINCTRL_PULL_STRENGTH_HIGH MAX96717_PINCTRL_X(4) +#define MAX96717_PINCTRL_INPUT_VALUE MAX96717_PINCTRL_X(5) +#define MAX96717_PINCTRL_TX_EN MAX96717_PINCTRL_X(6) +#define MAX96717_PINCTRL_RX_EN MAX96717_PINCTRL_X(7) + +static const struct pinconf_generic_params max96717_cfg_params[] = { + { "maxim,jitter-compensation", MAX96717_PINCTRL_JITTER_COMPENSATION_EN, 0 }, + { "maxim,tx-id", MAX96717_PINCTRL_TX_ID, 0 }, + { "maxim,rx-id", MAX96717_PINCTRL_RX_ID, 0 }, +}; + +static int max96717_ctrl_get_groups_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(max96717_ctrl_groups); +} + +static const char *max96717_ctrl_get_group_name(struct pinctrl_dev *pctldev, + unsigned int selector) +{ + return max96717_ctrl_groups[selector].name; +} + +static int max96717_ctrl_get_group_pins(struct pinctrl_dev *pctldev, + unsigned int selector, + const unsigned int **pins, + unsigned int *num_pins) +{ + *pins = (unsigned int *)max96717_ctrl_groups[selector].pins; + *num_pins = max96717_ctrl_groups[selector].npins; + + return 0; +} + +static int max96717_get_pin_config_reg(unsigned int offset, u32 param, + unsigned int *reg, unsigned int *mask, + unsigned int *val) +{ + *reg = MAX96717_GPIO_A(offset); + + switch (param) { + case PIN_CONFIG_OUTPUT_ENABLE: + *mask = MAX96717_GPIO_A_GPIO_OUT_DIS; + *val = 0b0; + return 0; + case PIN_CONFIG_INPUT_ENABLE: + *mask = MAX96717_GPIO_A_GPIO_OUT_DIS; + *val = 0b1; + return 0; + case MAX96717_PINCTRL_TX_EN: + *mask = MAX96717_GPIO_A_GPIO_TX_EN; + *val = 0b1; + return 0; + case MAX96717_PINCTRL_RX_EN: + *mask = MAX96717_GPIO_A_GPIO_RX_EN; + *val = 0b1; + return 0; + case MAX96717_PINCTRL_INPUT_VALUE: + *mask = MAX96717_GPIO_A_GPIO_IN; + *val = 0b1; + return 0; + case PIN_CONFIG_OUTPUT: + *mask = MAX96717_GPIO_A_GPIO_OUT; + *val = 0b1; + return 0; + case MAX96717_PINCTRL_JITTER_COMPENSATION_EN: + *mask = MAX96717_GPIO_A_TX_COMP_EN; + *val = 0b1; + return 0; + case MAX96717_PINCTRL_PULL_STRENGTH_HIGH: + *mask = MAX96717_GPIO_A_RES_CFG; + *val = 0b1; + return 0; + } + + *reg = MAX96717_GPIO_B(offset); + + switch (param) { + case MAX96717_PINCTRL_TX_ID: + *mask = MAX96717_GPIO_B_GPIO_TX_ID; + return 0; + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + *mask = MAX96717_GPIO_B_OUT_TYPE; + *val = 0b0; + return 0; + case PIN_CONFIG_DRIVE_PUSH_PULL: + *mask = MAX96717_GPIO_B_OUT_TYPE; + *val = 0b1; + return 0; + case PIN_CONFIG_BIAS_DISABLE: + *mask = MAX96717_GPIO_B_PULL_UPDN_SEL; + *val = MAX96717_GPIO_B_PULL_UPDN_SEL_NONE; + return 0; + case PIN_CONFIG_BIAS_PULL_DOWN: + *mask = MAX96717_GPIO_B_PULL_UPDN_SEL; + *val = MAX96717_GPIO_B_PULL_UPDN_SEL_PD; + return 0; + case PIN_CONFIG_BIAS_PULL_UP: + *mask = MAX96717_GPIO_B_PULL_UPDN_SEL; + *val = MAX96717_GPIO_B_PULL_UPDN_SEL_PU; + return 0; + } + + switch (param) { + case PIN_CONFIG_SLEW_RATE: + if (offset < 3) { + *reg = MAX96717_PIO_SLEW_0; + if (offset == 0) + *mask = MAX96717_PIO_SLEW_0_PIO00_SLEW; + else if (offset == 1) + *mask = MAX96717_PIO_SLEW_0_PIO01_SLEW; + else + *mask = MAX96717_PIO_SLEW_0_PIO02_SLEW; + } else if (offset < 5) { + *reg = MAX96717_PIO_SLEW_1; + if (offset == 3) + *mask = MAX96717_PIO_SLEW_1_PIO05_SLEW; + else + *mask = MAX96717_PIO_SLEW_1_PIO06_SLEW; + } else if (offset < 7) { + return -EINVAL; + } else if (offset < 9) { + *reg = MAX96717_PIO_SLEW_2; + if (offset == 7) + *mask = MAX96717_PIO_SLEW_2_PIO010_SLEW; + else + *mask = MAX96717_PIO_SLEW_2_PIO011_SLEW; + } else { + return -EINVAL; + } + return 0; + case MAX96717_PINCTRL_RX_ID: + *reg = MAX96717_GPIO_C(offset); + *mask = MAX96717_GPIO_C_GPIO_RX_ID; + return 0; + default: + return -ENOTSUPP; + } +} + +static int max96717_conf_pin_config_get(struct pinctrl_dev *pctldev, + unsigned int offset, + unsigned long *config) +{ + struct max96717_priv *priv = pinctrl_dev_get_drvdata(pctldev); + u32 param = pinconf_to_config_param(*config); + unsigned int reg, mask, val, en_val; + int ret; + + ret = max96717_get_pin_config_reg(offset, param, ®, &mask, &en_val); + if (ret) + return ret; + + switch (param) { + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + case PIN_CONFIG_DRIVE_PUSH_PULL: + case PIN_CONFIG_BIAS_DISABLE: + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_PULL_UP: + case MAX96717_PINCTRL_JITTER_COMPENSATION_EN: + case MAX96717_PINCTRL_TX_EN: + case MAX96717_PINCTRL_RX_EN: + case PIN_CONFIG_OUTPUT_ENABLE: + case PIN_CONFIG_INPUT_ENABLE: + ret = regmap_read(priv->regmap, reg, &val); + if (ret) + return ret; + + val = field_get(mask, val) == en_val; + if (!val) + return -EINVAL; + + break; + case MAX96717_PINCTRL_PULL_STRENGTH_HIGH: + case MAX96717_PINCTRL_INPUT_VALUE: + case PIN_CONFIG_OUTPUT: + ret = regmap_read(priv->regmap, reg, &val); + if (ret) + return ret; + + val = field_get(mask, val) == en_val; + break; + case MAX96717_PINCTRL_TX_ID: + case MAX96717_PINCTRL_RX_ID: + case PIN_CONFIG_SLEW_RATE: + ret = regmap_read(priv->regmap, reg, &val); + if (ret) + return ret; + + val = field_get(mask, val); + break; + default: + return -ENOTSUPP; + } + + switch (param) { + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_PULL_UP: + *config = pinconf_to_config_packed(MAX96717_PINCTRL_PULL_STRENGTH_HIGH, 0); + + ret = max96717_conf_pin_config_get(pctldev, offset, config); + if (ret) + return ret; + + val = pinconf_to_config_argument(*config); + if (val) + val = MAX96717_BIAS_PULL_STRENGTH_1000000_OHM; + else + val = MAX96717_BIAS_PULL_STRENGTH_40000_OHM; + + break; + case MAX96717_PINCTRL_TX_ID: + *config = pinconf_to_config_packed(MAX96717_PINCTRL_TX_EN, 0); + + ret = max96717_conf_pin_config_get(pctldev, offset, config); + if (ret) + return ret; + + break; + case MAX96717_PINCTRL_RX_ID: + *config = pinconf_to_config_packed(MAX96717_PINCTRL_RX_EN, 0); + + ret = max96717_conf_pin_config_get(pctldev, offset, config); + if (ret) + return ret; + + break; + default: + break; + } + + *config = pinconf_to_config_packed(param, val); + + return 0; +} + +static int max96717_conf_pin_config_set_one(struct max96717_priv *priv, + unsigned int offset, + unsigned long config) +{ + u32 param = pinconf_to_config_param(config); + u32 arg = pinconf_to_config_argument(config); + unsigned int reg, mask, val, en_val; + int ret; + + ret = max96717_get_pin_config_reg(offset, param, ®, &mask, &en_val); + if (ret) + return ret; + + switch (param) { + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + case PIN_CONFIG_DRIVE_PUSH_PULL: + case PIN_CONFIG_BIAS_DISABLE: + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_PULL_UP: + val = field_prep(mask, en_val); + + ret = regmap_update_bits(priv->regmap, reg, mask, val); + break; + case MAX96717_PINCTRL_JITTER_COMPENSATION_EN: + case MAX96717_PINCTRL_PULL_STRENGTH_HIGH: + case MAX96717_PINCTRL_TX_EN: + case MAX96717_PINCTRL_RX_EN: + case PIN_CONFIG_OUTPUT_ENABLE: + case PIN_CONFIG_INPUT_ENABLE: + case PIN_CONFIG_OUTPUT: + val = field_prep(mask, arg ? en_val : ~en_val); + + ret = regmap_update_bits(priv->regmap, reg, mask, val); + break; + case MAX96717_PINCTRL_TX_ID: + case MAX96717_PINCTRL_RX_ID: + case PIN_CONFIG_SLEW_RATE: + val = field_prep(mask, arg); + + ret = regmap_update_bits(priv->regmap, reg, mask, val); + break; + default: + return -ENOTSUPP; + } + + if (ret) + return ret; + + switch (param) { + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_PULL_UP: + arg = arg >= MAX96717_BIAS_PULL_STRENGTH_1000000_OHM; + config = pinconf_to_config_packed(MAX96717_PINCTRL_PULL_STRENGTH_HIGH, arg); + return max96717_conf_pin_config_set_one(priv, offset, config); + case PIN_CONFIG_OUTPUT: + config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT_ENABLE, 1); + return max96717_conf_pin_config_set_one(priv, offset, config); + case PIN_CONFIG_OUTPUT_ENABLE: + config = pinconf_to_config_packed(MAX96717_PINCTRL_RX_EN, 0); + return max96717_conf_pin_config_set_one(priv, offset, config); + case MAX96717_PINCTRL_TX_ID: + config = pinconf_to_config_packed(MAX96717_PINCTRL_TX_EN, 1); + return max96717_conf_pin_config_set_one(priv, offset, config); + case MAX96717_PINCTRL_RX_ID: + config = pinconf_to_config_packed(MAX96717_PINCTRL_RX_EN, 1); + return max96717_conf_pin_config_set_one(priv, offset, config); + default: + break; + } + + return 0; +} + +static int max96717_conf_pin_config_set(struct pinctrl_dev *pctldev, + unsigned int offset, + unsigned long *configs, + unsigned int num_configs) +{ + struct max96717_priv *priv = pinctrl_dev_get_drvdata(pctldev); + int ret; + + while (num_configs--) { + unsigned long config = *configs; + + ret = max96717_conf_pin_config_set_one(priv, offset, config); + if (ret) + return ret; + + configs++; + } + + return 0; +} + +static int max96717_mux_get_functions_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(max96717_functions); +} + +static const char *max96717_mux_get_function_name(struct pinctrl_dev *pctldev, + unsigned int selector) +{ + return max96717_functions[selector].name; +} + +static int max96717_mux_get_groups(struct pinctrl_dev *pctldev, + unsigned int selector, + const char * const **groups, + unsigned int * const num_groups) +{ + *groups = max96717_functions[selector].groups; + *num_groups = max96717_functions[selector].ngroups; + + return 0; +} + +static int max96717_mux_set_rclkout(struct max96717_priv *priv, unsigned int group) +{ + unsigned long config; + int ret; + + config = pinconf_to_config_packed(PIN_CONFIG_SLEW_RATE, + MAX96717_PIO_SLEW_FASTEST); + ret = max96717_conf_pin_config_set_one(priv, group, config); + if (ret) + return ret; + + return regmap_assign_bits(priv->regmap, MAX96717_REG3, + MAX96717_REG3_RCLK_ALT, + group == MAX96717_RCLK_ALT_MFP); +} + +static int max96717_mux_set(struct pinctrl_dev *pctldev, unsigned int selector, + unsigned int group) +{ + struct max96717_priv *priv = pinctrl_dev_get_drvdata(pctldev); + + switch (selector) { + case max96717_func_rclkout: + return max96717_mux_set_rclkout(priv, group); + } + + return 0; +} + +static int max96717_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + unsigned long config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT_ENABLE, 0); + struct max96717_priv *priv = gpiochip_get_data(gc); + int ret; + + ret = max96717_conf_pin_config_get(priv->pctldev, offset, &config); + if (ret) + return ret; + + return pinconf_to_config_argument(config) ? GPIO_LINE_DIRECTION_OUT + : GPIO_LINE_DIRECTION_IN; +} + +static int max96717_gpio_direction_input(struct gpio_chip *gc, unsigned int offset) +{ + unsigned long config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1); + struct max96717_priv *priv = gpiochip_get_data(gc); + + return max96717_conf_pin_config_set_one(priv, offset, config); +} + +static int max96717_gpio_direction_output(struct gpio_chip *gc, unsigned int offset, + int value) +{ + unsigned long config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value); + struct max96717_priv *priv = gpiochip_get_data(gc); + + return max96717_conf_pin_config_set_one(priv, offset, config); +} + +static int max96717_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + unsigned long config = pinconf_to_config_packed(MAX96717_PINCTRL_INPUT_VALUE, 0); + struct max96717_priv *priv = gpiochip_get_data(gc); + int ret; + + ret = max96717_conf_pin_config_get(priv->pctldev, offset, &config); + if (ret) + return ret; + + return pinconf_to_config_argument(config); +} + +static int max96717_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) +{ + unsigned long config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value); + struct max96717_priv *priv = gpiochip_get_data(gc); + + return max96717_conf_pin_config_set_one(priv, offset, config); +} + +static unsigned int max96717_pipe_id(struct max96717_priv *priv, + struct max_ser_pipe *pipe) +{ + return priv->info->pipe_hw_ids[pipe->index]; +} + +static unsigned int max96717_phy_id(struct max96717_priv *priv, + struct max_ser_phy *phy) +{ + return priv->info->phy_hw_ids[phy->index]; +} + +static int max96717_set_pipe_enable(struct max_ser *ser, + struct max_ser_pipe *pipe, bool enable) +{ + struct max96717_priv *priv = ser_to_priv(ser); + unsigned int index = max96717_pipe_id(priv, pipe); + unsigned int mask = MAX96717_REG2_VID_TX_EN_P(index); + + return regmap_assign_bits(priv->regmap, MAX96717_REG2, mask, enable); +} + +static int __maybe_unused max96717_reg_read(struct max_ser *ser, unsigned int reg, + unsigned int *val) +{ + struct max96717_priv *priv = ser_to_priv(ser); + + return regmap_read(priv->regmap, reg, val); +} + +static int __maybe_unused max96717_reg_write(struct max_ser *ser, unsigned int reg, + unsigned int val) +{ + struct max96717_priv *priv = ser_to_priv(ser); + + return regmap_write(priv->regmap, reg, val); +} + +static int max96717_set_pipe_dt_en(struct max_ser *ser, struct max_ser_pipe *pipe, + unsigned int i, bool enable) +{ + struct max96717_priv *priv = ser_to_priv(ser); + unsigned int index = max96717_pipe_id(priv, pipe); + unsigned int reg; + + if (i < 2) + reg = MAX96717_FRONTTOP_12(index, i); + else + /* + * DT 7 and 8 are only supported on MAX96717, no need for pipe + * index to be taken into account. + */ + reg = MAX96717_EXTA(i - 2); + + return regmap_assign_bits(priv->regmap, reg, MAX96717_MEM_DT_EN, enable); +} + +static int max96717_set_pipe_dt(struct max_ser *ser, struct max_ser_pipe *pipe, + unsigned int i, unsigned int dt) +{ + struct max96717_priv *priv = ser_to_priv(ser); + unsigned int index = max96717_pipe_id(priv, pipe); + unsigned int reg; + + if (i < 2) + reg = MAX96717_FRONTTOP_12(index, i); + else + reg = MAX96717_EXTA(i - 2); + + return regmap_update_bits(priv->regmap, reg, MAX96717_MEM_DT_SEL, + FIELD_PREP(MAX96717_MEM_DT_SEL, dt)); +} + +static int max96717_set_pipe_vcs(struct max_ser *ser, + struct max_ser_pipe *pipe, + unsigned int vcs) +{ + struct max96717_priv *priv = ser_to_priv(ser); + unsigned int index = max96717_pipe_id(priv, pipe); + int ret; + + ret = regmap_write(priv->regmap, MAX96717_FRONTTOP_1(index), + (vcs >> 0) & 0xff); + if (ret) + return ret; + + return regmap_write(priv->regmap, MAX96717_FRONTTOP_2(index), + (vcs >> 8) & 0xff); +} + +static int max96717_log_status(struct max_ser *ser) +{ + struct max96717_priv *priv = ser_to_priv(ser); + unsigned int val; + int ret; + + if (!(priv->info->modes & BIT(MAX_SERDES_GMSL_TUNNEL_MODE))) + return 0; + + ret = regmap_read(priv->regmap, MAX96717_EXT23, &val); + if (ret) + return ret; + + dev_info(priv->dev, "tun_pkt_cnt: %u\n", val); + + return 0; +} + +static int max96717_log_pipe_status(struct max_ser *ser, + struct max_ser_pipe *pipe) +{ + struct max96717_priv *priv = ser_to_priv(ser); + unsigned int index = max96717_pipe_id(priv, pipe); + unsigned int val; + int ret; + + ret = regmap_read(priv->regmap, MAX96717_VIDEO_TX2(index), &val); + if (ret) + return ret; + + dev_info(priv->dev, "\tpclkdet: %u\n", + !!(val & MAX96717_VIDEO_TX2_PCLKDET)); + + return 0; +} + +static int max96717_log_phy_status(struct max_ser *ser, + struct max_ser_phy *phy) +{ + struct max96717_priv *priv = ser_to_priv(ser); + unsigned int val; + int ret; + + if (!priv->info->supports_pkt_cnt) + return 0; + + ret = regmap_read(priv->regmap, MAX96717_EXT21, &val); + if (ret) + return ret; + + dev_info(priv->dev, "\tphy_pkt_cnt: %u\n", val); + + ret = regmap_read(priv->regmap, MAX96717_EXT22, &val); + if (ret) + return ret; + + dev_info(priv->dev, "\tcsi_pkt_cnt: %u\n", val); + + ret = regmap_read(priv->regmap, MAX96717_EXT24, &val); + if (ret) + return ret; + + dev_info(priv->dev, "\tphy_clk_cnt: %u\n", val); + + return 0; +} + +static int max96717_init_phy(struct max_ser *ser, + struct max_ser_phy *phy) +{ + struct max96717_priv *priv = ser_to_priv(ser); + unsigned int num_data_lanes = phy->mipi.num_data_lanes; + unsigned int used_data_lanes = 0; + unsigned int val; + unsigned int i; + int ret; + + if (num_data_lanes == 3 && !priv->info->supports_3_data_lanes) { + dev_err(priv->dev, "Unsupported 3 data lane mode\n"); + return -EINVAL; + } + + if (phy->mipi.flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK && + !priv->info->supports_noncontinuous_clock) { + dev_err(priv->dev, "Unsupported non-continuous mode\n"); + return -EINVAL; + } + + /* Configure a lane count. */ + ret = regmap_update_bits(priv->regmap, MAX96717_MIPI_RX1, + MAX96717_MIPI_RX1_CTRL_NUM_LANES, + FIELD_PREP(MAX96717_MIPI_RX1_CTRL_NUM_LANES, + num_data_lanes - 1)); + if (ret) + return ret; + + /* Configure lane mapping. */ + val = 0; + for (i = 0; i < 4; i++) { + unsigned int map; + + if (i < num_data_lanes) + map = phy->mipi.data_lanes[i] - 1; + else + map = ffz(used_data_lanes); + + val |= map << (i * 2); + used_data_lanes |= BIT(map); + } + + ret = regmap_update_bits(priv->regmap, MAX96717_MIPI_RX3, + MAX96717_MIPI_RX3_PHY2_LANE_MAP, + FIELD_PREP(MAX96717_MIPI_RX3_PHY2_LANE_MAP, val)); + if (ret) + return ret; + + ret = regmap_update_bits(priv->regmap, MAX96717_MIPI_RX2, + MAX96717_MIPI_RX2_PHY1_LANE_MAP, + FIELD_PREP(MAX96717_MIPI_RX2_PHY1_LANE_MAP, val >> 4)); + if (ret) + return ret; + + /* Configure lane polarity. */ + for (i = 0, val = 0; i < num_data_lanes; i++) + if (phy->mipi.lane_polarities[i + 1]) + val |= BIT(i); + + ret = regmap_update_bits(priv->regmap, MAX96717_MIPI_RX5, + MAX96717_MIPI_RX5_PHY2_POL_MAP, + FIELD_PREP(MAX96717_MIPI_RX5_PHY2_POL_MAP, val)); + if (ret) + return ret; + + ret = regmap_update_bits(priv->regmap, MAX96717_MIPI_RX4, + MAX96717_MIPI_RX4_PHY1_POL_MAP, + FIELD_PREP(MAX96717_MIPI_RX4_PHY1_POL_MAP, val >> 2)); + if (ret) + return ret; + + ret = regmap_assign_bits(priv->regmap, MAX96717_MIPI_RX5, + MAX96717_MIPI_RX5_PHY2_POL_MAP_CLK, + phy->mipi.lane_polarities[0]); + if (ret) + return ret; + + if (priv->info->supports_noncontinuous_clock) { + ret = regmap_assign_bits(priv->regmap, MAX96717_MIPI_RX0, + MAX96717_MIPI_RX0_NONCONTCLK_EN, + phy->mipi.flags & + V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK); + if (ret) + return ret; + } + + return 0; +} + +static int max96717_set_phy_active(struct max_ser *ser, struct max_ser_phy *phy, + bool enable) +{ + struct max96717_priv *priv = ser_to_priv(ser); + unsigned int index = max96717_phy_id(priv, phy); + + return regmap_assign_bits(priv->regmap, MAX96717_FRONTTOP_0, + MAX96717_FRONTTOP_0_START_PORT(index), enable); +} + +static int max96717_set_pipe_stream_id(struct max_ser *ser, + struct max_ser_pipe *pipe, + unsigned int stream_id) +{ + struct max96717_priv *priv = ser_to_priv(ser); + unsigned int index = max96717_pipe_id(priv, pipe); + + return regmap_update_bits(priv->regmap, MAX96717_TX3(index), + MAX96717_TX3_TX_STR_SEL, + FIELD_PREP(MAX96717_TX3_TX_STR_SEL, stream_id)); +} + +static int max96717_set_pipe_phy(struct max_ser *ser, struct max_ser_pipe *pipe, + struct max_ser_phy *phy) +{ + struct max96717_priv *priv = ser_to_priv(ser); + unsigned int index = max96717_pipe_id(priv, pipe); + unsigned int phy_id = max96717_phy_id(priv, phy); + int ret; + + ret = regmap_assign_bits(priv->regmap, MAX96717_FRONTTOP_0, + MAX96717_FRONTTOP_0_CLK_SEL_P(index), + phy_id == 1); + if (ret) + return ret; + + ret = regmap_assign_bits(priv->regmap, MAX96717_FRONTTOP_9, + MAX96717_FRONTTOP_9_START_PORT(index, 0), + phy_id == 0); + if (ret) + return ret; + + return regmap_assign_bits(priv->regmap, MAX96717_FRONTTOP_9, + MAX96717_FRONTTOP_9_START_PORT(index, 1), + phy_id == 1); +} + +static int max96717_set_pipe_mode(struct max_ser *ser, + struct max_ser_pipe *pipe, + struct max_ser_pipe_mode *mode) +{ + struct max96717_priv *priv = ser_to_priv(ser); + unsigned int index = max96717_pipe_id(priv, pipe); + int ret; + + ret = regmap_assign_bits(priv->regmap, MAX96717_VIDEO_TX0(index), + MAX96717_VIDEO_TX0_AUTO_BPP, !mode->bpp); + if (ret) + return ret; + + ret = regmap_update_bits(priv->regmap, MAX96717_VIDEO_TX1(index), + MAX96717_VIDEO_TX1_BPP, + FIELD_PREP(MAX96717_VIDEO_TX1_BPP, mode->bpp)); + if (ret) + return ret; + + ret = regmap_assign_bits(priv->regmap, MAX96717_VIDEO_TX2(index), + MAX96717_VIDEO_TX2_DRIFT_DET_EN, !mode->bpp); + if (ret) + return ret; + + ret = regmap_assign_bits(priv->regmap, MAX96717_FRONTTOP_10, + MAX96717_FRONTTOP_10_BPP8DBL(index), + mode->dbl8); + if (ret) + return ret; + + ret = regmap_assign_bits(priv->regmap, MAX96717_FRONTTOP_11, + MAX96717_FRONTTOP_11_BPP10DBL(index), + mode->dbl10); + if (ret) + return ret; + + ret = regmap_assign_bits(priv->regmap, MAX96717_FRONTTOP_11, + MAX96717_FRONTTOP_11_BPP12DBL(index), + mode->dbl12); + if (ret) + return ret; + + return regmap_update_bits(priv->regmap, MAX96717_FRONTTOP_20(index), + MAX96717_FRONTTOP_20_SOFT_BPP | + MAX96717_FRONTTOP_20_SOFT_BPP_EN, + FIELD_PREP(MAX96717_FRONTTOP_20_SOFT_BPP, + mode->soft_bpp) | + FIELD_PREP(MAX96717_FRONTTOP_20_SOFT_BPP_EN, + !!mode->soft_bpp)); +} + +static int max96717_set_i2c_xlate(struct max_ser *ser, unsigned int i, + struct max_serdes_i2c_xlate *xlate) +{ + struct max96717_priv *priv = ser_to_priv(ser); + int ret; + + ret = regmap_update_bits(priv->regmap, MAX96717_I2C_2(i), + MAX96717_I2C_2_SRC, + FIELD_PREP(MAX96717_I2C_2_SRC, xlate->src)); + if (ret) + return ret; + + return regmap_update_bits(priv->regmap, MAX96717_I2C_3(i), + MAX96717_I2C_3_DST, + FIELD_PREP(MAX96717_I2C_3_DST, xlate->dst)); +} + +static int max96717_set_tunnel_enable(struct max_ser *ser, bool enable) +{ + struct max96717_priv *priv = ser_to_priv(ser); + + return regmap_assign_bits(priv->regmap, MAX96717_EXT11, + MAX96717_EXT11_TUN_MODE, enable); +} + +static int max96717_set_tpg_timings(struct max96717_priv *priv, + const struct max_serdes_tpg_timings *tm, + unsigned int index) +{ + const struct reg_sequence regs[] = { + REG_SEQUENCE_3(MAX96717_VTX2_VS_DLY_2(index), tm->vs_dly), + REG_SEQUENCE_3(MAX96717_VTX5_VS_HIGH_2(index), tm->vs_high), + REG_SEQUENCE_3(MAX96717_VTX8_VS_LOW_2(index), tm->vs_low), + REG_SEQUENCE_3(MAX96717_VTX11_V2H_2(index), tm->v2h), + REG_SEQUENCE_2(MAX96717_VTX14_HS_HIGH_1(index), tm->hs_high), + REG_SEQUENCE_2(MAX96717_VTX16_HS_LOW_1(index), tm->hs_low), + REG_SEQUENCE_2(MAX96717_VTX18_HS_CNT_1(index), tm->hs_cnt), + REG_SEQUENCE_3(MAX96717_VTX20_V2D_2(index), tm->v2d), + REG_SEQUENCE_2(MAX96717_VTX23_DE_HIGH_1(index), tm->de_high), + REG_SEQUENCE_2(MAX96717_VTX25_DE_LOW_1(index), tm->de_low), + REG_SEQUENCE_2(MAX96717_VTX27_DE_CNT_1(index), tm->de_cnt), + }; + int ret; + + ret = regmap_multi_reg_write(priv->regmap, regs, ARRAY_SIZE(regs)); + if (ret) + return ret; + + return regmap_write(priv->regmap, MAX96717_VTX0(index), + FIELD_PREP(MAX96717_VTX0_VTG_MODE, + MAX96717_VTX0_VTG_MODE_FREE_RUNNING) | + FIELD_PREP(MAX96717_VTX0_DE_INV, tm->de_inv) | + FIELD_PREP(MAX96717_VTX0_HS_INV, tm->hs_inv) | + FIELD_PREP(MAX96717_VTX0_VS_INV, tm->vs_inv) | + FIELD_PREP(MAX96717_VTX0_GEN_DE, tm->gen_de) | + FIELD_PREP(MAX96717_VTX0_GEN_HS, tm->gen_hs) | + FIELD_PREP(MAX96717_VTX0_GEN_VS, tm->gen_vs)); +} + +static int max96717_set_tpg_clk(struct max96717_priv *priv, u32 clock, + unsigned int index) +{ + u8 pclk_src; + + switch (clock) { + case 25000000: + pclk_src = MAX96717_VTX1_PATGEN_CLK_SRC_25MHZ; + break; + case 75000000: + pclk_src = MAX96717_VTX1_PATGEN_CLK_SRC_75MHZ; + break; + case 150000000: + pclk_src = MAX96717_VTX1_PATGEN_CLK_SRC_150MHZ; + break; + case 375000000: + pclk_src = MAX96717_VTX1_PATGEN_CLK_SRC_375MHZ; + break; + case 0: + return 0; + default: + return -EINVAL; + } + + return regmap_update_bits(priv->regmap, MAX96717_VTX1(index), + MAX96717_VTX1_PATGEN_CLK_SRC, + FIELD_PREP(MAX96717_VTX1_PATGEN_CLK_SRC, + pclk_src)); +} + +static int max96717_set_tpg_mode(struct max96717_priv *priv, bool enable, + unsigned int index) +{ + unsigned int patgen_mode; + + switch (priv->ser.tpg_pattern) { + case MAX_SERDES_TPG_PATTERN_GRADIENT: + patgen_mode = MAX96717_VTX29_PATGEN_MODE_GRADIENT; + break; + case MAX_SERDES_TPG_PATTERN_CHECKERBOARD: + patgen_mode = MAX96717_VTX29_PATGEN_MODE_CHECKER; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(priv->regmap, MAX96717_VTX29(index), + MAX96717_VTX29_PATGEN_MODE, + FIELD_PREP(MAX96717_VTX29_PATGEN_MODE, + enable ? patgen_mode + : MAX96717_VTX29_PATGEN_MODE_DISABLED)); +} + +static int max96717_set_tpg(struct max_ser *ser, + const struct max_serdes_tpg_entry *entry) +{ + struct max96717_priv *priv = ser_to_priv(ser); + /* + * MAX9295A supports multiple pipes, each with a pattern generator, + * use only the first pipe for simplicity. + */ + unsigned int index = max96717_pipe_id(priv, &ser->pipes[0]); + struct max_serdes_tpg_timings timings = { 0 }; + int ret; + + ret = max_serdes_get_tpg_timings(entry, &timings); + if (ret) + return ret; + + ret = max96717_set_tpg_timings(priv, &timings, index); + if (ret) + return ret; + + ret = max96717_set_tpg_clk(priv, timings.clock, index); + if (ret) + return ret; + + return max96717_set_tpg_mode(priv, entry, index); +} + +static const struct max_serdes_phys_config max96717_phys_configs[] = { + { { 4 } }, +}; + +static int max96717_init_tpg(struct max_ser *ser) +{ + struct max96717_priv *priv = ser_to_priv(ser); + /* + * MAX9295A supports multiple pipes, each with a pattern generator, + * use only the first pipe for simplicity. + */ + unsigned int index = max96717_pipe_id(priv, &ser->pipes[0]); + + const struct reg_sequence regs[] = { + { MAX96717_VTX30_GRAD_INCR(index), MAX_SERDES_GRAD_INCR }, + REG_SEQUENCE_3_LE(MAX96717_VTX31_CHKR_A_L(index), + MAX_SERDES_CHECKER_COLOR_A), + REG_SEQUENCE_3_LE(MAX96717_VTX34_CHKR_B_L(index), + MAX_SERDES_CHECKER_COLOR_B), + { MAX96717_VTX37_CHKR_RPT_A(index), MAX_SERDES_CHECKER_SIZE }, + { MAX96717_VTX38_CHKR_RPT_B(index), MAX_SERDES_CHECKER_SIZE }, + { MAX96717_VTX39_CHKR_ALT(index), MAX_SERDES_CHECKER_SIZE }, + }; + + return regmap_multi_reg_write(priv->regmap, regs, ARRAY_SIZE(regs)); +} + +static int max96717_init(struct max_ser *ser) +{ + struct max96717_priv *priv = ser_to_priv(ser); + int ret; + + /* + * Set CMU2 PFDDIV to 1.1V for correct functionality of the device, + * as mentioned in the datasheet, under section MANDATORY REGISTER PROGRAMMING. + */ + ret = regmap_update_bits(priv->regmap, MAX96717_CMU2, + MAX96717_CMU2_PFDDIV_RSHORT, + FIELD_PREP(MAX96717_CMU2_PFDDIV_RSHORT, + MAX96717_CMU2_PFDDIV_RSHORT_1_1V)); + if (ret) + return ret; + + if (ser->ops->set_tunnel_enable) { + ret = ser->ops->set_tunnel_enable(ser, false); + if (ret) + return ret; + } + + return max96717_init_tpg(ser); +} + +static const struct pinctrl_ops max96717_ctrl_ops = { + .get_groups_count = max96717_ctrl_get_groups_count, + .get_group_name = max96717_ctrl_get_group_name, + .get_group_pins = max96717_ctrl_get_group_pins, +#ifndef CONFIG_ACPI + .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, + .dt_free_map = pinconf_generic_dt_free_map, +#endif +}; + +static const struct pinconf_ops max96717_conf_ops = { + .pin_config_get = max96717_conf_pin_config_get, + .pin_config_set = max96717_conf_pin_config_set, + .is_generic = true, +}; + +static const struct pinmux_ops max96717_mux_ops = { + .get_functions_count = max96717_mux_get_functions_count, + .get_function_name = max96717_mux_get_function_name, + .get_function_groups = max96717_mux_get_groups, + .set_mux = max96717_mux_set, +}; + +static const struct max_serdes_tpg_entry max96717_tpg_entries[] = { + MAX_TPG_ENTRY_640X480P60_RGB888, + MAX_TPG_ENTRY_1920X1080P30_RGB888, + MAX_TPG_ENTRY_1920X1080P60_RGB888, +}; + +static const struct max_ser_ops max96717_ops = { + .num_i2c_xlates = 2, + .phys_configs = { + .num_configs = ARRAY_SIZE(max96717_phys_configs), + .configs = max96717_phys_configs, + }, + .tpg_entries = { + .num_entries = ARRAY_SIZE(max96717_tpg_entries), + .entries = max96717_tpg_entries, + }, + .tpg_mode = MAX_SERDES_GMSL_PIXEL_MODE, + .tpg_patterns = BIT(MAX_SERDES_TPG_PATTERN_CHECKERBOARD) | + BIT(MAX_SERDES_TPG_PATTERN_GRADIENT), +#ifdef CONFIG_VIDEO_ADV_DEBUG + .reg_read = max96717_reg_read, + .reg_write = max96717_reg_write, +#endif + .log_status = max96717_log_status, + .log_pipe_status = max96717_log_pipe_status, + .log_phy_status = max96717_log_phy_status, + .init = max96717_init, + .set_i2c_xlate = max96717_set_i2c_xlate, + .set_tpg = max96717_set_tpg, + .init_phy = max96717_init_phy, + .set_phy_active = max96717_set_phy_active, + .set_pipe_enable = max96717_set_pipe_enable, + .set_pipe_dt = max96717_set_pipe_dt, + .set_pipe_dt_en = max96717_set_pipe_dt_en, + .set_pipe_vcs = max96717_set_pipe_vcs, + .set_pipe_mode = max96717_set_pipe_mode, + .set_pipe_stream_id = max96717_set_pipe_stream_id, + .set_pipe_phy = max96717_set_pipe_phy, +}; + +struct max96717_pll_predef_freq { + unsigned long freq; + bool is_rclk; + bool is_alt; + u8 val; + u8 rclksel; +}; + +static const struct max96717_pll_predef_freq max96717_predef_freqs[] = { + { 6250000, true, false, 0, 2 }, + { 12500000, true, false, 0, 1 }, + { 13500000, false, true, 0, 3 }, + { 19200000, false, false, 0, 3 }, + { 24000000, false, true, 1, 3 }, + { 25000000, true, false, 0, 0 }, + { 27000000, false, false, 1, 3 }, + { 37125000, false, false, 2, 3 }, + { 74250000, false, false, 3, 3 }, +}; + +static unsigned long +max96717_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + struct max96717_priv *priv = clk_hw_to_priv(hw); + + return max96717_predef_freqs[priv->pll_predef_index].freq; +} + +static unsigned int max96717_clk_find_best_index(struct max96717_priv *priv, + unsigned long rate) +{ + unsigned int i, idx = 0; + unsigned long diff_new, diff_old = U32_MAX; + + for (i = 0; i < ARRAY_SIZE(max96717_predef_freqs); i++) { + diff_new = abs(rate - max96717_predef_freqs[i].freq); + if (diff_new < diff_old) { + diff_old = diff_new; + idx = i; + } + } + + return idx; +} + +static long max96717_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct max96717_priv *priv = clk_hw_to_priv(hw); + struct device *dev = &priv->client->dev; + unsigned int idx; + + idx = max96717_clk_find_best_index(priv, rate); + + if (rate != max96717_predef_freqs[idx].freq) { + dev_warn(dev, "Request CLK freq:%lu, found CLK freq:%lu\n", + rate, max96717_predef_freqs[idx].freq); + } + + return max96717_predef_freqs[idx].freq; +} + +static int max96717_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + const struct max96717_pll_predef_freq *predef_freq; + struct max96717_priv *priv = clk_hw_to_priv(hw); + unsigned int val, idx; + int ret = 0; + + idx = max96717_clk_find_best_index(priv, rate); + predef_freq = &max96717_predef_freqs[idx]; + + ret = regmap_update_bits(priv->regmap, MAX96717_REG3, + MAX96717_REG3_RCLKSEL, + FIELD_PREP(MAX96717_REG3_RCLKSEL, + predef_freq->rclksel)); + if (ret) + return ret; + + val = FIELD_PREP(MAX96717_REF_VTG0_REFGEN_PREDEF_FREQ, + predef_freq->val); + + if (predef_freq->is_alt) + val |= MAX96717_REF_VTG0_REFGEN_PREDEF_FREQ_ALT; + if (!predef_freq->is_rclk) + val |= MAX96717_REF_VTG0_REFGEN_EN; + + val |= MAX96717_REF_VTG0_REFGEN_RST; + + ret = regmap_write(priv->regmap, MAX96717_REF_VTG0, val); + if (ret) + return ret; + + ret = regmap_clear_bits(priv->regmap, MAX96717_REF_VTG0, + MAX96717_REF_VTG0_REFGEN_RST); + if (ret) + return ret; + + priv->pll_predef_index = idx; + + return 0; +} + +static int max96717_clk_prepare(struct clk_hw *hw) +{ + struct max96717_priv *priv = clk_hw_to_priv(hw); + + return regmap_set_bits(priv->regmap, MAX96717_REG6, MAX96717_REG6_RCLKEN); +} + +static void max96717_clk_unprepare(struct clk_hw *hw) +{ + struct max96717_priv *priv = clk_hw_to_priv(hw); + + regmap_clear_bits(priv->regmap, MAX96717_REG6, MAX96717_REG6_RCLKEN); +} + +static const struct clk_ops max96717_clk_ops = { + .prepare = max96717_clk_prepare, + .unprepare = max96717_clk_unprepare, + .set_rate = max96717_clk_set_rate, + .recalc_rate = max96717_clk_recalc_rate, + .round_rate = max96717_clk_round_rate, +}; + +static int max96717_register_clkout(struct max96717_priv *priv) +{ + struct device *dev = &priv->client->dev; + struct clk_init_data init = { .ops = &max96717_clk_ops }; + int ret; + + ret = max96717_mux_set_rclkout(priv, MAX96717_RCLK_MFP); + if (ret) + return ret; + + init.name = kasprintf(GFP_KERNEL, "max96717.%s.clk_out", dev_name(dev)); + if (!init.name) + return -ENOMEM; + + priv->clk_hw.init = &init; + + ret = max96717_clk_set_rate(&priv->clk_hw, + MAX96717_DEFAULT_CLKOUT_RATE, 0); + if (ret) + goto free_init_name; + + ret = devm_clk_hw_register(dev, &priv->clk_hw); + kfree(init.name); + if (ret) + return dev_err_probe(dev, ret, "Cannot register clock HW\n"); + + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, + &priv->clk_hw); + if (ret) + return dev_err_probe(dev, ret, + "Cannot add OF clock provider\n"); + + return 0; + +free_init_name: + kfree(init.name); + return ret; +} + +static int max96717_gpiochip_probe(struct max96717_priv *priv) +{ + struct device *dev = priv->dev; + int ret; + + priv->pctldesc = (struct pinctrl_desc) { + .owner = THIS_MODULE, + .name = MAX96717_PINCTRL_NAME, + .pins = max96717_pins, + .npins = ARRAY_SIZE(max96717_pins), + .pctlops = &max96717_ctrl_ops, + .confops = &max96717_conf_ops, + .pmxops = &max96717_mux_ops, + .custom_params = max96717_cfg_params, + .num_custom_params = ARRAY_SIZE(max96717_cfg_params), + }; + + ret = devm_pinctrl_register_and_init(dev, &priv->pctldesc, priv, &priv->pctldev); + if (ret) + return ret; + + ret = pinctrl_enable(priv->pctldev); + if (ret) + return ret; + + priv->gc = (struct gpio_chip) { + .owner = THIS_MODULE, + .label = MAX96717_GPIOCHIP_NAME, + .base = -1, + .ngpio = MAX96717_GPIO_NUM, + .parent = dev, + .can_sleep = true, + .request = gpiochip_generic_request, + .free = gpiochip_generic_free, + .set_config = gpiochip_generic_config, + .get_direction = max96717_gpio_get_direction, + .direction_input = max96717_gpio_direction_input, + .direction_output = max96717_gpio_direction_output, + .get = max96717_gpio_get, +#if 0 + .set_rv = max96717_gpio_set, +#else + .set = max96717_gpio_set, +#endif + }; + + return devm_gpiochip_add_data(dev, &priv->gc, priv); +} + +static int max96717_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct max96717_priv *priv; + struct max_ser_ops *ops; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL); + if (!ops) + return -ENOMEM; + + priv->info = device_get_match_data(dev); + if (!priv->info) { + dev_err(dev, "Failed to get match data\n"); + return -ENODEV; + } + + priv->dev = dev; + priv->client = client; + i2c_set_clientdata(client, priv); + + priv->regmap = devm_regmap_init_i2c(client, &max96717_i2c_regmap); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + *ops = max96717_ops; + + if (priv->info->modes & BIT(MAX_SERDES_GMSL_TUNNEL_MODE)) + ops->set_tunnel_enable = max96717_set_tunnel_enable; + + ops->modes = priv->info->modes; + ops->num_pipes = priv->info->num_pipes; + ops->num_dts_per_pipe = priv->info->num_dts_per_pipe; + ops->num_phys = priv->info->num_phys; + priv->ser.ops = ops; + + ret = max96717_wait_for_device(priv); + if (ret) + return ret; + + ret = max96717_gpiochip_probe(priv); + if (ret) + return ret; + + ret = max96717_register_clkout(priv); + if (ret) + return ret; + + return max_ser_probe(client, &priv->ser); +} + +static void max96717_remove(struct i2c_client *client) +{ + struct max96717_priv *priv = i2c_get_clientdata(client); + + max_ser_remove(&priv->ser); +} + +static const struct max96717_chip_info max9295a_info = { + .modes = BIT(MAX_SERDES_GMSL_PIXEL_MODE), + .num_pipes = 4, + .num_dts_per_pipe = 2, + .pipe_hw_ids = { 0, 1, 2, 3 }, + .num_phys = 1, + .phy_hw_ids = { 1 }, +}; + +static const struct max96717_chip_info max96717_info = { + .modes = BIT(MAX_SERDES_GMSL_PIXEL_MODE) | + BIT(MAX_SERDES_GMSL_TUNNEL_MODE), + .supports_3_data_lanes = true, + .supports_pkt_cnt = true, + .supports_noncontinuous_clock = true, + .num_pipes = 1, + .num_dts_per_pipe = 4, + .pipe_hw_ids = { 2 }, + .num_phys = 1, + .phy_hw_ids = { 1 }, +}; + +static const struct acpi_device_id max9295a_acpi_ids[] = { + { "INTC1138", .driver_data = (kernel_ulong_t)&max9295a_info}, + {} +}; +MODULE_DEVICE_TABLE(acpi, max9295a_acpi_ids); + +static const struct of_device_id max96717_of_ids[] = { + { .compatible = "maxim,max9295a", .data = &max9295a_info }, + { .compatible = "maxim,max96717", .data = &max96717_info }, + { .compatible = "maxim,max96717f", .data = &max96717_info }, + { .compatible = "maxim,max96793", .data = &max96717_info }, + { } +}; +MODULE_DEVICE_TABLE(of, max96717_of_ids); + +static struct i2c_driver max96717_i2c_driver = { + .driver = { + .name = MAX96717_NAME, + .of_match_table = max96717_of_ids, + .acpi_match_table = ACPI_PTR(max9295a_acpi_ids), + }, + .probe = max96717_probe, + .remove = max96717_remove, +}; + +module_i2c_driver(max96717_i2c_driver); + +MODULE_IMPORT_NS("MAX_SERDES"); +MODULE_DESCRIPTION("MAX96717 GMSL2 Serializer Driver"); +MODULE_AUTHOR("Cosmin Tanislav "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/maxim-serdes/max96724.c b/drivers/media/i2c/maxim-serdes/max96724.c new file mode 100644 index 00000000000000..763a12ecc18172 --- /dev/null +++ b/drivers/media/i2c/maxim-serdes/max96724.c @@ -0,0 +1,1191 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Maxim MAX96724 Quad GMSL2 Deserializer Driver + * + * Copyright (C) 2025 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "max_des.h" + +#define MAX96724_REG0 0x0 + +#define MAX96724_REG6 0x6 +#define MAX96724_REG6_LINK_EN GENMASK(3, 0) + +#define MAX96724_DEBUG_EXTRA 0x9 +#define MAX96724_DEBUG_EXTRA_PCLK_SRC GENMASK(1, 0) +#define MAX96724_DEBUG_EXTRA_PCLK_SRC_25MHZ 0b00 +#define MAX96724_DEBUG_EXTRA_PCLK_SRC_75MHZ 0b01 +#define MAX96724_DEBUG_EXTRA_PCLK_SRC_USE_PIPE 0b10 + +#define MAX96724_REG26(x) (0x10 + (x) / 2) +#define MAX96724_REG26_RX_RATE_PHY(x) (GENMASK(1, 0) << (4 * ((x) % 2))) +#define MAX96724_REG26_RX_RATE_3GBPS 0b01 +#define MAX96724_REG26_RX_RATE_6GBPS 0b10 + +#define MAX96724_PWR1 0x13 +#define MAX96724_PWR1_RESET_ALL BIT(6) + +#define MAX96724_CTRL1 0x18 +#define MAX96724_CTRL1_RESET_ONESHOT GENMASK(3, 0) + +#define MAX96724_VIDEO_PIPE_SEL(p) (0xf0 + (p) / 2) +#define MAX96724_VIDEO_PIPE_SEL_STREAM(p) (GENMASK(1, 0) << (4 * ((p) % 2))) +#define MAX96724_VIDEO_PIPE_SEL_LINK(p) (GENMASK(3, 2) << (4 * ((p) % 2))) + +#define MAX96724_VIDEO_PIPE_EN 0xf4 +#define MAX96724_VIDEO_PIPE_EN_MASK(p) BIT(p) +#define MAX96724_VIDEO_PIPE_EN_STREAM_SEL_ALL BIT(4) + +#define MAX96724_VPRBS(p) (0x1dc + (p) * 0x20) +#define MAX96724_VPRBS_VIDEO_LOCK BIT(0) +#define MAX96724_VPRBS_PATGEN_CLK_SRC BIT(7) +#define MAX96724_VPRBS_PATGEN_CLK_SRC_150MHZ 0b0 +#define MAX96724_VPRBS_PATGEN_CLK_SRC_375MHZ 0b1 + +#define MAX96724_BACKTOP12 0x40b +#define MAX96724_BACKTOP12_CSI_OUT_EN BIT(1) + +#define MAX96724_BACKTOP21(p) (0x414 + (p) / 4 * 0x20) +#define MAX96724_BACKTOP21_BPP8DBL(p) BIT(4 + (p) % 4) + +#define MAX96724_BACKTOP22(x) (0x415 + (x) * 0x3) +#define MAX96724_BACKTOP22_PHY_CSI_TX_DPLL GENMASK(4, 0) +#define MAX96724_BACKTOP22_PHY_CSI_TX_DPLL_EN BIT(5) + +#define MAX96724_BACKTOP24(p) (0x417 + (p) / 4 * 0x20) +#define MAX96724_BACKTOP24_BPP8DBL_MODE(p) BIT(4 + (p) % 4) + +#define MAX96724_BACKTOP30(p) (0x41d + (p) / 4 * 0x20) +#define MAX96724_BACKTOP30_BPP10DBL3 BIT(4) +#define MAX96724_BACKTOP30_BPP10DBL3_MODE BIT(5) + +#define MAX96724_BACKTOP31(p) (0x41e + (p) / 4 * 0x20) +#define MAX96724_BACKTOP31_BPP10DBL2 BIT(6) +#define MAX96724_BACKTOP31_BPP10DBL2_MODE BIT(7) + +#define MAX96724_BACKTOP32(p) (0x41f + (p) / 4 * 0x20) +#define MAX96724_BACKTOP32_BPP12(p) BIT(p) +#define MAX96724_BACKTOP32_BPP10DBL0 BIT(4) +#define MAX96724_BACKTOP32_BPP10DBL0_MODE BIT(5) +#define MAX96724_BACKTOP32_BPP10DBL1 BIT(6) +#define MAX96724_BACKTOP32_BPP10DBL1_MODE BIT(7) + +#define MAX96724_MIPI_PHY0 0x8a0 +#define MAX96724_MIPI_PHY0_PHY_CONFIG GENMASK(4, 0) +#define MAX96724_MIPI_PHY0_PHY_4X2 BIT(0) +#define MAX96724_MIPI_PHY0_PHY_2X4 BIT(2) +#define MAX96724_MIPI_PHY0_PHY_1X4A_2X2 BIT(3) +#define MAX96724_MIPI_PHY0_PHY_1X4B_2X2 BIT(4) +#define MAX96724_MIPI_PHY0_FORCE_CSI_OUT_EN BIT(7) + +#define MAX96724_MIPI_PHY2 0x8a2 +#define MAX96724_MIPI_PHY2_PHY_STDB_N_4(x) (GENMASK(5, 4) << ((x) / 2 * 2)) +#define MAX96724_MIPI_PHY2_PHY_STDB_N_2(x) (BIT(4 + (x))) + +#define MAX96724_MIPI_PHY3(x) (0x8a3 + (x) / 2) +#define MAX96724_MIPI_PHY3_PHY_LANE_MAP_4 GENMASK(7, 0) +#define MAX96724_MIPI_PHY3_PHY_LANE_MAP_2(x) (GENMASK(3, 0) << (4 * ((x) % 2))) + +#define MAX96724_MIPI_PHY5(x) (0x8a5 + (x) / 2) +#define MAX96724_MIPI_PHY5_PHY_POL_MAP_4_0_1 GENMASK(1, 0) +#define MAX96724_MIPI_PHY5_PHY_POL_MAP_4_2_3 GENMASK(4, 3) +#define MAX96724_MIPI_PHY5_PHY_POL_MAP_4_CLK BIT(5) +#define MAX96724_MIPI_PHY5_PHY_POL_MAP_2(x) (GENMASK(1, 0) << (3 * ((x) % 2))) +#define MAX96724_MIPI_PHY5_PHY_POL_MAP_2_CLK(x) BIT(2 + 3 * ((x) % 2)) + +#define MAX96724_MIPI_PHY13 0x8ad +#define MAX96724_MIPI_PHY13_T_T3_PREBEGIN GENMASK(5, 0) +#define MAX96724_MIPI_PHY13_T_T3_PREBEGIN_64X7 FIELD_PREP(MAX96724_MIPI_PHY13_T_T3_PREBEGIN, 63) + +#define MAX96724_MIPI_PHY14 0x8ae +#define MAX96724_MIPI_PHY14_T_T3_PREP GENMASK(1, 0) +#define MAX96724_MIPI_PHY14_T_T3_PREP_55NS FIELD_PREP(MAX96724_MIPI_PHY14_T_T3_PREP, 0b01) +#define MAX96724_MIPI_PHY14_T_T3_POST GENMASK(6, 2) +#define MAX96724_MIPI_PHY14_T_T3_POST_32X7 FIELD_PREP(MAX96724_MIPI_PHY14_T_T3_POST, 31) + +#define MAX96724_MIPI_CTRL_SEL 0x8ca +#define MAX96724_MIPI_CTRL_SEL_MASK(p) (GENMASK(1, 0) << ((p) * 2)) + +#define MAX96724_MIPI_PHY25(x) (0x8d0 + (x) / 2) +#define MAX96724_MIPI_PHY25_CSI2_TX_PKT_CNT(x) (GENMASK(3, 0) << (4 * ((x) % 2))) + +#define MAX96724_MIPI_PHY27(x) (0x8d2 + (x) / 2) +#define MAX96724_MIPI_PHY27_PHY_PKT_CNT(x) (GENMASK(3, 0) << (4 * ((x) % 2))) + +#define MAX96724_MIPI_TX3(x) (0x903 + (x) * 0x40) +#define MAX96724_MIPI_TX3_DESKEW_INIT_8X32K FIELD_PREP(GENMASK(2, 0), 0b001) +#define MAX96724_MIPI_TX3_DESKEW_INIT_AUTO BIT(7) + +#define MAX96724_MIPI_TX4(x) (0x904 + (x) * 0x40) +#define MAX96724_MIPI_TX4_DESKEW_PER_2K FIELD_PREP(GENMASK(2, 0), 0b001) +#define MAX96724_MIPI_TX4_DESKEW_PER_AUTO BIT(7) + +#define MAX96724_MIPI_TX10(x) (0x90a + (x) * 0x40) +#define MAX96724_MIPI_TX10_CSI2_CPHY_EN BIT(5) +#define MAX96724_MIPI_TX10_CSI2_LANE_CNT GENMASK(7, 6) + +#define MAX96724_MIPI_TX11(p) (0x90b + (p) * 0x40) +#define MAX96724_MIPI_TX12(p) (0x90c + (p) * 0x40) + +#define MAX96724_MIPI_TX13(p, x) (0x90d + (p) * 0x40 + (x) * 0x2) +#define MAX96724_MIPI_TX13_MAP_SRC_DT GENMASK(5, 0) +#define MAX96724_MIPI_TX13_MAP_SRC_VC GENMASK(7, 6) + +#define MAX96724_MIPI_TX14(p, x) (0x90e + (p) * 0x40 + (x) * 0x2) +#define MAX96724_MIPI_TX14_MAP_DST_DT GENMASK(5, 0) +#define MAX96724_MIPI_TX14_MAP_DST_VC GENMASK(7, 6) + +#define MAX96724_MIPI_TX45(p, x) (0x92d + (p) * 0x40 + (x) / 4) +#define MAX96724_MIPI_TX45_MAP_DPHY_DEST(x) (GENMASK(1, 0) << (2 * ((x) % 4))) + +#define MAX96724_MIPI_TX51(x) (0x933 + (x) * 0x40) +#define MAX96724_MIPI_TX51_ALT_MEM_MAP_12 BIT(0) +#define MAX96724_MIPI_TX51_ALT_MEM_MAP_8 BIT(1) +#define MAX96724_MIPI_TX51_ALT_MEM_MAP_10 BIT(2) +#define MAX96724_MIPI_TX51_ALT2_MEM_MAP_8 BIT(4) + +#define MAX96724_MIPI_TX54(x) (0x936 + (x) * 0x40) +#define MAX96724_MIPI_TX54_TUN_EN BIT(0) + +#define MAX96724_MIPI_TX57(x) (0x939 + (x) * 0x40) +#define MAX96724_MIPI_TX57_TUN_DEST GENMASK(5, 4) +#define MAX96724_MIPI_TX57_DIS_AUTO_TUN_DET BIT(6) +#define MAX96724_DET(p) BIT(p) + +#define MAX96724_PATGEN_0 0x1050 +#define MAX96724_PATGEN_0_VTG_MODE GENMASK(1, 0) +#define MAX96724_PATGEN_0_VTG_MODE_FREE_RUNNING 0b11 +#define MAX96724_PATGEN_0_DE_INV BIT(2) +#define MAX96724_PATGEN_0_HS_INV BIT(3) +#define MAX96724_PATGEN_0_VS_INV BIT(4) +#define MAX96724_PATGEN_0_GEN_DE BIT(5) +#define MAX96724_PATGEN_0_GEN_HS BIT(6) +#define MAX96724_PATGEN_0_GEN_VS BIT(7) + +#define MAX96724_PATGEN_1 0x1051 +#define MAX96724_PATGEN_1_PATGEN_MODE GENMASK(5, 4) +#define MAX96724_PATGEN_1_PATGEN_MODE_DISABLED 0b00 +#define MAX96724_PATGEN_1_PATGEN_MODE_CHECKER 0b01 +#define MAX96724_PATGEN_1_PATGEN_MODE_GRADIENT 0b10 + +#define MAX96724_VS_DLY_2 0x1052 +#define MAX96724_VS_HIGH_2 0x1055 +#define MAX96724_VS_LOW_2 0x1058 +#define MAX96724_V2H_2 0x105b +#define MAX96724_HS_HIGH_1 0x105e +#define MAX96724_HS_LOW_1 0x1060 +#define MAX96724_HS_CNT_1 0x1062 +#define MAX96724_V2D_2 0x1064 +#define MAX96724_DE_HIGH_1 0x1067 +#define MAX96724_DE_LOW_1 0x1069 +#define MAX96724_DE_CNT_1 0x106b +#define MAX96724_GRAD_INCR 0x106d +#define MAX96724_CHKR_COLOR_A_L 0x106e +#define MAX96724_CHKR_COLOR_B_L 0x1071 +#define MAX96724_CHKR_RPT_A 0x1074 +#define MAX96724_CHKR_RPT_B 0x1075 +#define MAX96724_CHKR_ALT 0x1076 + +#define MAX96724_DE_DET 0x11f0 +#define MAX96724_HS_DET 0x11f1 +#define MAX96724_VS_DET 0x11f2 +#define MAX96724_HS_POL 0x11f3 +#define MAX96724_VS_POL 0x11f4 +#define MAX96724_DET(p) BIT(p) + +#define MAX96724_DPLL_0(x) (0x1c00 + (x) * 0x100) +#define MAX96724_DPLL_0_CONFIG_SOFT_RST_N BIT(0) + +#define MAX96724_PHY1_ALT_CLOCK 5 + +static const struct regmap_config max96724_i2c_regmap = { + .reg_bits = 16, + .val_bits = 8, + .max_register = 0x1f00, +}; + +struct max96724_priv { + struct max_des des; + const struct max96724_chip_info *info; + + struct device *dev; + struct i2c_client *client; + struct regmap *regmap; + + struct gpio_desc *gpiod_enable; +}; + +struct max96724_chip_info { + unsigned int versions; + unsigned int modes; + bool supports_pipe_stream_autoselect; + unsigned int num_pipes; + + int (*set_pipe_phy)(struct max_des *des, struct max_des_pipe *pipe, + struct max_des_phy *phy); + int (*set_pipe_tunnel_phy)(struct max_des *des, struct max_des_pipe *pipe, + struct max_des_phy *phy); + int (*set_pipe_tunnel_enable)(struct max_des *des, struct max_des_pipe *pipe, + bool enable); +}; + +#define des_to_priv(_des) \ + container_of(_des, struct max96724_priv, des) + +static int max96724_wait_for_device(struct max96724_priv *priv) +{ + unsigned int i; + int ret; + + for (i = 0; i < 10; i++) { + unsigned int val; + + ret = regmap_read(priv->regmap, MAX96724_REG0, &val); + if (!ret && val) + return 0; + + msleep(100); + + dev_err(priv->dev, "Retry %u waiting for deserializer: %d\n", i, ret); + } + + return ret; +} + +static int max96724_reset(struct max96724_priv *priv) +{ + int ret; + + ret = max96724_wait_for_device(priv); + if (ret) + return ret; + + ret = regmap_update_bits(priv->regmap, MAX96724_PWR1, + MAX96724_PWR1_RESET_ALL, + FIELD_PREP(MAX96724_PWR1_RESET_ALL, 1)); + if (ret) + return ret; + + fsleep(10000); + + return max96724_wait_for_device(priv); +} + +static int __maybe_unused max96724_reg_read(struct max_des *des, unsigned int reg, + unsigned int *val) +{ + struct max96724_priv *priv = des_to_priv(des); + + return regmap_read(priv->regmap, reg, val); +} + +static int __maybe_unused max96724_reg_write(struct max_des *des, unsigned int reg, + unsigned int val) +{ + struct max96724_priv *priv = des_to_priv(des); + + return regmap_write(priv->regmap, reg, val); +} + +static unsigned int max96724_phy_id(struct max_des *des, struct max_des_phy *phy) +{ + unsigned int num_hw_data_lanes = max_des_phy_hw_data_lanes(des, phy); + + /* PHY 1 is the master PHY when combining PHY 0 and PHY 1. */ + if (phy->index == 0 && num_hw_data_lanes == 4) + return 1; + + if (phy->index == 3 && num_hw_data_lanes == 4) + return 2; + + return phy->index; +} + +static int max96724_log_pipe_status(struct max_des *des, + struct max_des_pipe *pipe) +{ + struct max96724_priv *priv = des_to_priv(des); + unsigned int index = pipe->index; + unsigned int val, mask; + int ret; + + ret = regmap_read(priv->regmap, MAX96724_VPRBS(index), &val); + if (ret) + return ret; + + dev_info(priv->dev, "\tvideo_lock: %u\n", + !!(val & MAX96724_VPRBS_VIDEO_LOCK)); + + mask = MAX96724_DET(index); + + ret = regmap_read(priv->regmap, MAX96724_DE_DET, &val); + if (ret) + return ret; + + dev_info(priv->dev, "\tde_det: %u\n", !!(val & mask)); + + ret = regmap_read(priv->regmap, MAX96724_HS_DET, &val); + if (ret) + return ret; + + dev_info(priv->dev, "\ths_det: %u\n", !!(val & mask)); + + ret = regmap_read(priv->regmap, MAX96724_VS_DET, &val); + if (ret) + return ret; + + dev_info(priv->dev, "\tvs_det: %u\n", !!(val & mask)); + + ret = regmap_read(priv->regmap, MAX96724_HS_POL, &val); + if (ret) + return ret; + + dev_info(priv->dev, "\ths_pol: %u\n", !!(val & mask)); + + ret = regmap_read(priv->regmap, MAX96724_VS_POL, &val); + if (ret) + return ret; + + dev_info(priv->dev, "\tvs_pol: %u\n", !!(val & mask)); + + return 0; +} + +static int max96724_log_phy_status(struct max_des *des, + struct max_des_phy *phy) +{ + struct max96724_priv *priv = des_to_priv(des); + unsigned int index = max96724_phy_id(des, phy); + unsigned int val; + int ret; + + ret = regmap_read(priv->regmap, MAX96724_MIPI_PHY25(index), &val); + if (ret) + return ret; + + dev_info(priv->dev, "\tcsi2_pkt_cnt: %lu\n", + field_get(MAX96724_MIPI_PHY25_CSI2_TX_PKT_CNT(index), val)); + + ret = regmap_read(priv->regmap, MAX96724_MIPI_PHY27(index), &val); + if (ret) + return ret; + + dev_info(priv->dev, "\tphy_pkt_cnt: %lu\n", + field_get(MAX96724_MIPI_PHY27_PHY_PKT_CNT(index), val)); + + return 0; +} + +static int max96724_set_enable(struct max_des *des, bool enable) +{ + struct max96724_priv *priv = des_to_priv(des); + + return regmap_assign_bits(priv->regmap, MAX96724_BACKTOP12, + MAX96724_BACKTOP12_CSI_OUT_EN, enable); +} + +static const unsigned int max96724_phys_configs_reg_val[] = { + MAX96724_MIPI_PHY0_PHY_1X4A_2X2, + MAX96724_MIPI_PHY0_PHY_2X4, + + MAX96724_MIPI_PHY0_PHY_4X2, + MAX96724_MIPI_PHY0_PHY_1X4A_2X2, + MAX96724_MIPI_PHY0_PHY_1X4B_2X2, + MAX96724_MIPI_PHY0_PHY_2X4, +}; + +static const struct max_serdes_phys_config max96724_phys_configs[] = { + /* + * PHY 1 can be in 4-lane mode (combining lanes of PHY 0 and PHY 1) + * but only use the data lanes of PHY0, while continuing to use the + * clock lane of PHY 1. + * Specifying clock-lanes as 5 turns on alternate clocking mode. + */ + { { 2, 0, 2, 2 }, { MAX96724_PHY1_ALT_CLOCK, 0, 0, 0 } }, + { { 2, 0, 4, 0 }, { MAX96724_PHY1_ALT_CLOCK, 0, 0, 0 } }, + + /* + * When combining PHY 0 and PHY 1 to make them function in 4-lane mode, + * PHY 1 is the master PHY, but we use PHY 0 here to maintain + * compatibility. + */ + { { 2, 2, 2, 2 } }, + { { 4, 0, 2, 2 } }, + { { 2, 2, 4, 0 } }, + { { 4, 0, 4, 0 } }, +}; + +static int max96724_init_tpg(struct max_des *des) +{ + const struct reg_sequence regs[] = { + { MAX96724_GRAD_INCR, MAX_SERDES_GRAD_INCR }, + REG_SEQUENCE_3_LE(MAX96724_CHKR_COLOR_A_L, + MAX_SERDES_CHECKER_COLOR_A), + REG_SEQUENCE_3_LE(MAX96724_CHKR_COLOR_B_L, + MAX_SERDES_CHECKER_COLOR_B), + { MAX96724_CHKR_RPT_A, MAX_SERDES_CHECKER_SIZE }, + { MAX96724_CHKR_RPT_B, MAX_SERDES_CHECKER_SIZE }, + { MAX96724_CHKR_ALT, MAX_SERDES_CHECKER_SIZE }, + }; + struct max96724_priv *priv = des_to_priv(des); + + return regmap_multi_reg_write(priv->regmap, regs, ARRAY_SIZE(regs)); +} + +static int max96724_init(struct max_des *des) +{ + struct max96724_priv *priv = des_to_priv(des); + unsigned int i; + int ret; + + if (priv->info->set_pipe_tunnel_enable) { + for (i = 0; i < des->ops->num_pipes; i++) { + ret = regmap_set_bits(priv->regmap, MAX96724_MIPI_TX57(i), + MAX96724_MIPI_TX57_DIS_AUTO_TUN_DET); + if (ret) + return ret; + } + } + + if (priv->info->supports_pipe_stream_autoselect) { + /* Enable stream autoselect. */ + ret = regmap_set_bits(priv->regmap, MAX96724_VIDEO_PIPE_EN, + MAX96724_VIDEO_PIPE_EN_STREAM_SEL_ALL); + if (ret) + return ret; + } + + /* Set PHY mode. */ + ret = regmap_update_bits(priv->regmap, MAX96724_MIPI_PHY0, + MAX96724_MIPI_PHY0_PHY_CONFIG, + max96724_phys_configs_reg_val[des->phys_config]); + if (ret) + return ret; + + return max96724_init_tpg(des); +} + +static int max96724_init_phy(struct max_des *des, struct max_des_phy *phy) +{ + struct max96724_priv *priv = des_to_priv(des); + bool is_cphy = phy->bus_type == V4L2_MBUS_CSI2_CPHY; + unsigned int num_data_lanes = phy->mipi.num_data_lanes; + unsigned int dpll_freq = phy->link_frequency * 2; + unsigned int num_hw_data_lanes; + unsigned int index; + unsigned int used_data_lanes = 0; + unsigned int val, mask; + unsigned int i; + int ret; + + index = max96724_phy_id(des, phy); + num_hw_data_lanes = max_des_phy_hw_data_lanes(des, phy); + + ret = regmap_update_bits(priv->regmap, MAX96724_MIPI_TX10(index), + MAX96724_MIPI_TX10_CSI2_LANE_CNT, + FIELD_PREP(MAX96724_MIPI_TX10_CSI2_LANE_CNT, + num_data_lanes - 1)); + if (ret) + return ret; + + ret = regmap_assign_bits(priv->regmap, MAX96724_MIPI_TX10(index), + MAX96724_MIPI_TX10_CSI2_CPHY_EN, is_cphy); + if (ret) + return ret; + + /* Configure lane mapping. */ + val = 0; + for (i = 0; i < num_hw_data_lanes ; i++) { + unsigned int map; + + if (i < num_data_lanes) + map = phy->mipi.data_lanes[i] - 1; + else + map = ffz(used_data_lanes); + + val |= map << (i * 2); + used_data_lanes |= BIT(map); + } + + if (num_hw_data_lanes == 4) + mask = MAX96724_MIPI_PHY3_PHY_LANE_MAP_4; + else + mask = MAX96724_MIPI_PHY3_PHY_LANE_MAP_2(index); + + ret = regmap_update_bits(priv->regmap, MAX96724_MIPI_PHY3(index), + mask, field_prep(mask, val)); + if (ret) + return ret; + + /* Configure lane polarity. */ + for (i = 0, val = 0; i < num_data_lanes; i++) + if (phy->mipi.lane_polarities[i + 1]) + val |= BIT(i); + + if (num_hw_data_lanes == 4) { + ret = regmap_update_bits(priv->regmap, MAX96724_MIPI_PHY5(index), + MAX96724_MIPI_PHY5_PHY_POL_MAP_4_0_1 | + MAX96724_MIPI_PHY5_PHY_POL_MAP_4_2_3, + FIELD_PREP(MAX96724_MIPI_PHY5_PHY_POL_MAP_4_0_1, + val) | + FIELD_PREP(MAX96724_MIPI_PHY5_PHY_POL_MAP_4_2_3, + val >> 2)); + if (ret) + return ret; + + ret = regmap_assign_bits(priv->regmap, MAX96724_MIPI_PHY5(index), + MAX96724_MIPI_PHY5_PHY_POL_MAP_4_CLK, + phy->mipi.lane_polarities[0]); + if (ret) + return ret; + } else { + ret = regmap_update_bits(priv->regmap, MAX96724_MIPI_PHY5(index), + MAX96724_MIPI_PHY5_PHY_POL_MAP_2(index), + field_prep(MAX96724_MIPI_PHY5_PHY_POL_MAP_2(index), val)); + if (ret) + return ret; + + ret = regmap_assign_bits(priv->regmap, MAX96724_MIPI_PHY5(index), + MAX96724_MIPI_PHY5_PHY_POL_MAP_2_CLK(index), + phy->mipi.lane_polarities[0]); + if (ret) + return ret; + } + + if (!is_cphy && dpll_freq > 1500000000ull) { + /* Enable initial deskew with 2 x 32k UI. */ + ret = regmap_write(priv->regmap, MAX96724_MIPI_TX3(index), + MAX96724_MIPI_TX3_DESKEW_INIT_AUTO | + MAX96724_MIPI_TX3_DESKEW_INIT_8X32K); + if (ret) + return ret; + + /* Enable periodic deskew with 2 x 1k UI.. */ + ret = regmap_write(priv->regmap, MAX96724_MIPI_TX4(index), + MAX96724_MIPI_TX4_DESKEW_PER_AUTO | + MAX96724_MIPI_TX4_DESKEW_PER_2K); + if (ret) + return ret; + } else { + /* Disable initial deskew. */ + ret = regmap_write(priv->regmap, MAX96724_MIPI_TX3(index), 0x0); + if (ret) + return ret; + + /* Disable periodic deskew. */ + ret = regmap_write(priv->regmap, MAX96724_MIPI_TX4(index), 0x0); + if (ret) + return ret; + } + + if (is_cphy) { + /* Configure C-PHY timings. */ + ret = regmap_write(priv->regmap, MAX96724_MIPI_PHY13, + MAX96724_MIPI_PHY13_T_T3_PREBEGIN_64X7); + if (ret) + return ret; + + ret = regmap_write(priv->regmap, MAX96724_MIPI_PHY14, + MAX96724_MIPI_PHY14_T_T3_PREP_55NS | + MAX96724_MIPI_PHY14_T_T3_POST_32X7); + if (ret) + return ret; + } + + /* Put DPLL block into reset. */ + ret = regmap_clear_bits(priv->regmap, MAX96724_DPLL_0(index), + MAX96724_DPLL_0_CONFIG_SOFT_RST_N); + if (ret) + return ret; + + /* Set DPLL frequency. */ + ret = regmap_update_bits(priv->regmap, MAX96724_BACKTOP22(index), + MAX96724_BACKTOP22_PHY_CSI_TX_DPLL, + FIELD_PREP(MAX96724_BACKTOP22_PHY_CSI_TX_DPLL, + div_u64(dpll_freq, 100000000))); + if (ret) + return ret; + + /* Enable DPLL frequency. */ + ret = regmap_set_bits(priv->regmap, MAX96724_BACKTOP22(index), + MAX96724_BACKTOP22_PHY_CSI_TX_DPLL_EN); + if (ret) + return ret; + + /* Pull DPLL block out of reset. */ + return regmap_set_bits(priv->regmap, MAX96724_DPLL_0(index), + MAX96724_DPLL_0_CONFIG_SOFT_RST_N); +} + +static int max96724_set_phy_mode(struct max_des *des, struct max_des_phy *phy, + struct max_des_phy_mode *mode) +{ + struct max96724_priv *priv = des_to_priv(des); + unsigned int index = max96724_phy_id(des, phy); + int ret; + + /* Set alternate memory map modes. */ + ret = regmap_assign_bits(priv->regmap, MAX96724_MIPI_TX51(index), + MAX96724_MIPI_TX51_ALT_MEM_MAP_12, + mode->alt_mem_map12); + if (ret) + return ret; + + ret = regmap_assign_bits(priv->regmap, MAX96724_MIPI_TX51(index), + MAX96724_MIPI_TX51_ALT_MEM_MAP_8, + mode->alt_mem_map8); + if (ret) + return ret; + + ret = regmap_assign_bits(priv->regmap, MAX96724_MIPI_TX51(index), + MAX96724_MIPI_TX51_ALT_MEM_MAP_10, + mode->alt_mem_map10); + if (ret) + return ret; + + return regmap_assign_bits(priv->regmap, MAX96724_MIPI_TX51(index), + MAX96724_MIPI_TX51_ALT2_MEM_MAP_8, + mode->alt2_mem_map8); +} + +static int max96724_set_phy_enable(struct max_des *des, struct max_des_phy *phy, + bool enable) +{ + struct max96724_priv *priv = des_to_priv(des); + unsigned int index = max96724_phy_id(des, phy); + unsigned int num_hw_data_lanes; + unsigned int mask; + + num_hw_data_lanes = max_des_phy_hw_data_lanes(des, phy); + + if (num_hw_data_lanes == 4) + /* PHY 1 -> bits [1:0] */ + /* PHY 2 -> bits [3:2] */ + mask = MAX96724_MIPI_PHY2_PHY_STDB_N_4(index); + else + mask = MAX96724_MIPI_PHY2_PHY_STDB_N_2(index); + + return regmap_assign_bits(priv->regmap, MAX96724_MIPI_PHY2, mask, enable); +} + +static int max96724_set_pipe_remap(struct max_des *des, + struct max_des_pipe *pipe, + unsigned int i, + struct max_des_remap *remap) +{ + struct max96724_priv *priv = des_to_priv(des); + struct max_des_phy *phy = &des->phys[remap->phy]; + unsigned int phy_id = max96724_phy_id(des, phy); + unsigned int index = pipe->index; + int ret; + + /* Set source Data Type and Virtual Channel. */ + /* TODO: implement extended Virtual Channel. */ + ret = regmap_write(priv->regmap, MAX96724_MIPI_TX13(index, i), + FIELD_PREP(MAX96724_MIPI_TX13_MAP_SRC_DT, + remap->from_dt) | + FIELD_PREP(MAX96724_MIPI_TX13_MAP_SRC_VC, + remap->from_vc)); + if (ret) + return ret; + + /* Set destination Data Type and Virtual Channel. */ + /* TODO: implement extended Virtual Channel. */ + ret = regmap_write(priv->regmap, MAX96724_MIPI_TX14(index, i), + FIELD_PREP(MAX96724_MIPI_TX14_MAP_DST_DT, + remap->to_dt) | + FIELD_PREP(MAX96724_MIPI_TX14_MAP_DST_VC, + remap->to_vc)); + if (ret) + return ret; + + /* Set destination PHY. */ + return regmap_update_bits(priv->regmap, MAX96724_MIPI_TX45(index, i), + MAX96724_MIPI_TX45_MAP_DPHY_DEST(i), + field_prep(MAX96724_MIPI_TX45_MAP_DPHY_DEST(i), + phy_id)); +} + +static int max96724_set_pipe_remaps_enable(struct max_des *des, + struct max_des_pipe *pipe, + unsigned int mask) +{ + struct max96724_priv *priv = des_to_priv(des); + unsigned int index = pipe->index; + int ret; + + ret = regmap_write(priv->regmap, MAX96724_MIPI_TX11(index), mask); + if (ret) + return ret; + + return regmap_write(priv->regmap, MAX96724_MIPI_TX12(index), mask >> 8); +} + +static int max96724_set_pipe_tunnel_phy(struct max_des *des, + struct max_des_pipe *pipe, + struct max_des_phy *phy) +{ + struct max96724_priv *priv = des_to_priv(des); + unsigned int phy_index = max96724_phy_id(des, phy); + + return regmap_update_bits(priv->regmap, MAX96724_MIPI_TX57(pipe->index), + MAX96724_MIPI_TX57_TUN_DEST, + FIELD_PREP(MAX96724_MIPI_TX57_TUN_DEST, + phy_index)); +} + +static int max96724_set_pipe_phy(struct max_des *des, struct max_des_pipe *pipe, + struct max_des_phy *phy) +{ + struct max96724_priv *priv = des_to_priv(des); + unsigned int phy_index = max96724_phy_id(des, phy); + + return regmap_update_bits(priv->regmap, MAX96724_MIPI_CTRL_SEL, + MAX96724_MIPI_CTRL_SEL_MASK(pipe->index), + field_prep(MAX96724_MIPI_CTRL_SEL_MASK(pipe->index), + phy_index)); +} + +static int max96724_set_pipe_enable(struct max_des *des, struct max_des_pipe *pipe, + bool enable) +{ + struct max96724_priv *priv = des_to_priv(des); + unsigned int index = pipe->index; + + return regmap_assign_bits(priv->regmap, MAX96724_VIDEO_PIPE_EN, + MAX96724_VIDEO_PIPE_EN_MASK(index), enable); +} + +static int max96724_set_pipe_stream_id(struct max_des *des, struct max_des_pipe *pipe, + unsigned int stream_id) +{ + struct max96724_priv *priv = des_to_priv(des); + unsigned int index = pipe->index; + + return regmap_update_bits(priv->regmap, MAX96724_VIDEO_PIPE_SEL(index), + MAX96724_VIDEO_PIPE_SEL_STREAM(index), + field_prep(MAX96724_VIDEO_PIPE_SEL_STREAM(index), + stream_id)); +} + +static int max96724_set_pipe_link(struct max_des *des, struct max_des_pipe *pipe, + struct max_des_link *link) +{ + struct max96724_priv *priv = des_to_priv(des); + unsigned int index = pipe->index; + + return regmap_update_bits(priv->regmap, MAX96724_VIDEO_PIPE_SEL(index), + MAX96724_VIDEO_PIPE_SEL_LINK(index), + field_prep(MAX96724_VIDEO_PIPE_SEL_LINK(index), + link->index)); +} + +static int max96724_set_pipe_mode(struct max_des *des, + struct max_des_pipe *pipe, + struct max_des_pipe_mode *mode) +{ + struct max96724_priv *priv = des_to_priv(des); + unsigned int index = pipe->index; + unsigned int reg, mask, mode_mask; + int ret; + + /* Set 8bit double mode. */ + ret = regmap_assign_bits(priv->regmap, MAX96724_BACKTOP21(index), + MAX96724_BACKTOP21_BPP8DBL(index), mode->dbl8); + if (ret) + return ret; + + ret = regmap_assign_bits(priv->regmap, MAX96724_BACKTOP24(index), + MAX96724_BACKTOP24_BPP8DBL_MODE(index), + mode->dbl8mode); + if (ret) + return ret; + + /* Set 10bit double mode. */ + if (index % 4 == 3) { + reg = MAX96724_BACKTOP30(index); + mask = MAX96724_BACKTOP30_BPP10DBL3; + mode_mask = MAX96724_BACKTOP30_BPP10DBL3_MODE; + } else if (index % 4 == 2) { + reg = MAX96724_BACKTOP31(index); + mask = MAX96724_BACKTOP31_BPP10DBL2; + mode_mask = MAX96724_BACKTOP31_BPP10DBL2_MODE; + } else if (index % 4 == 1) { + reg = MAX96724_BACKTOP32(index); + mask = MAX96724_BACKTOP32_BPP10DBL1; + mode_mask = MAX96724_BACKTOP32_BPP10DBL1_MODE; + } else { + reg = MAX96724_BACKTOP32(index); + mask = MAX96724_BACKTOP32_BPP10DBL0; + mode_mask = MAX96724_BACKTOP32_BPP10DBL0_MODE; + } + + ret = regmap_assign_bits(priv->regmap, reg, mask, mode->dbl10); + if (ret) + return ret; + + ret = regmap_assign_bits(priv->regmap, reg, mode_mask, mode->dbl10mode); + if (ret) + return ret; + + /* Set 12bit double mode. */ + return regmap_assign_bits(priv->regmap, MAX96724_BACKTOP32(index), + MAX96724_BACKTOP32_BPP12(index), mode->dbl12); +} + +static int max96724_set_pipe_tunnel_enable(struct max_des *des, + struct max_des_pipe *pipe, bool enable) +{ + struct max96724_priv *priv = des_to_priv(des); + + return regmap_assign_bits(priv->regmap, MAX96724_MIPI_TX54(pipe->index), + MAX96724_MIPI_TX54_TUN_EN, enable); +} + +static int max96724_select_links(struct max_des *des, unsigned int mask) +{ + struct max96724_priv *priv = des_to_priv(des); + int ret; + + ret = regmap_update_bits(priv->regmap, MAX96724_REG6, MAX96724_REG6_LINK_EN, + field_prep(MAX96724_REG6_LINK_EN, mask)); + if (ret) + return ret; + + ret = regmap_set_bits(priv->regmap, MAX96724_CTRL1, + MAX96724_CTRL1_RESET_ONESHOT); + if (ret) + return ret; + + msleep(60); + + return 0; +} + +static int max96724_set_link_version(struct max_des *des, + struct max_des_link *link, + enum max_serdes_gmsl_version version) +{ + struct max96724_priv *priv = des_to_priv(des); + unsigned int index = link->index; + unsigned int val; + + if (version == MAX_SERDES_GMSL_2_6GBPS) + val = MAX96724_REG26_RX_RATE_6GBPS; + else + val = MAX96724_REG26_RX_RATE_3GBPS; + + return regmap_update_bits(priv->regmap, MAX96724_REG26(index), + MAX96724_REG26_RX_RATE_PHY(index), + field_prep(MAX96724_REG26_RX_RATE_PHY(index), val)); +} + +static int max96724_set_tpg_timings(struct max96724_priv *priv, + const struct max_serdes_tpg_timings *tm) +{ + const struct reg_sequence regs[] = { + REG_SEQUENCE_3(MAX96724_VS_DLY_2, tm->vs_dly), + REG_SEQUENCE_3(MAX96724_VS_HIGH_2, tm->vs_high), + REG_SEQUENCE_3(MAX96724_VS_LOW_2, tm->vs_low), + REG_SEQUENCE_3(MAX96724_V2H_2, tm->v2h), + REG_SEQUENCE_2(MAX96724_HS_HIGH_1, tm->hs_high), + REG_SEQUENCE_2(MAX96724_HS_LOW_1, tm->hs_low), + REG_SEQUENCE_2(MAX96724_HS_CNT_1, tm->hs_cnt), + REG_SEQUENCE_3(MAX96724_V2D_2, tm->v2d), + REG_SEQUENCE_2(MAX96724_DE_HIGH_1, tm->de_high), + REG_SEQUENCE_2(MAX96724_DE_LOW_1, tm->de_low), + REG_SEQUENCE_2(MAX96724_DE_CNT_1, tm->de_cnt), + }; + int ret; + + ret = regmap_multi_reg_write(priv->regmap, regs, ARRAY_SIZE(regs)); + if (ret) + return ret; + + return regmap_write(priv->regmap, MAX96724_PATGEN_0, + FIELD_PREP(MAX96724_PATGEN_0_VTG_MODE, + MAX96724_PATGEN_0_VTG_MODE_FREE_RUNNING) | + FIELD_PREP(MAX96724_PATGEN_0_DE_INV, tm->de_inv) | + FIELD_PREP(MAX96724_PATGEN_0_HS_INV, tm->hs_inv) | + FIELD_PREP(MAX96724_PATGEN_0_VS_INV, tm->vs_inv) | + FIELD_PREP(MAX96724_PATGEN_0_GEN_DE, tm->gen_de) | + FIELD_PREP(MAX96724_PATGEN_0_GEN_HS, tm->gen_hs) | + FIELD_PREP(MAX96724_PATGEN_0_GEN_VS, tm->gen_vs)); +} + +static int max96724_set_tpg_clk(struct max96724_priv *priv, u32 clock) +{ + bool patgen_clk_src = 0; + u8 pclk_src; + int ret; + + switch (clock) { + case 25000000: + pclk_src = MAX96724_DEBUG_EXTRA_PCLK_SRC_25MHZ; + break; + case 75000000: + pclk_src = MAX96724_DEBUG_EXTRA_PCLK_SRC_75MHZ; + break; + case 150000000: + pclk_src = MAX96724_DEBUG_EXTRA_PCLK_SRC_USE_PIPE; + patgen_clk_src = MAX96724_VPRBS_PATGEN_CLK_SRC_150MHZ; + break; + case 375000000: + pclk_src = MAX96724_DEBUG_EXTRA_PCLK_SRC_USE_PIPE; + patgen_clk_src = MAX96724_VPRBS_PATGEN_CLK_SRC_375MHZ; + break; + case 0: + return 0; + default: + return -EINVAL; + } + + /* + * TPG data is always injected on link 0, which is always routed to + * pipe 0. + */ + ret = regmap_update_bits(priv->regmap, MAX96724_VPRBS(0), + MAX96724_VPRBS_PATGEN_CLK_SRC, + FIELD_PREP(MAX96724_VPRBS_PATGEN_CLK_SRC, + patgen_clk_src)); + if (ret) + return ret; + + return regmap_update_bits(priv->regmap, MAX96724_DEBUG_EXTRA, + MAX96724_DEBUG_EXTRA_PCLK_SRC, + FIELD_PREP(MAX96724_DEBUG_EXTRA_PCLK_SRC, + pclk_src)); +} + +static int max96724_set_tpg_mode(struct max96724_priv *priv, bool enable) +{ + unsigned int patgen_mode; + + switch (priv->des.tpg_pattern) { + case MAX_SERDES_TPG_PATTERN_GRADIENT: + patgen_mode = MAX96724_PATGEN_1_PATGEN_MODE_GRADIENT; + break; + case MAX_SERDES_TPG_PATTERN_CHECKERBOARD: + patgen_mode = MAX96724_PATGEN_1_PATGEN_MODE_CHECKER; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(priv->regmap, MAX96724_PATGEN_1, + MAX96724_PATGEN_1_PATGEN_MODE, + FIELD_PREP(MAX96724_PATGEN_1_PATGEN_MODE, + enable ? patgen_mode + : MAX96724_PATGEN_1_PATGEN_MODE_DISABLED)); +} + +static int max96724_set_tpg(struct max_des *des, + const struct max_serdes_tpg_entry *entry) +{ + struct max96724_priv *priv = des_to_priv(des); + struct max_serdes_tpg_timings timings = { 0 }; + int ret; + + ret = max_serdes_get_tpg_timings(entry, &timings); + if (ret) + return ret; + + ret = max96724_set_tpg_timings(priv, &timings); + if (ret) + return ret; + + ret = max96724_set_tpg_clk(priv, timings.clock); + if (ret) + return ret; + + ret = max96724_set_tpg_mode(priv, entry); + if (ret) + return ret; + + return regmap_assign_bits(priv->regmap, MAX96724_MIPI_PHY0, + MAX96724_MIPI_PHY0_FORCE_CSI_OUT_EN, !!entry); +} + +static const struct max_serdes_tpg_entry max96724_tpg_entries[] = { + MAX_TPG_ENTRY_640X480P60_RGB888, + MAX_TPG_ENTRY_1920X1080P30_RGB888, + MAX_TPG_ENTRY_1920X1080P60_RGB888, +}; + +static const struct max_des_ops max96724_ops = { + .num_phys = 4, + .num_links = 4, + .num_remaps_per_pipe = 16, + .phys_configs = { + .num_configs = ARRAY_SIZE(max96724_phys_configs), + .configs = max96724_phys_configs, + }, + .tpg_entries = { + .num_entries = ARRAY_SIZE(max96724_tpg_entries), + .entries = max96724_tpg_entries, + }, + .tpg_mode = MAX_SERDES_GMSL_PIXEL_MODE, + .tpg_patterns = BIT(MAX_SERDES_TPG_PATTERN_CHECKERBOARD) | + BIT(MAX_SERDES_TPG_PATTERN_GRADIENT), + .use_atr = true, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .reg_read = max96724_reg_read, + .reg_write = max96724_reg_write, +#endif + .log_pipe_status = max96724_log_pipe_status, + .log_phy_status = max96724_log_phy_status, + .set_enable = max96724_set_enable, + .init = max96724_init, + .init_phy = max96724_init_phy, + .set_phy_mode = max96724_set_phy_mode, + .set_phy_enable = max96724_set_phy_enable, + .set_pipe_stream_id = max96724_set_pipe_stream_id, + .set_pipe_link = max96724_set_pipe_link, + .set_pipe_enable = max96724_set_pipe_enable, + .set_pipe_remap = max96724_set_pipe_remap, + .set_pipe_remaps_enable = max96724_set_pipe_remaps_enable, + .set_pipe_mode = max96724_set_pipe_mode, + .set_tpg = max96724_set_tpg, + .select_links = max96724_select_links, + .set_link_version = max96724_set_link_version, +}; + +static const struct max96724_chip_info max96724_info = { + .versions = BIT(MAX_SERDES_GMSL_2_3GBPS) | + BIT(MAX_SERDES_GMSL_2_6GBPS), + .modes = BIT(MAX_SERDES_GMSL_PIXEL_MODE) | + BIT(MAX_SERDES_GMSL_TUNNEL_MODE), + .set_pipe_tunnel_enable = max96724_set_pipe_tunnel_enable, + .set_pipe_phy = max96724_set_pipe_phy, + .set_pipe_tunnel_phy = max96724_set_pipe_tunnel_phy, + .supports_pipe_stream_autoselect = true, + .num_pipes = 4, +}; + +static const struct max96724_chip_info max96724f_info = { + .versions = BIT(MAX_SERDES_GMSL_2_3GBPS), + .modes = BIT(MAX_SERDES_GMSL_PIXEL_MODE) | + BIT(MAX_SERDES_GMSL_TUNNEL_MODE), + .set_pipe_tunnel_enable = max96724_set_pipe_tunnel_enable, + .set_pipe_phy = max96724_set_pipe_phy, + .set_pipe_tunnel_phy = max96724_set_pipe_tunnel_phy, + .supports_pipe_stream_autoselect = true, + .num_pipes = 4, +}; + +static const struct max96724_chip_info max96712_info = { + .versions = BIT(MAX_SERDES_GMSL_2_3GBPS) | + BIT(MAX_SERDES_GMSL_2_6GBPS), + .modes = BIT(MAX_SERDES_GMSL_PIXEL_MODE), + .num_pipes = 8, +}; + +static int max96724_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct max96724_priv *priv; + struct max_des_ops *ops; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL); + if (!ops) + return -ENOMEM; + + priv->info = device_get_match_data(dev); + if (!priv->info) { + dev_err(dev, "Failed to get match data\n"); + return -ENODEV; + } + + priv->dev = dev; + priv->client = client; + i2c_set_clientdata(client, priv); + + priv->regmap = devm_regmap_init_i2c(client, &max96724_i2c_regmap); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + priv->gpiod_enable = devm_gpiod_get_optional(&client->dev, "enable", + GPIOD_OUT_LOW); + if (IS_ERR(priv->gpiod_enable)) + return PTR_ERR(priv->gpiod_enable); + + if (priv->gpiod_enable) { + /* PWDN must be held for 1us for reset */ + udelay(1); + + gpiod_set_value_cansleep(priv->gpiod_enable, 1); + + /* Maximum power-up time (tLOCK) 4ms */ + usleep_range(4000, 5000); + } + + *ops = max96724_ops; + ops->versions = priv->info->versions; + ops->modes = priv->info->modes; + ops->num_pipes = priv->info->num_pipes; + ops->set_pipe_tunnel_enable = priv->info->set_pipe_tunnel_enable; + ops->set_pipe_phy = priv->info->set_pipe_phy; + ops->set_pipe_tunnel_phy = priv->info->set_pipe_tunnel_phy; + priv->des.ops = ops; + + ret = max96724_reset(priv); + if (ret) + return ret; + + return max_des_probe(client, &priv->des); +} + +static void max96724_remove(struct i2c_client *client) +{ + struct max96724_priv *priv = i2c_get_clientdata(client); + + max_des_remove(&priv->des); + + gpiod_set_value_cansleep(priv->gpiod_enable, 0); +} + +static const struct acpi_device_id max96724_acpi_ids[] = { + { "INTC1139", .driver_data = (kernel_ulong_t)&max96724_info}, + {} +}; +MODULE_DEVICE_TABLE(acpi, max96724_acpi_ids); + +static const struct of_device_id max96724_of_table[] = { + { .compatible = "maxim,max96712", .data = &max96712_info }, + { .compatible = "maxim,max96724", .data = &max96724_info }, + { .compatible = "maxim,max96724f", .data = &max96724f_info }, + { .compatible = "maxim,max96724r", .data = &max96724f_info }, + { }, +}; +MODULE_DEVICE_TABLE(of, max96724_of_table); + +static struct i2c_driver max96724_i2c_driver = { + .driver = { + .name = "max96724", + .of_match_table = max96724_of_table, + .acpi_match_table = max96724_acpi_ids, + }, + .probe = max96724_probe, + .remove = max96724_remove, +}; + +module_i2c_driver(max96724_i2c_driver); + +MODULE_IMPORT_NS("MAX_SERDES"); +MODULE_DESCRIPTION("Maxim MAX96724 Quad GMSL2 Deserializer Driver"); +MODULE_AUTHOR("Cosmin Tanislav "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/maxim-serdes/max_des.c b/drivers/media/i2c/maxim-serdes/max_des.c new file mode 100644 index 00000000000000..ecb6c66e766022 --- /dev/null +++ b/drivers/media/i2c/maxim-serdes/max_des.c @@ -0,0 +1,3184 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Maxim GMSL2 Deserializer Driver + * + * Copyright (C) 2025 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "max_des.h" +#include "max_ser.h" +#include "max_serdes.h" + +#define MAX_DES_LINK_FREQUENCY_MIN 100000000ull +#define MAX_DES_LINK_FREQUENCY_DEFAULT 750000000ull +#define MAX_DES_LINK_FREQUENCY_MAX 1250000000ull + +#define MAX_DES_NUM_PHYS 4 +#define MAX_DES_NUM_LINKS 4 +#define MAX_DES_NUM_PIPES 8 + +struct max_des_priv { + struct max_des *des; + + struct device *dev; + struct i2c_client *client; + struct i2c_atr *atr; + struct i2c_mux_core *mux; + + struct media_pad *pads; + struct regulator **pocs; + struct max_serdes_source *sources; + u64 *streams_masks; + + struct notifier_block i2c_nb; + struct v4l2_subdev sd; + struct v4l2_async_notifier nf; + struct v4l2_ctrl_handler ctrl_handler; + + struct max_des_phy *unused_phy; +}; + +struct max_des_remap_context { + enum max_serdes_gmsl_mode mode; + /* Mark whether TPG is enabled */ + bool tpg; + /* Mark the PHYs to which each pipe is mapped. */ + unsigned long pipe_phy_masks[MAX_DES_NUM_PIPES]; + /* Mark the pipes in use. */ + bool pipe_in_use[MAX_DES_NUM_PIPES]; + /* Mark whether pipe has remapped VC ids. */ + bool vc_ids_remapped[MAX_DES_NUM_PIPES]; + /* Map between pipe VC ids and PHY VC ids. */ + unsigned int vc_ids_map[MAX_DES_NUM_PIPES][MAX_DES_NUM_PHYS][MAX_SERDES_VC_ID_NUM]; + /* Mark whether a pipe VC id has been mapped to a PHY VC id. */ + unsigned long vc_ids_masks[MAX_DES_NUM_PIPES][MAX_DES_NUM_PHYS]; + /* Mark whether a PHY VC id has been mapped. */ + unsigned long dst_vc_ids_masks[MAX_DES_NUM_PHYS]; +}; + +struct max_des_mode_context { + bool phys_bpp8_shared_with_16[MAX_DES_NUM_PHYS]; + bool pipes_bpp8_shared_with_16[MAX_DES_NUM_PIPES]; + u32 phys_double_bpps[MAX_DES_NUM_PHYS]; + u32 pipes_double_bpps[MAX_DES_NUM_PIPES]; +}; + +struct max_des_route_hw { + struct max_serdes_source *source; + struct max_des_pipe *pipe; + struct max_des_phy *phy; + struct v4l2_mbus_frame_desc_entry entry; + bool is_tpg; +}; + +struct max_des_link_hw { + struct max_serdes_source *source; + struct max_des_link *link; + struct max_des_pipe *pipe; +}; + +static inline struct max_des_priv *sd_to_priv(struct v4l2_subdev *sd) +{ + return container_of(sd, struct max_des_priv, sd); +} + +static inline struct max_des_priv *nf_to_priv(struct v4l2_async_notifier *nf) +{ + return container_of(nf, struct max_des_priv, nf); +} + +static inline struct max_des_priv *ctrl_to_priv(struct v4l2_ctrl_handler *handler) +{ + return container_of(handler, struct max_des_priv, ctrl_handler); +} + +static inline bool max_des_pad_is_sink(struct max_des *des, u32 pad) +{ + return pad < des->ops->num_links; +} + +static inline bool max_des_pad_is_source(struct max_des *des, u32 pad) +{ + return pad >= des->ops->num_links && + pad < des->ops->num_links + des->ops->num_phys; +} + +static inline bool max_des_pad_is_tpg(struct max_des *des, u32 pad) +{ + return pad == des->ops->num_links + des->ops->num_phys; +} + +static inline unsigned int max_des_link_to_pad(struct max_des *des, + struct max_des_link *link) +{ + return link->index; +} + +static inline unsigned int max_des_phy_to_pad(struct max_des *des, + struct max_des_phy *phy) +{ + return phy->index + des->ops->num_links; +} + +static inline unsigned int max_des_num_pads(struct max_des *des) +{ + return des->ops->num_links + des->ops->num_phys + + (des->ops->set_tpg ? 1 : 0); +} + +static struct max_des_phy *max_des_pad_to_phy(struct max_des *des, u32 pad) +{ + if (!max_des_pad_is_source(des, pad)) + return NULL; + + return &des->phys[pad - des->ops->num_links]; +} + +static struct max_des_link *max_des_pad_to_link(struct max_des *des, u32 pad) +{ + if (!max_des_pad_is_sink(des, pad)) + return NULL; + + return &des->links[pad]; +} + +static struct max_des_pipe * +max_des_find_link_pipe(struct max_des *des, struct max_des_link *link) +{ + unsigned int i; + + for (i = 0; i < des->ops->num_pipes; i++) { + struct max_des_pipe *pipe = &des->pipes[i]; + + if (pipe->link_id == link->index) + return pipe; + } + + return NULL; +} + +static struct max_serdes_source * +max_des_get_link_source(struct max_des_priv *priv, struct max_des_link *link) +{ + return &priv->sources[link->index]; +} + +static const struct max_serdes_tpg_entry * +max_des_find_tpg_entry(struct max_des *des, u32 target_index, + u32 width, u32 height, u32 code, + u32 numerator, u32 denominator) +{ + const struct max_serdes_tpg_entry *entry; + unsigned int index = 0; + unsigned int i; + + for (i = 0; i < des->ops->tpg_entries.num_entries; i++) { + entry = &des->ops->tpg_entries.entries[i]; + + if ((width != 0 && width != entry->width) || + (height != 0 && height != entry->height) || + (code != 0 && code != entry->code) || + (numerator != 0 && numerator != entry->interval.numerator) || + (denominator != 0 && denominator != entry->interval.denominator)) + continue; + + if (index == target_index) + break; + + index++; + } + + if (i == des->ops->tpg_entries.num_entries) + return NULL; + + return &des->ops->tpg_entries.entries[i]; +} + +static const struct max_serdes_tpg_entry * +max_des_find_state_tpg_entry(struct max_des *des, struct v4l2_subdev_state *state, + unsigned int pad) +{ + struct v4l2_mbus_framefmt *fmt; + struct v4l2_fract *in; + + fmt = v4l2_subdev_state_get_format(state, pad, MAX_SERDES_TPG_STREAM); + if (!fmt) + return NULL; + + in = v4l2_subdev_state_get_interval(state, pad, MAX_SERDES_TPG_STREAM); + if (!in) + return NULL; + + return max_des_find_tpg_entry(des, 0, fmt->width, fmt->height, fmt->code, + in->numerator, in->denominator); +} + +static int max_des_get_tpg_fd_entry_state(struct max_des *des, + struct v4l2_subdev_state *state, + struct v4l2_mbus_frame_desc_entry *fd_entry, + unsigned int pad) +{ + const struct max_serdes_tpg_entry *entry; + + entry = max_des_find_state_tpg_entry(des, state, pad); + if (!entry) + return -EINVAL; + + fd_entry->stream = MAX_SERDES_TPG_STREAM; + fd_entry->flags = V4L2_MBUS_FRAME_DESC_FL_LEN_MAX; + fd_entry->length = entry->width * entry->height * entry->bpp / 8; + fd_entry->pixelcode = entry->code; + fd_entry->bus.csi2.vc = 0; + fd_entry->bus.csi2.dt = entry->dt; + + return 0; +} + +static int max_des_tpg_route_to_hw(struct max_des_priv *priv, + struct v4l2_subdev_state *state, + struct v4l2_subdev_route *route, + struct max_des_route_hw *hw) +{ + struct max_des *des = priv->des; + + /* TPG injects its data into all pipes, but use pipe 0 for simplicity. */ + hw->pipe = &des->pipes[0]; + + hw->phy = max_des_pad_to_phy(des, route->source_pad); + if (!hw->phy) + return -ENOENT; + + return max_des_get_tpg_fd_entry_state(des, state, &hw->entry, + route->sink_pad); +} + +static int max_des_route_to_hw(struct max_des_priv *priv, + struct v4l2_subdev_state *state, + struct v4l2_subdev_route *route, + struct max_des_route_hw *hw) +{ + struct max_des *des = priv->des; + struct v4l2_mbus_frame_desc fd; + struct max_des_link *link; + unsigned int i; + int ret; + + memset(hw, 0, sizeof(*hw)); + + hw->is_tpg = max_des_pad_is_tpg(des, route->sink_pad); + if (hw->is_tpg) + return max_des_tpg_route_to_hw(priv, state, route, hw); + + link = max_des_pad_to_link(des, route->sink_pad); + if (!link) + return -ENOENT; + + hw->phy = max_des_pad_to_phy(des, route->source_pad); + if (!hw->phy) + return -ENOENT; + + hw->pipe = max_des_find_link_pipe(des, link); + if (!hw->pipe) + return -ENOENT; + + hw->source = max_des_get_link_source(priv, link); + if (!hw->source->sd) + return 0; + + ret = v4l2_subdev_call(hw->source->sd, pad, get_frame_desc, + hw->source->pad, &fd); + if (ret) + return ret; + + for (i = 0; i < fd.num_entries; i++) + if (fd.entry[i].stream == route->sink_stream) + break; + + if (i == fd.num_entries) + return -ENOENT; + + hw->entry = fd.entry[i]; + + return 0; +} + +static int max_des_link_to_hw(struct max_des_priv *priv, + struct max_des_link *link, + struct max_des_link_hw *hw) +{ + struct max_des *des = priv->des; + + memset(hw, 0, sizeof(*hw)); + + hw->link = link; + + hw->pipe = max_des_find_link_pipe(des, hw->link); + if (!hw->pipe) + return -ENOENT; + + hw->source = max_des_get_link_source(priv, hw->link); + + return 0; +} + +static int max_des_link_index_to_hw(struct max_des_priv *priv, unsigned int i, + struct max_des_link_hw *hw) +{ + return max_des_link_to_hw(priv, &priv->des->links[i], hw); +} + +static int max_des_set_pipe_remaps(struct max_des_priv *priv, + struct max_des_pipe *pipe, + struct max_des_remap *remaps, + unsigned int num_remaps) +{ + struct max_des *des = priv->des; + unsigned int mask = 0; + unsigned int i; + int ret; + + if (!des->ops->set_pipe_remap) + return 0; + + for (i = 0; i < num_remaps; i++) { + ret = des->ops->set_pipe_remap(des, pipe, i, &remaps[i]); + if (ret) + return ret; + + mask |= BIT(i); + } + + return des->ops->set_pipe_remaps_enable(des, pipe, mask); +} + +static int max_des_set_pipe_vc_remaps(struct max_des_priv *priv, + struct max_des_pipe *pipe, + struct max_serdes_vc_remap *vc_remaps, + unsigned int num_vc_remaps) +{ + struct max_des *des = priv->des; + unsigned int mask = 0; + unsigned int i; + int ret; + + for (i = 0; i < num_vc_remaps; i++) { + ret = des->ops->set_pipe_vc_remap(des, pipe, i, &vc_remaps[i]); + if (ret) + return ret; + + mask |= BIT(i); + } + + return des->ops->set_pipe_vc_remaps_enable(des, pipe, mask); +} + +static int max_des_map_src_dst_vc_id(struct max_des_remap_context *context, + unsigned int pipe_id, unsigned int phy_id, + unsigned int src_vc_id, bool keep_vc) +{ + unsigned int vc_id; + + if (src_vc_id >= MAX_SERDES_VC_ID_NUM) + return -E2BIG; + + if (context->vc_ids_masks[pipe_id][phy_id] & BIT(src_vc_id)) + return 0; + + if (keep_vc && !(context->dst_vc_ids_masks[phy_id] & BIT(src_vc_id))) + vc_id = src_vc_id; + else + vc_id = ffz(context->dst_vc_ids_masks[phy_id]); + + if (vc_id != src_vc_id) + context->vc_ids_remapped[pipe_id] = true; + + if (vc_id >= MAX_SERDES_VC_ID_NUM) + return -E2BIG; + + context->pipe_phy_masks[pipe_id] |= BIT(phy_id); + context->dst_vc_ids_masks[phy_id] |= BIT(vc_id); + + context->vc_ids_map[pipe_id][phy_id][src_vc_id] = vc_id; + context->vc_ids_masks[pipe_id][phy_id] |= BIT(src_vc_id); + + return 0; +} + +static int max_des_get_src_dst_vc_id(struct max_des_remap_context *context, + unsigned int pipe_id, unsigned int phy_id, + unsigned int src_vc_id, unsigned int *dst_vc_id) +{ + if (!(context->vc_ids_masks[pipe_id][phy_id] & BIT(src_vc_id))) + return -ENOENT; + + *dst_vc_id = context->vc_ids_map[pipe_id][phy_id][src_vc_id]; + + return 0; +} + +static int max_des_populate_remap_usage(struct max_des_priv *priv, + struct max_des_remap_context *context, + struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_route *route; + int ret; + + for_each_active_route(&state->routing, route) { + struct max_des_route_hw hw; + + ret = max_des_route_to_hw(priv, state, route, &hw); + if (ret) + return ret; + + if (hw.is_tpg) + context->tpg = true; + + context->pipe_in_use[hw.pipe->index] = true; + } + + return 0; +} + +static int max_des_get_supported_modes(struct max_des_priv *priv, + struct max_des_remap_context *context, + unsigned int *modes) +{ + struct max_des *des = priv->des; + unsigned int i; + int ret; + + *modes = des->ops->modes; + + if (context->tpg) + *modes = BIT(des->ops->tpg_mode); + + for (i = 0; i < des->ops->num_links; i++) { + struct max_des_link_hw hw; + + ret = max_des_link_index_to_hw(priv, i, &hw); + if (ret) + return ret; + + if (!hw.link->enabled) + continue; + + if (!hw.source->sd) + continue; + + if (!context->pipe_in_use[hw.pipe->index]) + continue; + + *modes &= max_ser_get_supported_modes(hw.source->sd); + } + + /* + * Serializers need to all be in the same mode because of hardware + * issues when running them in mixed modes. + */ + if (!*modes) + return -EINVAL; + + return 0; +} + +static int max_des_populate_remap_context_mode(struct max_des_priv *priv, + struct max_des_remap_context *context, + unsigned int modes) +{ + struct max_des *des = priv->des; + unsigned int i; + int ret; + + context->mode = MAX_SERDES_GMSL_PIXEL_MODE; + + /* + * If pixel mode is the only supported mode, do not try to see if + * tunnel mode can be used. + */ + if (modes == BIT(MAX_SERDES_GMSL_PIXEL_MODE)) + return 0; + + for (i = 0; i < des->ops->num_links; i++) { + struct max_des_link_hw hw; + + ret = max_des_link_index_to_hw(priv, i, &hw); + if (ret) + return ret; + + if (!hw.link->enabled) + continue; + + if (!hw.source->sd) + continue; + + if (!context->pipe_in_use[hw.pipe->index]) + continue; + + if (hweight_long(context->pipe_phy_masks[hw.pipe->index]) == 1 && + (!context->vc_ids_remapped[hw.pipe->index] || + max_ser_supports_vc_remap(hw.source->sd) || + des->ops->set_pipe_vc_remap)) + continue; + + return 0; + } + + context->mode = MAX_SERDES_GMSL_TUNNEL_MODE; + + return 0; +} + +static int max_des_should_keep_vc(struct max_des_priv *priv, + struct max_des_route_hw *hw, + unsigned int modes) +{ + struct max_des *des = priv->des; + + /* Pixel mode deserializers always have the ability to remap VCs. */ + if (modes == BIT(MAX_SERDES_GMSL_PIXEL_MODE)) + return false; + + if (des->ops->set_pipe_vc_remap) + return false; + + if (!hw->is_tpg && hw->source && hw->source->sd && + max_ser_supports_vc_remap(hw->source->sd)) + return false; + + return true; +} + +static int max_des_populate_remap_context(struct max_des_priv *priv, + struct max_des_remap_context *context, + struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_route *route; + unsigned int modes; + int ret; + + ret = max_des_populate_remap_usage(priv, context, state); + if (ret) + return ret; + + ret = max_des_get_supported_modes(priv, context, &modes); + if (ret) + return ret; + + for_each_active_route(&state->routing, route) { + struct max_des_route_hw hw; + bool keep_vc; + + ret = max_des_route_to_hw(priv, state, route, &hw); + if (ret) + return ret; + + keep_vc = max_des_should_keep_vc(priv, &hw, modes); + + ret = max_des_map_src_dst_vc_id(context, hw.pipe->index, hw.phy->index, + hw.entry.bus.csi2.vc, keep_vc); + if (ret) + return ret; + } + + return max_des_populate_remap_context_mode(priv, context, modes); +} + +static int max_des_populate_mode_context(struct max_des_priv *priv, + struct max_des_mode_context *context, + struct v4l2_subdev_state *state, + enum max_serdes_gmsl_mode mode) +{ + bool bpp8_not_shared_with_16_phys[MAX_DES_NUM_PHYS] = { 0 }; + u32 undoubled_bpps_phys[MAX_DES_NUM_PHYS] = { 0 }; + u32 bpps_pipes[MAX_DES_NUM_PIPES] = { 0 }; + struct max_des *des = priv->des; + struct v4l2_subdev_route *route; + unsigned int i; + int ret; + + if (mode != MAX_SERDES_GMSL_PIXEL_MODE) + return 0; + + /* + * Go over all streams and gather the bpps for all pipes. + * + * Then, go over all the streams again and check if the + * current stream is doubled. + * + * If the current stream is doubled, add it to a doubled mask for both + * the pipe and the PHY. + * + * If the current stream is not doubled, add it to a local undoubled + * mask for the PHY. + * + * Also, track whether an 8bpp stream is shared with any bpp > 8 on both + * the PHYs and the pipes, since that needs to be special cased. + * + * After going over all the streams, remove the undoubled streams from + * the doubled ones. Doubled and undoubled streams cannot be streamed + * over the same PHY. + * + * Then, do a second pass to remove the undoubled streams from the pipes. + * + * This operation cannot be done in a single pass because any pipe might + * generate an undoubled stream for a specific bpp, causing already + * processed pipes to need to have their doubled bpps updated. + */ + + for_each_active_route(&state->routing, route) { + struct max_des_route_hw hw; + unsigned int bpp; + + ret = max_des_route_to_hw(priv, state, route, &hw); + if (ret) + return ret; + + ret = max_serdes_get_fd_bpp(&hw.entry, &bpp); + if (ret) + return ret; + + bpps_pipes[hw.pipe->index] |= BIT(bpp); + } + + for_each_active_route(&state->routing, route) { + unsigned int bpp, min_bpp, max_bpp, doubled_bpp; + unsigned int pipe_id, phy_id; + struct max_des_route_hw hw; + u32 sink_bpps; + + ret = max_des_route_to_hw(priv, state, route, &hw); + if (ret) + return ret; + + ret = max_serdes_get_fd_bpp(&hw.entry, &bpp); + if (ret) + return ret; + + sink_bpps = bpps_pipes[hw.pipe->index]; + + ret = max_serdes_process_bpps(priv->dev, sink_bpps, ~0U, &doubled_bpp); + if (ret) + return ret; + + min_bpp = __ffs(sink_bpps); + max_bpp = __fls(sink_bpps); + pipe_id = hw.pipe->index; + phy_id = hw.phy->index; + + if (bpp == doubled_bpp) { + context->phys_double_bpps[phy_id] |= BIT(bpp); + context->pipes_double_bpps[pipe_id] |= BIT(bpp); + } else { + undoubled_bpps_phys[phy_id] |= BIT(bpp); + } + + if (min_bpp == 8 && max_bpp > 8) { + context->phys_bpp8_shared_with_16[phy_id] = true; + context->pipes_bpp8_shared_with_16[pipe_id] = true; + } else if (min_bpp == 8 && max_bpp == 8) { + bpp8_not_shared_with_16_phys[phy_id] = true; + } + } + + for (i = 0; i < des->ops->num_phys; i++) { + if (context->phys_bpp8_shared_with_16[i] && bpp8_not_shared_with_16_phys[i]) { + dev_err(priv->dev, + "Cannot stream 8bpp coming from pipes padded to 16bpp " + "and pipes not padded to 16bpp on the same PHY\n"); + return -EINVAL; + } + } + + for (i = 0; i < des->ops->num_phys; i++) + context->phys_double_bpps[i] &= ~undoubled_bpps_phys[i]; + + for_each_active_route(&state->routing, route) { + struct max_des_route_hw hw; + + ret = max_des_route_to_hw(priv, state, route, &hw); + if (ret) + return ret; + + context->pipes_double_bpps[hw.pipe->index] &= + context->phys_double_bpps[hw.phy->index]; + } + + return 0; +} + +static int max_des_add_vc_remap(struct max_des *des, struct max_serdes_vc_remap *vc_remaps, + unsigned int *num_vc_remaps, unsigned int src_vc_id, + unsigned int dst_vc_id) +{ + struct max_serdes_vc_remap *vc_remap; + unsigned int i; + + for (i = 0; i < *num_vc_remaps; i++) { + vc_remap = &vc_remaps[i]; + + if (vc_remap->src == src_vc_id && vc_remap->dst == dst_vc_id) + return 0; + } + + if (*num_vc_remaps == MAX_SERDES_VC_ID_NUM) + return -E2BIG; + + vc_remaps[*num_vc_remaps].src = src_vc_id; + vc_remaps[*num_vc_remaps].dst = dst_vc_id; + + (*num_vc_remaps)++; + + return 0; +} + +static int max_des_get_pipe_vc_remaps(struct max_des_priv *priv, + struct max_des_remap_context *context, + struct max_des_pipe *pipe, + struct max_serdes_vc_remap *vc_remaps, + unsigned int *num_vc_remaps, + struct v4l2_subdev_state *state, + u64 *streams_masks, bool with_tpg) +{ + struct max_des *des = priv->des; + struct v4l2_subdev_route *route; + int ret; + + *num_vc_remaps = 0; + + if (context->mode != MAX_SERDES_GMSL_TUNNEL_MODE) + return 0; + + for_each_active_route(&state->routing, route) { + unsigned int src_vc_id, dst_vc_id; + struct max_des_route_hw hw; + + if (!(BIT_ULL(route->sink_stream) & streams_masks[route->sink_pad])) + continue; + + ret = max_des_route_to_hw(priv, state, route, &hw); + if (ret) + return ret; + + if (!with_tpg && hw.is_tpg) + continue; + + if (hw.pipe != pipe) + continue; + + src_vc_id = hw.entry.bus.csi2.vc; + + ret = max_des_get_src_dst_vc_id(context, pipe->index, hw.phy->index, + src_vc_id, &dst_vc_id); + if (ret) + return ret; + + ret = max_des_add_vc_remap(des, vc_remaps, num_vc_remaps, + src_vc_id, dst_vc_id); + if (ret) + return ret; + } + + return 0; +} + +static void max_des_get_pipe_mode(struct max_des_mode_context *context, + struct max_des_pipe *pipe, + struct max_des_pipe_mode *mode) +{ + u32 double_bpps = context->pipes_double_bpps[pipe->index]; + + if ((double_bpps & BIT(8)) && + !context->pipes_bpp8_shared_with_16[pipe->index]) { + mode->dbl8 = true; + mode->dbl8mode = true; + } +} + +static void max_des_get_phy_mode(struct max_des_mode_context *context, + struct max_des_phy *phy, + struct max_des_phy_mode *mode) +{ + bool bpp8_pipe_shared_with_16 = context->phys_bpp8_shared_with_16[phy->index]; + u32 double_bpps = context->phys_double_bpps[phy->index]; + + if (BIT(8) & double_bpps) { + if (bpp8_pipe_shared_with_16) + mode->alt2_mem_map8 = true; + else + mode->alt_mem_map8 = true; + } + + if (BIT(10) & double_bpps) + mode->alt_mem_map10 = true; + + if (BIT(12) & double_bpps) + mode->alt_mem_map12 = true; +} + +static int max_des_set_modes(struct max_des_priv *priv, + struct max_des_mode_context *context) +{ + struct max_des *des = priv->des; + unsigned int i; + int ret; + + for (i = 0; i < des->ops->num_phys; i++) { + struct max_des_phy *phy = &des->phys[i]; + struct max_des_phy_mode mode = { 0 }; + + max_des_get_phy_mode(context, phy, &mode); + + if (phy->mode.alt_mem_map8 == mode.alt_mem_map8 && + phy->mode.alt_mem_map10 == mode.alt_mem_map10 && + phy->mode.alt_mem_map12 == mode.alt_mem_map12 && + phy->mode.alt2_mem_map8 == mode.alt2_mem_map8) + continue; + + if (des->ops->set_phy_mode) { + ret = des->ops->set_phy_mode(des, phy, &mode); + if (ret) + return ret; + } + + phy->mode = mode; + } + + for (i = 0; i < des->ops->num_pipes; i++) { + struct max_des_pipe *pipe = &des->pipes[i]; + struct max_des_pipe_mode mode = { 0 }; + + max_des_get_pipe_mode(context, pipe, &mode); + + if (pipe->mode.dbl8 == mode.dbl8 && + pipe->mode.dbl10 == mode.dbl10 && + pipe->mode.dbl12 == mode.dbl12 && + pipe->mode.dbl8mode == mode.dbl8mode && + pipe->mode.dbl10mode == mode.dbl10mode) + continue; + + if (des->ops->set_pipe_mode) { + ret = des->ops->set_pipe_mode(des, pipe, &mode); + if (ret) + return ret; + } + + pipe->mode = mode; + } + + for (i = 0; i < des->ops->num_links; i++) { + struct max_des_link_hw hw; + u32 pipe_double_bpps = 0; + + ret = max_des_link_index_to_hw(priv, i, &hw); + if (ret) + return ret; + + if (!hw.link->enabled) + continue; + + if (!hw.source->sd) + continue; + + pipe_double_bpps = context->pipes_double_bpps[hw.pipe->index]; + + ret = max_ser_set_double_bpps(hw.source->sd, pipe_double_bpps); + if (ret) + return ret; + } + + return 0; +} + +static int max_des_set_tunnel(struct max_des_priv *priv, + struct max_des_remap_context *context) +{ + struct max_des *des = priv->des; + unsigned int i; + int ret; + + if (des->ops->set_pipe_tunnel_enable) { + for (i = 0; i < des->ops->num_pipes; i++) { + struct max_des_pipe *pipe = &des->pipes[i]; + bool tunnel_mode = context->mode == MAX_SERDES_GMSL_TUNNEL_MODE; + + ret = des->ops->set_pipe_tunnel_enable(des, pipe, tunnel_mode); + if (ret) + return ret; + } + } + + for (i = 0; i < des->ops->num_links; i++) { + struct max_des_link_hw hw; + + ret = max_des_link_index_to_hw(priv, i, &hw); + if (ret) + return ret; + + if (!hw.link->enabled) + continue; + + if (!hw.source->sd) + continue; + + if (!context->pipe_in_use[hw.pipe->index]) + continue; + + ret = max_ser_set_mode(hw.source->sd, context->mode); + if (ret) + return ret; + } + + des->mode = context->mode; + + return 0; +} + +static int max_des_set_vc_remaps(struct max_des_priv *priv, + struct max_des_remap_context *context, + struct v4l2_subdev_state *state, + u64 *streams_masks) +{ + struct max_des *des = priv->des; + unsigned int i; + int ret; + + if (des->ops->set_pipe_vc_remap) + return 0; + + for (i = 0; i < des->ops->num_links; i++) { + struct max_serdes_vc_remap vc_remaps[MAX_SERDES_VC_ID_NUM]; + struct max_des_link_hw hw; + unsigned int num_vc_remaps; + + ret = max_des_link_index_to_hw(priv, i, &hw); + if (ret) + return ret; + + if (!hw.link->enabled) + continue; + + if (!hw.source->sd) + continue; + + if (!max_ser_supports_vc_remap(hw.source->sd)) + continue; + + ret = max_des_get_pipe_vc_remaps(priv, context, hw.pipe, + vc_remaps, &num_vc_remaps, + state, streams_masks, false); + if (ret) + return ret; + + ret = max_ser_set_vc_remaps(hw.source->sd, vc_remaps, num_vc_remaps); + if (ret) + return ret; + } + + return 0; +} + +static int max_des_set_pipes_stream_id(struct max_des_priv *priv) +{ + bool stream_id_usage[MAX_SERDES_STREAMS_NUM] = { 0 }; + struct max_des *des = priv->des; + unsigned int i; + int ret; + + for (i = 0; i < des->ops->num_links; i++) { + struct max_des_link_hw hw; + unsigned int stream_id; + + ret = max_des_link_index_to_hw(priv, i, &hw); + if (ret) + return ret; + + if (!hw.link->enabled) + continue; + + if (!hw.source->sd) + continue; + + stream_id = hw.pipe->stream_id; + + ret = max_ser_set_stream_id(hw.source->sd, stream_id); + if (ret == -EOPNOTSUPP) { + /* + * Serializer does not support setting the stream id, + * retrieve its hardcoded stream id. + */ + ret = max_ser_get_stream_id(hw.source->sd, &stream_id); + } + + if (ret) + return ret; + + if (stream_id_usage[stream_id] && des->ops->needs_unique_stream_id) { + dev_err(priv->dev, "Duplicate stream id %u\n", stream_id); + return -EINVAL; + } + + ret = des->ops->set_pipe_stream_id(des, hw.pipe, stream_id); + if (ret) + return ret; + + stream_id_usage[stream_id] = true; + hw.pipe->stream_id = stream_id; + } + + return 0; +} + +static int max_des_set_pipes_phy(struct max_des_priv *priv, + struct max_des_remap_context *context) +{ + struct max_des *des = priv->des; + unsigned int i; + int ret; + + if (!des->ops->set_pipe_phy && !des->ops->set_pipe_tunnel_phy) + return 0; + + for (i = 0; i < des->ops->num_pipes; i++) { + struct max_des_pipe *pipe = &des->pipes[i]; + struct max_des_phy *phy; + unsigned int phy_id; + + phy_id = find_first_bit(&context->pipe_phy_masks[pipe->index], + des->ops->num_phys); + + if (priv->unused_phy && + (context->mode != MAX_SERDES_GMSL_TUNNEL_MODE || + phy_id == des->ops->num_phys)) + phy_id = priv->unused_phy->index; + + if (phy_id != des->ops->num_phys) { + phy = &des->phys[phy_id]; + + if (context->mode == MAX_SERDES_GMSL_PIXEL_MODE && + des->ops->set_pipe_phy) + ret = des->ops->set_pipe_phy(des, pipe, phy); + else if (context->mode == MAX_SERDES_GMSL_TUNNEL_MODE && + des->ops->set_pipe_tunnel_phy) + ret = des->ops->set_pipe_tunnel_phy(des, pipe, phy); + else + ret = 0; + + if (ret) + return ret; + } + + pipe->phy_id = phy_id; + } + + return 0; +} + +static int max_des_add_remap(struct max_des *des, struct max_des_remap *remaps, + unsigned int *num_remaps, unsigned int phy_id, + unsigned int src_vc_id, unsigned int dst_vc_id, + unsigned int dt) +{ + struct max_des_remap *remap; + unsigned int i; + + for (i = 0; i < *num_remaps; i++) { + remap = &remaps[i]; + + if (remap->from_dt == dt && remap->to_dt == dt && + remap->from_vc == src_vc_id && remap->to_vc == dst_vc_id && + remap->phy == phy_id) + return 0; + } + + if (*num_remaps == des->ops->num_remaps_per_pipe) + return -E2BIG; + + remap = &remaps[*num_remaps]; + remap->from_dt = dt; + remap->from_vc = src_vc_id; + remap->to_dt = dt; + remap->to_vc = dst_vc_id; + remap->phy = phy_id; + + (*num_remaps)++; + + return 0; +} + +static int max_des_add_remaps(struct max_des *des, struct max_des_remap *remaps, + unsigned int *num_remaps, unsigned int phy_id, + unsigned int src_vc_id, unsigned int dst_vc_id, + unsigned int dt) +{ + int ret; + + ret = max_des_add_remap(des, remaps, num_remaps, phy_id, + src_vc_id, dst_vc_id, dt); + if (ret) + return ret; + + ret = max_des_add_remap(des, remaps, num_remaps, phy_id, + src_vc_id, dst_vc_id, MIPI_CSI2_DT_FS); + if (ret) + return ret; + + ret = max_des_add_remap(des, remaps, num_remaps, phy_id, + src_vc_id, dst_vc_id, MIPI_CSI2_DT_FE); + if (ret) + return ret; + + return 0; +} + +static int max_des_get_pipe_remaps(struct max_des_priv *priv, + struct max_des_remap_context *context, + struct max_des_pipe *pipe, + struct max_des_remap *remaps, + unsigned int *num_remaps, + struct v4l2_subdev_state *state, + u64 *streams_masks) +{ + struct v4l2_mbus_frame_desc_entry tpg_entry = { 0 }; + struct max_des *des = priv->des; + struct v4l2_subdev_route *route; + bool is_tpg_pipe = true; + int ret; + + *num_remaps = 0; + + if (context->mode != MAX_SERDES_GMSL_PIXEL_MODE) + return 0; + + for_each_active_route(&state->routing, route) { + struct max_des_route_hw hw; + unsigned int src_vc_id, dst_vc_id; + + if (!(BIT_ULL(route->sink_stream) & streams_masks[route->sink_pad])) + continue; + + ret = max_des_route_to_hw(priv, state, route, &hw); + if (ret) + return ret; + + if (hw.is_tpg && hw.pipe != pipe) { + is_tpg_pipe = false; + tpg_entry = hw.entry; + } + + if (hw.pipe != pipe) + continue; + + src_vc_id = hw.entry.bus.csi2.vc; + + ret = max_des_get_src_dst_vc_id(context, pipe->index, hw.phy->index, + src_vc_id, &dst_vc_id); + if (ret) + return ret; + + ret = max_des_add_remaps(des, remaps, num_remaps, hw.phy->index, + src_vc_id, dst_vc_id, + hw.entry.bus.csi2.dt); + if (ret) + return ret; + } + + /* + * TPG mode is only handled on pipe 0, but the TPG pollutes other pipes + * with the same data. + * For devices that do not support setting the default PHY of a pipe, + * we want to filter out this data so it does not end up on the wrong + * PHY. + * Devices that support setting the default PHY of a pipe already use it + * to route unused pipes to an unused PHY. + */ + if (context->tpg && !is_tpg_pipe && !des->ops->set_pipe_phy && + priv->unused_phy) { + ret = max_des_add_remaps(des, remaps, num_remaps, + priv->unused_phy->index, + tpg_entry.bus.csi2.vc, + tpg_entry.bus.csi2.vc, + tpg_entry.bus.csi2.dt); + if (ret) + return ret; + } + + return 0; +} + +static int max_des_update_pipe_vc_remaps(struct max_des_priv *priv, + struct max_des_remap_context *context, + struct max_des_pipe *pipe, + struct v4l2_subdev_state *state, + u64 *streams_masks) +{ + struct max_des *des = priv->des; + struct max_serdes_vc_remap *vc_remaps; + unsigned int num_vc_remaps; + int ret; + + if (!des->ops->set_pipe_vc_remap) + return 0; + + vc_remaps = devm_kcalloc(priv->dev, MAX_SERDES_VC_ID_NUM, + sizeof(*vc_remaps), GFP_KERNEL); + if (!vc_remaps) + return -ENOMEM; + + ret = max_des_get_pipe_vc_remaps(priv, context, pipe, vc_remaps, &num_vc_remaps, + state, streams_masks, true); + if (ret) + goto err_free_new_vc_remaps; + + ret = max_des_set_pipe_vc_remaps(priv, pipe, vc_remaps, num_vc_remaps); + if (ret) + goto err_free_new_vc_remaps; + + if (pipe->num_vc_remaps) + devm_kfree(priv->dev, pipe->vc_remaps); + + pipe->vc_remaps = vc_remaps; + pipe->num_vc_remaps = num_vc_remaps; + + return 0; + +err_free_new_vc_remaps: + devm_kfree(priv->dev, vc_remaps); + + return ret; +} + +static int max_des_update_pipe_remaps(struct max_des_priv *priv, + struct max_des_remap_context *context, + struct max_des_pipe *pipe, + struct v4l2_subdev_state *state, + u64 *streams_masks) +{ + struct max_des *des = priv->des; + struct max_des_remap *remaps; + unsigned int num_remaps; + int ret; + + if (!des->ops->set_pipe_remap) + return 0; + + remaps = devm_kcalloc(priv->dev, des->ops->num_remaps_per_pipe, + sizeof(*remaps), GFP_KERNEL); + if (!remaps) + return -ENOMEM; + + ret = max_des_get_pipe_remaps(priv, context, pipe, remaps, &num_remaps, + state, streams_masks); + if (ret) + goto err_free_new_remaps; + + ret = max_des_set_pipe_remaps(priv, pipe, remaps, num_remaps); + if (ret) + goto err_free_new_remaps; + + if (pipe->remaps) + devm_kfree(priv->dev, pipe->remaps); + + pipe->remaps = remaps; + pipe->num_remaps = num_remaps; + + return 0; + +err_free_new_remaps: + devm_kfree(priv->dev, remaps); + + return ret; +} + +static int max_des_update_pipe_enable(struct max_des_priv *priv, + struct max_des_pipe *pipe, + struct v4l2_subdev_state *state, + u64 *streams_masks) +{ + struct max_des *des = priv->des; + struct v4l2_subdev_route *route; + bool enable = false; + int ret; + + for_each_active_route(&state->routing, route) { + struct max_des_route_hw hw; + + if (!(BIT_ULL(route->sink_stream) & streams_masks[route->sink_pad])) + continue; + + ret = max_des_route_to_hw(priv, state, route, &hw); + if (ret) + return ret; + + if (hw.pipe != pipe) + continue; + + enable = true; + break; + } + + if (enable == pipe->enabled) + return 0; + + ret = des->ops->set_pipe_enable(des, pipe, enable); + if (ret) + return ret; + + pipe->enabled = enable; + + return 0; +} + +static int max_des_update_pipe(struct max_des_priv *priv, + struct max_des_remap_context *context, + struct max_des_pipe *pipe, + struct v4l2_subdev_state *state, + u64 *streams_masks) +{ + int ret; + + ret = max_des_update_pipe_remaps(priv, context, pipe, + state, streams_masks); + if (ret) + return ret; + + ret = max_des_update_pipe_vc_remaps(priv, context, pipe, state, + streams_masks); + if (ret) + goto err_revert_update_pipe_remaps; + + ret = max_des_update_pipe_enable(priv, pipe, state, streams_masks); + if (ret) + goto err_revert_update_pipe_vc_remaps; + + return 0; + +err_revert_update_pipe_vc_remaps: + max_des_update_pipe_vc_remaps(priv, context, pipe, state, + priv->streams_masks); + +err_revert_update_pipe_remaps: + max_des_update_pipe_remaps(priv, context, pipe, state, + priv->streams_masks); + + return ret; +} + +static int max_des_init_link_ser_xlate(struct max_des_priv *priv, + struct max_des_link *link, + struct i2c_adapter *adapter, + u8 power_up_addr, u8 new_addr) +{ + struct max_des *des = priv->des; + u8 addrs[] = { power_up_addr, new_addr }; + u8 current_addr; + int ret; + + ret = des->ops->select_links(des, BIT(link->index)); + if (ret) + return ret; + + ret = max_ser_wait_for_multiple(adapter, addrs, ARRAY_SIZE(addrs), + ¤t_addr); + if (ret) { + dev_err(priv->dev, + "Failed to wait for serializer at 0x%02x or 0x%02x: %d\n", + power_up_addr, new_addr, ret); + return ret; + } + + ret = max_ser_reset(adapter, current_addr); + if (ret) { + dev_err(priv->dev, "Failed to reset serializer: %d\n", ret); + return ret; + } + + ret = max_ser_wait(adapter, power_up_addr); + if (ret) { + dev_err(priv->dev, + "Failed to wait for serializer at 0x%02x: %d\n", + power_up_addr, ret); + return ret; + } + + ret = max_ser_change_address(adapter, power_up_addr, new_addr); + if (ret) { + dev_err(priv->dev, + "Failed to change serializer from 0x%02x to 0x%02x: %d\n", + power_up_addr, new_addr, ret); + return ret; + } + + ret = max_ser_wait(adapter, new_addr); + if (ret) { + dev_err(priv->dev, + "Failed to wait for serializer at 0x%02x: %d\n", + new_addr, ret); + return ret; + } + + if (des->ops->fix_tx_ids) { + ret = max_ser_fix_tx_ids(adapter, new_addr); + if (ret) + return ret; + } + + return ret; +} + +static int max_des_init(struct max_des_priv *priv) +{ + struct max_des *des = priv->des; + unsigned int i; + int ret; + + if (des->ops->init) { + ret = des->ops->init(des); + if (ret) + return ret; + } + + if (des->ops->set_enable) { + ret = des->ops->set_enable(des, false); + if (ret) + return ret; + } + + for (i = 0; i < des->ops->num_phys; i++) { + struct max_des_phy *phy = &des->phys[i]; + + if (phy->enabled) { + ret = des->ops->init_phy(des, phy); + if (ret) + return ret; + } + + ret = des->ops->set_phy_enable(des, phy, phy->enabled); + if (ret) + return ret; + } + + for (i = 0; i < des->ops->num_pipes; i++) { + struct max_des_pipe *pipe = &des->pipes[i]; + struct max_des_link *link = &des->links[pipe->link_id]; + + ret = des->ops->set_pipe_enable(des, pipe, false); + if (ret) + return ret; + + if (des->ops->set_pipe_tunnel_enable) { + ret = des->ops->set_pipe_tunnel_enable(des, pipe, false); + if (ret) + return ret; + } + + if (des->ops->set_pipe_stream_id) { + ret = des->ops->set_pipe_stream_id(des, pipe, pipe->stream_id); + if (ret) + return ret; + } + + if (des->ops->set_pipe_link) { + ret = des->ops->set_pipe_link(des, pipe, link); + if (ret) + return ret; + } + + ret = max_des_set_pipe_remaps(priv, pipe, pipe->remaps, + pipe->num_remaps); + if (ret) + return ret; + } + + if (!des->ops->init_link) + return 0; + + for (i = 0; i < des->ops->num_links; i++) { + struct max_des_link *link = &des->links[i]; + + if (!link->enabled) + continue; + + ret = des->ops->init_link(des, link); + if (ret) + return ret; + } + + return 0; +} + +static void max_des_ser_find_version_range(struct max_des *des, int *min, int *max) +{ + unsigned int i; + + *min = MAX_SERDES_GMSL_MIN; + *max = MAX_SERDES_GMSL_MAX; + + if (!des->ops->needs_single_link_version) + return; + + for (i = 0; i < des->ops->num_links; i++) { + struct max_des_link *link = &des->links[i]; + + if (!link->enabled) + continue; + + if (!link->ser_xlate.en) + continue; + + *min = *max = link->version; + + return; + } +} + +static int max_des_ser_attach_addr(struct max_des_priv *priv, u32 chan_id, + u16 addr, u16 alias) +{ + struct max_des *des = priv->des; + struct max_des_link *link = &des->links[chan_id]; + int i, min, max; + int ret = 0; + + max_des_ser_find_version_range(des, &min, &max); + + if (link->ser_xlate.en) { + dev_err(priv->dev, "Serializer for link %u already bound\n", + link->index); + return -EINVAL; + } + + for (i = max; i >= min; i--) { + if (!(des->ops->versions & BIT(i))) + continue; + + if (des->ops->set_link_version) { + ret = des->ops->set_link_version(des, link, i); + if (ret) + return ret; + } + + ret = max_des_init_link_ser_xlate(priv, link, priv->client->adapter, + addr, alias); + if (!ret) + break; + } + + if (ret) { + dev_err(priv->dev, "Cannot find serializer for link %u\n", + link->index); + return -ENOENT; + } + + link->version = i; + link->ser_xlate.src = alias; + link->ser_xlate.dst = addr; + link->ser_xlate.en = true; + + return 0; +} + +static int max_des_ser_atr_attach_addr(struct i2c_atr *atr, u32 chan_id, + u16 addr, u16 alias) +{ + struct max_des_priv *priv = i2c_atr_get_driver_data(atr); + + return max_des_ser_attach_addr(priv, chan_id, addr, alias); +} + +static void max_des_ser_atr_detach_addr(struct i2c_atr *atr, u32 chan_id, u16 addr) +{ + /* Don't do anything. */ +} + +static const struct i2c_atr_ops max_des_i2c_atr_ops = { + .attach_addr = max_des_ser_atr_attach_addr, + .detach_addr = max_des_ser_atr_detach_addr, +}; + +static void max_des_i2c_atr_deinit(struct max_des_priv *priv) +{ + struct max_des *des = priv->des; + unsigned int i; + + for (i = 0; i < des->ops->num_links; i++) { + struct max_des_link *link = &des->links[i]; + + /* Deleting adapters that haven't been added does no harm. */ + i2c_atr_del_adapter(priv->atr, link->index); + } + + i2c_atr_delete(priv->atr); +} + +static int max_des_i2c_atr_init(struct max_des_priv *priv) +{ + struct max_des *des = priv->des; + unsigned int mask = 0; + unsigned int i; + int ret; + + if (!i2c_check_functionality(priv->client->adapter, + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) + return -ENODEV; + + priv->atr = i2c_atr_new(priv->client->adapter, priv->dev, + &max_des_i2c_atr_ops, des->ops->num_links, + I2C_ATR_F_STATIC | I2C_ATR_F_PASSTHROUGH); + if (IS_ERR(priv->atr)) + return PTR_ERR(priv->atr); + + i2c_atr_set_driver_data(priv->atr, priv); + + for (i = 0; i < des->ops->num_links; i++) { + struct max_des_link *link = &des->links[i]; + struct i2c_atr_adap_desc desc = { + .chan_id = i, + }; + + if (!link->enabled) + continue; + + ret = i2c_atr_add_adapter(priv->atr, &desc); + if (ret) + goto err_add_adapters; + } + + for (i = 0; i < des->ops->num_links; i++) { + struct max_des_link *link = &des->links[i]; + + if (!link->enabled) + continue; + + mask |= BIT(link->index); + } + + return des->ops->select_links(des, mask); + +err_add_adapters: + max_des_i2c_atr_deinit(priv); + + return ret; +} + +static void max_des_i2c_mux_deinit(struct max_des_priv *priv) +{ + i2c_mux_del_adapters(priv->mux); + bus_unregister_notifier(&i2c_bus_type, &priv->i2c_nb); +} + +static int max_des_i2c_mux_bus_notifier_call(struct notifier_block *nb, + unsigned long event, void *device) +{ + struct max_des_priv *priv = container_of(nb, struct max_des_priv, i2c_nb); + struct device *dev = device; + struct i2c_client *client; + u32 chan_id; + + /* + * Ideally, we would want to negotiate the GMSL version on + * BUS_NOTIFY_ADD_DEVICE, but the adapters list is only populated with + * the new adapter after BUS_NOTIFY_ADD_DEVICE is issued. + */ + if (event != BUS_NOTIFY_BIND_DRIVER) + return NOTIFY_DONE; + + client = i2c_verify_client(dev); + if (!client) + return NOTIFY_DONE; + + for (chan_id = 0; chan_id < priv->mux->max_adapters; ++chan_id) { + if (client->adapter == priv->mux->adapter[chan_id]) + break; + } + + if (chan_id == priv->mux->max_adapters) + return NOTIFY_DONE; + + max_des_ser_attach_addr(priv, chan_id, client->addr, client->addr); + + return NOTIFY_DONE; +} + +static int max_des_i2c_mux_select(struct i2c_mux_core *muxc, u32 chan) +{ + struct max_des_priv *priv = i2c_mux_priv(muxc); + struct max_des *des = priv->des; + + if (!des->ops->select_links) + return 0; + + return des->ops->select_links(des, BIT(chan)); +} + +static int max_des_i2c_mux_init(struct max_des_priv *priv) +{ + struct max_des *des = priv->des; + u32 flags = I2C_MUX_LOCKED; + unsigned int i; + int ret; + + if (des->ops->num_links == 1) + flags |= I2C_MUX_GATE; + + priv->mux = i2c_mux_alloc(priv->client->adapter, priv->dev, + des->ops->num_links, 0, flags, + max_des_i2c_mux_select, NULL); + if (!priv->mux) + return -ENOMEM; + + priv->mux->priv = priv; + + priv->i2c_nb.notifier_call = max_des_i2c_mux_bus_notifier_call; + ret = bus_register_notifier(&i2c_bus_type, &priv->i2c_nb); + if (ret) + return ret; + + for (i = 0; i < des->ops->num_links; i++) { + struct max_des_link *link = &des->links[i]; + + if (!link->enabled) + continue; + + ret = i2c_mux_add_adapter(priv->mux, 0, i); + if (ret) + goto err_add_adapters; + } + + return 0; + +err_add_adapters: + max_des_i2c_mux_deinit(priv); + + return ret; +} + +static void max_des_i2c_adapter_deinit(struct max_des_priv *priv) +{ + struct max_des *des = priv->des; + + if (des->ops->use_atr) + return max_des_i2c_atr_deinit(priv); + else + return max_des_i2c_mux_deinit(priv); +} + +static int max_des_i2c_adapter_init(struct max_des_priv *priv) +{ + struct max_des *des = priv->des; + + if (des->ops->use_atr) + return max_des_i2c_atr_init(priv); + else + return max_des_i2c_mux_init(priv); + + return 0; +} + +static int max_des_set_tpg_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *fmt = &format->format; + struct max_des_priv *priv = v4l2_get_subdevdata(sd); + struct max_des *des = priv->des; + const struct max_serdes_tpg_entry *entry; + struct v4l2_fract *in; + + if (format->stream != MAX_SERDES_TPG_STREAM) + return -EINVAL; + + entry = max_des_find_tpg_entry(des, 0, fmt->width, fmt->height, + fmt->code, 0, 0); + if (!entry) + return -EINVAL; + + in = v4l2_subdev_state_get_interval(state, format->pad, format->stream); + if (!in) + return -EINVAL; + + in->numerator = entry->interval.numerator; + in->denominator = entry->interval.denominator; + + return 0; +} + +static int max_des_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct max_des_priv *priv = v4l2_get_subdevdata(sd); + struct max_des *des = priv->des; + struct v4l2_mbus_framefmt *fmt; + int ret; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && des->active) + return -EBUSY; + + /* No transcoding, source and sink formats must match. */ + if (max_des_pad_is_source(des, format->pad)) + return v4l2_subdev_get_fmt(sd, state, format); + + if (max_des_pad_is_tpg(des, format->pad)) { + ret = max_des_set_tpg_fmt(sd, state, format); + if (ret) + return ret; + } + + fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); + if (!fmt) + return -EINVAL; + + *fmt = format->format; + + fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, + format->stream); + if (!fmt) + return -EINVAL; + + *fmt = format->format; + + return 0; +} + +static int max_des_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct max_des_priv *priv = v4l2_get_subdevdata(sd); + struct max_des *des = priv->des; + const struct max_serdes_tpg_entry *entry; + + if (!max_des_pad_is_tpg(des, fie->pad) || + fie->stream != MAX_SERDES_TPG_STREAM) + return -EINVAL; + + entry = max_des_find_tpg_entry(des, fie->index, fie->width, fie->height, + fie->code, fie->interval.denominator, + fie->interval.numerator); + if (!entry) + return -EINVAL; + + fie->interval.numerator = entry->interval.numerator; + fie->interval.denominator = entry->interval.denominator; + + return 0; +} + +static int max_des_set_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_interval *fi) +{ + struct max_des_priv *priv = v4l2_get_subdevdata(sd); + struct max_des *des = priv->des; + const struct max_serdes_tpg_entry *entry; + struct v4l2_mbus_framefmt *fmt; + struct v4l2_fract *in; + + if (!max_des_pad_is_tpg(des, fi->pad) || + fi->stream != MAX_SERDES_TPG_STREAM) + return -EINVAL; + + if (fi->which == V4L2_SUBDEV_FORMAT_ACTIVE && des->active) + return -EBUSY; + + fmt = v4l2_subdev_state_get_format(state, fi->pad, fi->stream); + if (!fmt) + return -EINVAL; + + entry = max_des_find_tpg_entry(des, 0, fmt->width, fmt->height, + fmt->code, fi->interval.denominator, + fi->interval.numerator); + if (!entry) + return -EINVAL; + + in = v4l2_subdev_state_get_interval(state, fi->pad, fi->stream); + if (!in) + return -EINVAL; + + in->numerator = fi->interval.numerator; + in->denominator = fi->interval.denominator; + + return 0; +} + +static int max_des_log_status(struct v4l2_subdev *sd) +{ + struct max_des_priv *priv = v4l2_get_subdevdata(sd); + struct max_des *des = priv->des; + unsigned int i, j; + int ret; + + v4l2_info(sd, "active: %u\n", des->active); + v4l2_info(sd, "mode: %s", max_serdes_gmsl_mode_str(des->mode)); + if (des->ops->set_tpg) { + const struct max_serdes_tpg_entry *entry = des->tpg_entry; + + if (entry) { + v4l2_info(sd, "tpg: %ux%u@%u/%u, code: %u, dt: %u, bpp: %u\n", + entry->width, entry->height, + entry->interval.numerator, + entry->interval.denominator, + entry->code, entry->dt, entry->bpp); + } else { + v4l2_info(sd, "tpg: disabled\n"); + } + } + if (des->ops->log_status) { + ret = des->ops->log_status(des); + if (ret) + return ret; + } + v4l2_info(sd, "\n"); + + for (i = 0; i < des->ops->num_links; i++) { + struct max_des_link *link = &des->links[i]; + + v4l2_info(sd, "link: %u\n", link->index); + v4l2_info(sd, "\tenabled: %u\n", link->enabled); + + if (!link->enabled) { + v4l2_info(sd, "\n"); + continue; + } + + v4l2_info(sd, "\tversion: %s\n", max_serdes_gmsl_version_str(link->version)); + v4l2_info(sd, "\tser_xlate: en: %u, src: 0x%02x dst: 0x%02x\n", + link->ser_xlate.en, link->ser_xlate.src, + link->ser_xlate.dst); + v4l2_info(sd, "\n"); + } + + for (i = 0; i < des->ops->num_pipes; i++) { + struct max_des_pipe *pipe = &des->pipes[i]; + + v4l2_info(sd, "pipe: %u\n", pipe->index); + v4l2_info(sd, "\tenabled: %u\n", pipe->enabled); + if (pipe->phy_id == des->ops->num_phys || + (priv->unused_phy && pipe->phy_id == priv->unused_phy->index)) + v4l2_info(sd, "\tphy_id: invalid\n"); + else + v4l2_info(sd, "\tphy_id: %u\n", pipe->phy_id); + v4l2_info(sd, "\tlink_id: %u\n", pipe->link_id); + if (des->ops->set_pipe_stream_id) + v4l2_info(sd, "\tstream_id: %u\n", pipe->stream_id); + if (des->ops->set_pipe_mode) { + v4l2_info(sd, "\tdbl8: %u\n", pipe->mode.dbl8); + v4l2_info(sd, "\tdbl8mode: %u\n", pipe->mode.dbl8mode); + v4l2_info(sd, "\tdbl10: %u\n", pipe->mode.dbl10); + v4l2_info(sd, "\tdbl10mode: %u\n", pipe->mode.dbl10mode); + v4l2_info(sd, "\tdbl12: %u\n", pipe->mode.dbl12); + } + if (des->ops->set_pipe_remap) { + v4l2_info(sd, "\tremaps: %u\n", pipe->num_remaps); + for (j = 0; j < pipe->num_remaps; j++) { + struct max_des_remap *remap = &pipe->remaps[j]; + + v4l2_info(sd, "\t\tremap: from: vc: %u, dt: 0x%02x\n", + remap->from_vc, remap->from_dt); + v4l2_info(sd, "\t\t to: vc: %u, dt: 0x%02x, phy: %u\n", + remap->to_vc, remap->to_dt, remap->phy); + } + } + if (des->ops->set_pipe_vc_remap) { + v4l2_info(sd, "\tvc_remaps: %u\n", pipe->num_vc_remaps); + for (j = 0; j < pipe->num_vc_remaps; j++) { + v4l2_info(sd, "\t\tvc_remap: src: %u, dst: %u\n", + pipe->vc_remaps[j].src, pipe->vc_remaps[j].dst); + } + } + if (des->ops->log_pipe_status) { + ret = des->ops->log_pipe_status(des, pipe); + if (ret) + return ret; + } + v4l2_info(sd, "\n"); + } + + for (i = 0; i < des->ops->num_phys; i++) { + struct max_des_phy *phy = &des->phys[i]; + + v4l2_info(sd, "phy: %u\n", phy->index); + v4l2_info(sd, "\tenabled: %u\n", phy->enabled); + + if (!phy->enabled) { + v4l2_info(sd, "\n"); + continue; + } + + v4l2_info(sd, "\tlink_frequency: %llu\n", phy->link_frequency); + v4l2_info(sd, "\tnum_data_lanes: %u\n", phy->mipi.num_data_lanes); + v4l2_info(sd, "\tclock_lane: %u\n", phy->mipi.clock_lane); + if (des->ops->set_phy_mode) { + v4l2_info(sd, "\talt_mem_map8: %u\n", phy->mode.alt_mem_map8); + v4l2_info(sd, "\talt2_mem_map8: %u\n", phy->mode.alt2_mem_map8); + v4l2_info(sd, "\talt_mem_map10: %u\n", phy->mode.alt_mem_map10); + v4l2_info(sd, "\talt_mem_map12: %u\n", phy->mode.alt_mem_map12); + } + if (des->ops->log_phy_status) { + ret = des->ops->log_phy_status(des, phy); + if (ret) + return ret; + } + v4l2_info(sd, "\n"); + } + + return 0; +} + +static int max_des_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct max_des_priv *priv = ctrl_to_priv(ctrl->handler); + struct max_des *des = priv->des; + + switch (ctrl->id) { + case V4L2_CID_TEST_PATTERN: + des->tpg_pattern = ctrl->val; + return 0; + } + + return -EINVAL; +} + +static int max_des_get_frame_desc_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_mbus_frame_desc *fd, + unsigned int pad) +{ + struct max_des_remap_context context = { 0 }; + struct max_des_priv *priv = sd_to_priv(sd); + struct v4l2_subdev_route *route; + int ret; + + fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; + + ret = max_des_populate_remap_context(priv, &context, state); + if (ret) + return ret; + + for_each_active_route(&state->routing, route) { + struct max_des_route_hw hw; + unsigned int dst_vc_id; + + if (pad != route->source_pad) + continue; + + ret = max_des_route_to_hw(priv, state, route, &hw); + if (ret) + return ret; + + ret = max_des_get_src_dst_vc_id(&context, hw.pipe->index, hw.phy->index, + hw.entry.bus.csi2.vc, &dst_vc_id); + if (ret) + return ret; + + hw.entry.bus.csi2.vc = dst_vc_id; + hw.entry.stream = route->source_stream; + + fd->entry[fd->num_entries++] = hw.entry; + } + + return 0; +} + +static int max_des_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) +{ + struct max_des_priv *priv = sd_to_priv(sd); + struct v4l2_subdev_state *state; + int ret; + + state = v4l2_subdev_lock_and_get_active_state(&priv->sd); + + ret = max_des_get_frame_desc_state(sd, state, fd, pad); + + v4l2_subdev_unlock_state(state); + + return ret; +} + +static int max_des_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_config *cfg) +{ + struct max_des_priv *priv = sd_to_priv(sd); + struct max_des *des = priv->des; + struct max_des_phy *phy; + + phy = max_des_pad_to_phy(des, pad); + if (!phy) + return -EINVAL; + + cfg->type = phy->bus_type; + cfg->bus.mipi_csi2 = phy->mipi; + cfg->link_freq = phy->link_frequency; + + return 0; +} + +static int max_des_set_tpg_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing) +{ + struct max_des_priv *priv = sd_to_priv(sd); + struct max_des *des = priv->des; + const struct max_serdes_tpg_entry *entry; + struct v4l2_mbus_framefmt fmt = { 0 }; + int ret; + + ret = max_serdes_validate_tpg_routing(routing); + if (ret) + return ret; + + entry = &des->ops->tpg_entries.entries[0]; + + fmt.width = entry->width; + fmt.height = entry->height; + fmt.code = entry->code; + + return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &fmt); +} + +static int __max_des_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing) +{ + struct max_des_priv *priv = sd_to_priv(sd); + struct max_des *des = priv->des; + struct v4l2_subdev_route *route; + bool is_tpg = false; + int ret; + + ret = v4l2_subdev_routing_validate(sd, routing, + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 | + V4L2_SUBDEV_ROUTING_NO_SINK_STREAM_MIX); + if (ret) + return ret; + + for_each_active_route(routing, route) { + if (max_des_pad_is_tpg(des, route->sink_pad)) { + is_tpg = true; + break; + } + } + + if (is_tpg) + return max_des_set_tpg_routing(sd, state, routing); + + return v4l2_subdev_set_routing(sd, state, routing); +} + +static int max_des_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + struct max_des_priv *priv = sd_to_priv(sd); + struct max_des *des = priv->des; + + if (which == V4L2_SUBDEV_FORMAT_ACTIVE && des->active) + return -EBUSY; + + return __max_des_set_routing(sd, state, routing); +} + +static int max_des_update_link(struct max_des_priv *priv, + struct max_des_remap_context *context, + struct max_des_link *link, + struct v4l2_subdev_state *state, + u64 *streams_masks, bool enable) +{ + struct max_des *des = priv->des; + struct max_des_pipe *pipe; + int ret; + + pipe = max_des_find_link_pipe(des, link); + if (!pipe) + return -ENOENT; + + if (pipe->enabled == enable) + return 0; + + ret = max_des_update_pipe(priv, context, pipe, state, streams_masks); + if (ret) + return ret; + + return 0; +} + +static int max_des_update_tpg(struct max_des_priv *priv, + struct v4l2_subdev_state *state, + u64 *streams_masks) +{ + const struct max_serdes_tpg_entry *entry = NULL; + struct max_des *des = priv->des; + struct v4l2_subdev_route *route; + int ret; + + for_each_active_route(&state->routing, route) { + struct max_des_route_hw hw; + + if (!(BIT_ULL(route->sink_stream) & streams_masks[route->sink_pad])) + continue; + + ret = max_des_route_to_hw(priv, state, route, &hw); + if (ret) + return ret; + + if (!hw.is_tpg) + continue; + + entry = max_des_find_state_tpg_entry(des, state, route->sink_pad); + break; + } + + if (entry == des->tpg_entry) + return 0; + + ret = des->ops->set_tpg(des, entry); + if (ret) + return ret; + + des->tpg_entry = entry; + + return 0; +} + +static int max_des_update_active(struct max_des_priv *priv, u64 *streams_masks, + bool expected_active) +{ + struct max_des *des = priv->des; + bool active = false; + unsigned int i; + int ret; + + for (i = 0; i < des->ops->num_phys; i++) { + struct max_des_phy *phy = &des->phys[i]; + u32 pad = max_des_phy_to_pad(des, phy); + + if (streams_masks[pad]) { + active = true; + break; + } + } + + if (active != expected_active || des->active == active) + return 0; + + if (des->ops->set_enable) { + ret = des->ops->set_enable(des, active); + if (ret) + return ret; + } + + des->active = active; + + return 0; +} + +static int max_des_update_links(struct max_des_priv *priv, + struct max_des_remap_context *context, + struct v4l2_subdev_state *state, + u64 *streams_masks, bool enable) +{ + struct max_des *des = priv->des; + unsigned int failed_update_link_id = des->ops->num_links; + unsigned int i; + int ret; + + for (i = 0; i < des->ops->num_links; i++) { + struct max_des_link *link = &des->links[i]; + + ret = max_des_update_link(priv, context, link, state, + streams_masks, enable); + if (ret) { + failed_update_link_id = i; + goto err; + } + } + + return 0; + +err: + for (i = 0; i < failed_update_link_id; i++) { + struct max_des_link *link = &des->links[i]; + + max_des_update_link(priv, context, link, state, + priv->streams_masks, enable); + } + + return ret; +} + +static int max_des_enable_disable_streams(struct max_des_priv *priv, + struct v4l2_subdev_state *state, + u32 pad, u64 updated_streams_mask, + bool enable) +{ + struct max_des *des = priv->des; + + return max_serdes_xlate_enable_disable_streams(priv->sources, 0, state, + pad, updated_streams_mask, 0, + des->ops->num_links, enable); +} + +static int max_des_update_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 updated_streams_mask, bool enable) +{ + struct max_des_priv *priv = v4l2_get_subdevdata(sd); + struct max_des_remap_context context = { 0 }; + struct max_des_mode_context mode_context = { 0 }; + struct max_des *des = priv->des; + unsigned int num_pads = max_des_num_pads(des); + u64 *streams_masks; + int ret; + + ret = max_des_populate_remap_context(priv, &context, state); + if (ret) + return ret; + + ret = max_des_populate_mode_context(priv, &mode_context, state, context.mode); + if (ret) + return ret; + + ret = max_serdes_get_streams_masks(priv->dev, state, pad, updated_streams_mask, + num_pads, priv->streams_masks, &streams_masks, + enable); + if (ret) + return ret; + + ret = max_des_set_pipes_phy(priv, &context); + if (ret) + goto err_free_streams_masks; + + ret = max_des_set_tunnel(priv, &context); + if (ret) + goto err_free_streams_masks; + + ret = max_des_set_modes(priv, &mode_context); + if (ret) + goto err_free_streams_masks; + + ret = max_des_set_vc_remaps(priv, &context, state, streams_masks); + if (ret) + return ret; + + ret = max_des_set_pipes_stream_id(priv); + if (ret) + goto err_free_streams_masks; + + if (!enable) { + ret = max_des_enable_disable_streams(priv, state, pad, + updated_streams_mask, enable); + if (ret) + goto err_free_streams_masks; + } + + ret = max_des_update_active(priv, streams_masks, false); + if (ret) + goto err_revert_streams_disable; + + ret = max_des_update_links(priv, &context, state, streams_masks, enable); + if (ret) + goto err_revert_active_disable; + + ret = max_des_update_tpg(priv, state, streams_masks); + if (ret) + goto err_revert_links_update; + + ret = max_des_update_active(priv, streams_masks, true); + if (ret) + goto err_revert_tpg_update; + + if (enable) { + ret = max_des_enable_disable_streams(priv, state, pad, + updated_streams_mask, enable); + if (ret) + goto err_revert_active_enable; + } + + devm_kfree(priv->dev, priv->streams_masks); + priv->streams_masks = streams_masks; + + return 0; + +err_revert_active_enable: + max_des_update_active(priv, priv->streams_masks, false); + +err_revert_tpg_update: + max_des_update_tpg(priv, state, priv->streams_masks); + +err_revert_links_update: + max_des_update_links(priv, &context, state, priv->streams_masks, false); + +err_revert_active_disable: + max_des_update_active(priv, priv->streams_masks, true); + +err_revert_streams_disable: + if (!enable) + max_des_enable_disable_streams(priv, state, pad, + updated_streams_mask, !enable); + +err_free_streams_masks: + devm_kfree(priv->dev, streams_masks); + + return ret; +} + +static int max_des_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + return max_des_update_streams(sd, state, pad, streams_mask, true); +} + +static int max_des_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + return max_des_update_streams(sd, state, pad, streams_mask, false); +} + +static int max_des_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_route routes[MAX_DES_NUM_LINKS] = { 0 }; + struct v4l2_subdev_krouting routing = { + .routes = routes, + }; + struct max_des_priv *priv = v4l2_get_subdevdata(sd); + struct max_des *des = priv->des; + struct max_des_phy *phy = NULL; + unsigned int stream = 0; + unsigned int i; + + for (i = 0; i < des->ops->num_phys; i++) { + if (des->phys[i].enabled) { + phy = &des->phys[i]; + break; + } + } + + if (!phy) + return 0; + + for (i = 0; i < des->ops->num_links; i++) { + struct max_des_link *link = &des->links[i]; + + if (!link->enabled) + continue; + + routing.routes[routing.num_routes++] = (struct v4l2_subdev_route) { + .sink_pad = max_des_link_to_pad(des, link), + .sink_stream = 0, + .source_pad = max_des_phy_to_pad(des, phy), + .source_stream = stream, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }; + stream++; + + /* + * The Streams API is an experimental feature. + * If multiple routes are provided here, userspace will not be + * able to configure them unless the Streams API is enabled. + * Provide a single route until it is enabled. + */ + break; + } + + return __max_des_set_routing(sd, state, &routing); +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int max_des_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct max_des_priv *priv = v4l2_get_subdevdata(sd); + struct max_des *des = priv->des; + unsigned int val; + int ret; + + ret = des->ops->reg_read(des, reg->reg, &val); + if (ret) + return ret; + + reg->val = val; + reg->size = 1; + + return 0; +} + +static int max_des_s_register(struct v4l2_subdev *sd, + const struct v4l2_dbg_register *reg) +{ + struct max_des_priv *priv = v4l2_get_subdevdata(sd); + struct max_des *des = priv->des; + + return des->ops->reg_write(des, reg->reg, reg->val); +} +#endif + +static const struct v4l2_subdev_core_ops max_des_core_ops = { + .log_status = max_des_log_status, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = max_des_g_register, + .s_register = max_des_s_register, +#endif +}; + +static const struct v4l2_ctrl_ops max_des_ctrl_ops = { + .s_ctrl = max_des_s_ctrl, +}; + +static const struct v4l2_subdev_pad_ops max_des_pad_ops = { + .enable_streams = max_des_enable_streams, + .disable_streams = max_des_disable_streams, + + .set_routing = max_des_set_routing, + .get_frame_desc = max_des_get_frame_desc, + + .get_mbus_config = max_des_get_mbus_config, + + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = max_des_set_fmt, + + .enum_frame_interval = max_des_enum_frame_interval, + .get_frame_interval = v4l2_subdev_get_frame_interval, + .set_frame_interval = max_des_set_frame_interval, +}; + +static const struct v4l2_subdev_ops max_des_subdev_ops = { + .core = &max_des_core_ops, + .pad = &max_des_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops max_des_internal_ops = { + .init_state = &max_des_init_state, +}; + +static const struct media_entity_operations max_des_media_ops = { + .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1, + .has_pad_interdep = v4l2_subdev_has_pad_interdep, + .link_validate = v4l2_subdev_link_validate, +}; + +static int max_des_notify_bound(struct v4l2_async_notifier *nf, + struct v4l2_subdev *subdev, + struct v4l2_async_connection *base_asc) +{ + struct max_des_priv *priv = nf_to_priv(nf); + struct max_serdes_asc *asc = asc_to_max(base_asc); + struct max_serdes_source *source = asc->source; + struct max_des *des = priv->des; + struct max_des_link *link = &des->links[source->index]; + u32 pad = max_des_link_to_pad(des, link); + int ret; + + ret = media_entity_get_fwnode_pad(&subdev->entity, + source->ep_fwnode, + MEDIA_PAD_FL_SOURCE); + if (ret < 0) { + dev_err(priv->dev, "Failed to find pad for %s\n", subdev->name); + return ret; + } + + source->sd = subdev; + source->pad = ret; + + ret = media_create_pad_link(&source->sd->entity, source->pad, + &priv->sd.entity, pad, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) { + dev_err(priv->dev, "Unable to link %s:%u -> %s:%u\n", + source->sd->name, source->pad, priv->sd.name, pad); + return ret; + } + + return 0; +} + +static void max_des_notify_unbind(struct v4l2_async_notifier *nf, + struct v4l2_subdev *subdev, + struct v4l2_async_connection *base_asc) +{ + struct max_serdes_asc *asc = asc_to_max(base_asc); + struct max_serdes_source *source = asc->source; + + source->sd = NULL; +} + +static const struct v4l2_async_notifier_operations max_des_notify_ops = { + .bound = max_des_notify_bound, + .unbind = max_des_notify_unbind, +}; + +static int max_des_v4l2_notifier_register(struct max_des_priv *priv) +{ + struct max_des *des = priv->des; + unsigned int i; + int ret; + + v4l2_async_subdev_nf_init(&priv->nf, &priv->sd); + + for (i = 0; i < des->ops->num_links; i++) { + struct max_des_link *link = &des->links[i]; + struct max_serdes_source *source; + struct max_serdes_asc *asc; + + if (!link->enabled) + continue; + + source = max_des_get_link_source(priv, link); + if (!source->ep_fwnode) + continue; + + asc = v4l2_async_nf_add_fwnode(&priv->nf, source->ep_fwnode, + struct max_serdes_asc); + if (IS_ERR(asc)) { + dev_err(priv->dev, + "Failed to add subdev for source %u: %pe", i, + asc); + + v4l2_async_nf_cleanup(&priv->nf); + + return PTR_ERR(asc); + } + + asc->source = source; + } + + priv->nf.ops = &max_des_notify_ops; + + ret = v4l2_async_nf_register(&priv->nf); + if (ret) { + dev_err(priv->dev, "Failed to register subdev notifier"); + v4l2_async_nf_cleanup(&priv->nf); + return ret; + } + + return 0; +} + +static void max_des_v4l2_notifier_unregister(struct max_des_priv *priv) +{ + v4l2_async_nf_unregister(&priv->nf); + v4l2_async_nf_cleanup(&priv->nf); +} + +static int max_des_v4l2_register(struct max_des_priv *priv) +{ + struct v4l2_subdev *sd = &priv->sd; + struct max_des *des = priv->des; + void *data = i2c_get_clientdata(priv->client); + unsigned int num_pads = max_des_num_pads(des); + unsigned int i; + int ret; + + v4l2_i2c_subdev_init(sd, priv->client, &max_des_subdev_ops); + i2c_set_clientdata(priv->client, data); + sd->internal_ops = &max_des_internal_ops; + sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + sd->entity.ops = &max_des_media_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; + + for (i = 0; i < num_pads; i++) { + if (max_des_pad_is_sink(des, i)) + priv->pads[i].flags = MEDIA_PAD_FL_SINK; + else if (max_des_pad_is_source(des, i)) + priv->pads[i].flags = MEDIA_PAD_FL_SOURCE; + else if (max_des_pad_is_tpg(des, i)) + priv->pads[i].flags = MEDIA_PAD_FL_SINK | + MEDIA_PAD_FL_INTERNAL; + else + return -EINVAL; + } + + v4l2_set_subdevdata(sd, priv); + + if (des->ops->tpg_patterns) { + v4l2_ctrl_handler_init(&priv->ctrl_handler, 1); + priv->sd.ctrl_handler = &priv->ctrl_handler; + + v4l2_ctrl_new_std_menu_items(&priv->ctrl_handler, + &max_des_ctrl_ops, + V4L2_CID_TEST_PATTERN, + MAX_SERDES_TPG_PATTERN_MAX, + ~des->ops->tpg_patterns, + __ffs(des->ops->tpg_patterns), + max_serdes_tpg_patterns); + if (priv->ctrl_handler.error) { + ret = priv->ctrl_handler.error; + goto err_free_ctrl; + } + } + + ret = media_entity_pads_init(&sd->entity, num_pads, priv->pads); + if (ret) + goto err_free_ctrl; + + ret = max_des_v4l2_notifier_register(priv); + if (ret) + goto err_media_entity_cleanup; + + ret = v4l2_subdev_init_finalize(sd); + if (ret) + goto err_nf_cleanup; + + ret = v4l2_async_register_subdev(sd); + if (ret) + goto err_sd_cleanup; + + return 0; + +err_sd_cleanup: + v4l2_subdev_cleanup(sd); +err_nf_cleanup: + max_des_v4l2_notifier_unregister(priv); +err_media_entity_cleanup: + media_entity_cleanup(&sd->entity); +err_free_ctrl: + v4l2_ctrl_handler_free(&priv->ctrl_handler); + + return ret; +} + +static void max_des_v4l2_unregister(struct max_des_priv *priv) +{ + struct v4l2_subdev *sd = &priv->sd; + + v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); + max_des_v4l2_notifier_unregister(priv); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(&priv->ctrl_handler); +} + +static int max_des_update_pocs(struct max_des_priv *priv, bool enable) +{ + struct max_des *des = priv->des; + unsigned int i; + int ret; + + for (i = 0; i < des->ops->num_links; i++) { + struct max_des_link *link = &des->links[i]; + + if (!link->enabled) + continue; + + if (!priv->pocs[i]) + continue; + + if (enable) + ret = regulator_enable(priv->pocs[i]); + else + ret = regulator_disable(priv->pocs[i]); + + if (ret) { + dev_err(priv->dev, + "Failed to set POC supply to %u: %u\n", + enable, ret); + return ret; + } + } + + return 0; +} + +static int max_des_parse_sink_dt_endpoint(struct max_des_priv *priv, + struct max_des_link *link, + struct max_serdes_source *source, + struct fwnode_handle *fwnode) +{ + struct max_des *des = priv->des; + u32 pad = max_des_link_to_pad(des, link); + unsigned int index = link->index; + struct fwnode_handle *ep; + char poc_name[10]; + int ret; + + ep = fwnode_graph_get_endpoint_by_id(fwnode, pad, 0, 0); + if (!ep) + return 0; + + source->ep_fwnode = fwnode_graph_get_remote_endpoint(ep); + fwnode_handle_put(ep); + if (!source->ep_fwnode) { + dev_err(priv->dev, + "Failed to get remote endpoint on port %u\n", pad); + return -ENODEV; + } + + snprintf(poc_name, sizeof(poc_name), "port%u-poc", index); + priv->pocs[index] = devm_regulator_get_optional(priv->dev, poc_name); + if (IS_ERR(priv->pocs[index])) { + ret = PTR_ERR(priv->pocs[index]); + if (ret != -ENODEV) { + dev_err(priv->dev, + "Failed to get POC supply on port %u: %d\n", + index, ret); + goto err_put_source_ep_fwnode; + } + + priv->pocs[index] = NULL; + } + + link->enabled = true; + + return 0; + +err_put_source_ep_fwnode: + fwnode_handle_put(source->ep_fwnode); + + return ret; +} + +static int max_des_parse_src_dt_endpoint(struct max_des_priv *priv, + struct max_des_phy *phy, + struct fwnode_handle *fwnode) +{ + struct max_des *des = priv->des; + u32 pad = max_des_phy_to_pad(des, phy); + struct v4l2_fwnode_endpoint v4l2_ep = { .bus_type = V4L2_MBUS_UNKNOWN }; + struct v4l2_mbus_config_mipi_csi2 *mipi = &v4l2_ep.bus.mipi_csi2; + enum v4l2_mbus_type bus_type; + struct fwnode_handle *ep; + u64 link_frequency; + unsigned int i; + int ret; + + ep = fwnode_graph_get_endpoint_by_id(fwnode, pad, 0, 0); + if (!ep) + return 0; + + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &v4l2_ep); + fwnode_handle_put(ep); + if (ret) { + dev_err(priv->dev, "Could not parse endpoint on port %u\n", pad); + return ret; + } + + bus_type = v4l2_ep.bus_type; + if (bus_type != V4L2_MBUS_CSI2_DPHY && + bus_type != V4L2_MBUS_CSI2_CPHY) { + v4l2_fwnode_endpoint_free(&v4l2_ep); + dev_err(priv->dev, "Unsupported bus-type %u on port %u\n", + pad, bus_type); + return -EINVAL; + } + + if (v4l2_ep.nr_of_link_frequencies == 0) + link_frequency = MAX_DES_LINK_FREQUENCY_DEFAULT; + else if (v4l2_ep.nr_of_link_frequencies == 1) + link_frequency = v4l2_ep.link_frequencies[0]; + else + ret = -EINVAL; + + v4l2_fwnode_endpoint_free(&v4l2_ep); + + if (ret) { + dev_err(priv->dev, "Invalid link frequencies %u on port %u\n", + v4l2_ep.nr_of_link_frequencies, pad); + return -EINVAL; + } + + if (link_frequency < MAX_DES_LINK_FREQUENCY_MIN || + link_frequency > MAX_DES_LINK_FREQUENCY_MAX) { + dev_err(priv->dev, "Invalid link frequency %llu on port %u\n", + link_frequency, pad); + return -EINVAL; + } + + for (i = 0; i < mipi->num_data_lanes; i++) { + if (mipi->data_lanes[i] > mipi->num_data_lanes) { + dev_err(priv->dev, "Invalid data lane %u on port %u\n", + mipi->data_lanes[i], pad); + return -EINVAL; + } + } + + phy->bus_type = bus_type; + phy->mipi = *mipi; + phy->link_frequency = link_frequency; + phy->enabled = true; + + return 0; +} + +int max_des_phy_hw_data_lanes(struct max_des *des, struct max_des_phy *phy) +{ + const struct max_serdes_phys_configs *configs = &des->ops->phys_configs; + const struct max_serdes_phys_config *config = + &configs->configs[des->phys_config]; + + return config->lanes[phy->index]; +} +EXPORT_SYMBOL_NS_GPL(max_des_phy_hw_data_lanes, "MAX_SERDES"); + +static int max_des_find_phys_config(struct max_des_priv *priv) +{ + struct max_des *des = priv->des; + const struct max_serdes_phys_configs *configs = &des->ops->phys_configs; + struct max_des_phy *phy; + unsigned int i, j; + + if (!configs->num_configs) + return 0; + + for (i = 0; i < configs->num_configs; i++) { + const struct max_serdes_phys_config *config = &configs->configs[i]; + bool matching = true; + + for (j = 0; j < des->ops->num_phys; j++) { + phy = &des->phys[j]; + + if (!phy->enabled) + continue; + + if (phy->mipi.num_data_lanes <= config->lanes[j] && + phy->mipi.clock_lane == config->clock_lane[j]) + continue; + + matching = false; + + break; + } + + if (matching) + break; + } + + if (i == configs->num_configs) { + dev_err(priv->dev, "Invalid lane configuration\n"); + return -EINVAL; + } + + des->phys_config = i; + + return 0; +} + +static int max_des_parse_dt(struct max_des_priv *priv) +{ + struct fwnode_handle *fwnode = dev_fwnode(priv->dev); + struct max_des *des = priv->des; + struct max_des_link *link; + struct max_des_pipe *pipe; + struct max_des_phy *phy; + unsigned int i; + int ret; + + for (i = 0; i < des->ops->num_phys; i++) { + phy = &des->phys[i]; + phy->index = i; + + ret = max_des_parse_src_dt_endpoint(priv, phy, fwnode); + if (ret) + return ret; + } + + ret = max_des_find_phys_config(priv); + if (ret) + return ret; + + /* Find an unsed PHY to send unampped data to. */ + for (i = 0; i < des->ops->num_phys; i++) { + phy = &des->phys[i]; + + if (!phy->enabled) { + priv->unused_phy = phy; + break; + } + } + + for (i = 0; i < des->ops->num_pipes; i++) { + pipe = &des->pipes[i]; + pipe->index = i; + + /* + * Serializers can send data on different stream ids over the + * same link, and some deserializers support stream id autoselect + * allowing them to receive data from all stream ids. + * Deserializers that support that feature should enable it. + * Deserializers that support per-link stream ids do not need + * to assign unique stream ids to each serializer. + */ + if (des->ops->needs_unique_stream_id) + pipe->stream_id = i; + else + pipe->stream_id = 0; + + /* + * We already checked that num_pipes >= num_links. + * Set up pipe to receive data from the link with the same index. + * This is already the default for most chips, and some of them + * don't even support receiving pipe data from a different link. + */ + pipe->link_id = i % des->ops->num_links; + } + + for (i = 0; i < des->ops->num_links; i++) { + link = &des->links[i]; + link->index = i; + } + + for (i = 0; i < des->ops->num_links; i++) { + struct max_des_link *link = &des->links[i]; + struct max_serdes_source *source; + + source = max_des_get_link_source(priv, link); + source->index = i; + + ret = max_des_parse_sink_dt_endpoint(priv, link, source, fwnode); + if (ret) + return ret; + } + + return 0; +} + +static int max_des_allocate(struct max_des_priv *priv) +{ + struct max_des *des = priv->des; + unsigned int num_pads = max_des_num_pads(des); + + des->phys = devm_kcalloc(priv->dev, des->ops->num_phys, + sizeof(*des->phys), GFP_KERNEL); + if (!des->phys) + return -ENOMEM; + + des->pipes = devm_kcalloc(priv->dev, des->ops->num_pipes, + sizeof(*des->pipes), GFP_KERNEL); + if (!des->pipes) + return -ENOMEM; + + des->links = devm_kcalloc(priv->dev, des->ops->num_links, + sizeof(*des->links), GFP_KERNEL); + if (!des->links) + return -ENOMEM; + + priv->sources = devm_kcalloc(priv->dev, des->ops->num_links, + sizeof(*priv->sources), GFP_KERNEL); + if (!priv->sources) + return -ENOMEM; + + priv->pocs = devm_kcalloc(priv->dev, des->ops->num_links, + sizeof(*priv->pocs), GFP_KERNEL); + if (!priv->pocs) + return -ENOMEM; + + priv->pads = devm_kcalloc(priv->dev, num_pads, + sizeof(*priv->pads), GFP_KERNEL); + if (!priv->pads) + return -ENOMEM; + + priv->streams_masks = devm_kcalloc(priv->dev, num_pads, + sizeof(*priv->streams_masks), + GFP_KERNEL); + if (!priv->streams_masks) + return -ENOMEM; + + return 0; +} + +int max_des_probe(struct i2c_client *client, struct max_des *des) +{ + struct device *dev = &client->dev; + struct max_des_priv *priv; + int ret; + + if (des->ops->num_phys > MAX_DES_NUM_PHYS) + return -E2BIG; + + if (des->ops->num_pipes > MAX_DES_NUM_PIPES) + return -E2BIG; + + if (des->ops->num_links > MAX_DES_NUM_LINKS) + return -E2BIG; + + if (des->ops->num_links > des->ops->num_pipes) + return -E2BIG; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + if (des->ops->set_link_version && !des->ops->select_links) { + dev_err(dev, + "Cannot implement .select_link_version() without .select_links()\n"); + return -EINVAL; + } + + if (hweight_long(des->ops->versions) >= 1 && + !des->ops->set_link_version) { + dev_err(dev, "Multiple version without .select_link_version()\n"); + return -EINVAL; + } + + priv->client = client; + priv->dev = dev; + priv->des = des; + des->priv = priv; + + ret = max_des_allocate(priv); + if (ret) + return ret; + + ret = max_des_parse_dt(priv); + if (ret) + return ret; + + ret = max_des_init(priv); + if (ret) + return ret; + + ret = max_des_update_pocs(priv, true); + if (ret) + return ret; + + ret = max_des_i2c_adapter_init(priv); + if (ret) + goto err_disable_pocs; + + ret = max_des_v4l2_register(priv); + if (ret) + goto err_i2c_adapter_deinit; + + return 0; + +err_i2c_adapter_deinit: + max_des_i2c_adapter_deinit(priv); + +err_disable_pocs: + max_des_update_pocs(priv, false); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(max_des_probe, "MAX_SERDES"); + +int max_des_remove(struct max_des *des) +{ + struct max_des_priv *priv = des->priv; + + max_des_v4l2_unregister(priv); + + max_des_i2c_adapter_deinit(priv); + + max_des_update_pocs(priv, false); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(max_des_remove, "MAX_SERDES"); + +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("I2C_ATR"); diff --git a/drivers/media/i2c/maxim-serdes/max_des.h b/drivers/media/i2c/maxim-serdes/max_des.h new file mode 100644 index 00000000000000..dd5d37575710b6 --- /dev/null +++ b/drivers/media/i2c/maxim-serdes/max_des.h @@ -0,0 +1,153 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2025 Analog Devices Inc. + */ + +#ifndef MAX_DES_H +#define MAX_DES_H + +#include + +#include "max_serdes.h" + +#define MAX_DES_DT_VC(dt, vc) (((vc) & 0x3) << 6 | ((dt) & 0x3f)) + +struct max_des_remap { + u8 from_dt; + u8 from_vc; + u8 to_dt; + u8 to_vc; + u8 phy; +}; + +struct max_des_link { + unsigned int index; + bool enabled; + enum max_serdes_gmsl_version version; + struct max_serdes_i2c_xlate ser_xlate; +}; + +struct max_des_pipe_mode { + bool dbl8; + bool dbl10; + bool dbl12; + bool dbl8mode; + bool dbl10mode; +}; + +struct max_des_pipe { + unsigned int index; + unsigned int stream_id; + unsigned int link_id; + unsigned int phy_id; + struct max_des_remap *remaps; + unsigned int num_remaps; + struct max_serdes_vc_remap *vc_remaps; + unsigned int num_vc_remaps; + struct max_des_pipe_mode mode; + bool enabled; +}; + +struct max_des_phy_mode { + bool alt_mem_map8; + bool alt2_mem_map8; + bool alt_mem_map10; + bool alt_mem_map12; +}; + +struct max_des_phy { + unsigned int index; + s64 link_frequency; + struct v4l2_mbus_config_mipi_csi2 mipi; + enum v4l2_mbus_type bus_type; + struct max_des_phy_mode mode; + bool enabled; +}; + +struct max_des; + +struct max_des_ops { + unsigned int num_phys; + unsigned int num_pipes; + unsigned int num_links; + unsigned int num_remaps_per_pipe; + unsigned int versions; + unsigned int modes; + bool fix_tx_ids; + bool use_atr; + bool needs_single_link_version; + bool needs_unique_stream_id; + + struct max_serdes_phys_configs phys_configs; + struct max_serdes_tpg_entries tpg_entries; + enum max_serdes_gmsl_mode tpg_mode; + unsigned int tpg_patterns; + +#ifdef CONFIG_VIDEO_ADV_DEBUG + int (*reg_read)(struct max_des *des, unsigned int reg, unsigned int *val); + int (*reg_write)(struct max_des *des, unsigned int reg, unsigned int val); +#endif + int (*log_status)(struct max_des *des); + int (*log_pipe_status)(struct max_des *des, struct max_des_pipe *pipe); + int (*log_phy_status)(struct max_des *des, struct max_des_phy *phy); + int (*set_enable)(struct max_des *des, bool enable); + int (*set_tpg)(struct max_des *des, const struct max_serdes_tpg_entry *entry); + int (*init)(struct max_des *des); + int (*init_phy)(struct max_des *des, struct max_des_phy *phy); + int (*set_phy_mode)(struct max_des *des, struct max_des_phy *phy, + struct max_des_phy_mode *mode); + int (*set_phy_enable)(struct max_des *des, struct max_des_phy *phy, + bool active); + int (*set_pipe_stream_id)(struct max_des *des, struct max_des_pipe *pipe, + unsigned int stream_id); + int (*set_pipe_link)(struct max_des *des, struct max_des_pipe *pipe, + struct max_des_link *link); + int (*set_pipe_phy)(struct max_des *des, struct max_des_pipe *pipe, + struct max_des_phy *phy); + int (*set_pipe_tunnel_phy)(struct max_des *des, struct max_des_pipe *pipe, + struct max_des_phy *phy); + int (*set_pipe_enable)(struct max_des *des, struct max_des_pipe *pipe, + bool enable); + int (*set_pipe_remap)(struct max_des *des, struct max_des_pipe *pipe, + unsigned int i, struct max_des_remap *remap); + int (*set_pipe_remaps_enable)(struct max_des *des, struct max_des_pipe *pipe, + unsigned int mask); + int (*set_pipe_vc_remap)(struct max_des *des, struct max_des_pipe *pipe, + unsigned int i, struct max_serdes_vc_remap *vc_remap); + int (*set_pipe_vc_remaps_enable)(struct max_des *des, struct max_des_pipe *pipe, + unsigned int mask); + int (*set_pipe_mode)(struct max_des *des, struct max_des_pipe *pipe, + struct max_des_pipe_mode *mode); + int (*set_pipe_tunnel_enable)(struct max_des *des, struct max_des_pipe *pipe, + bool enable); + int (*init_link)(struct max_des *des, struct max_des_link *link); + int (*select_links)(struct max_des *des, unsigned int mask); + int (*set_link_version)(struct max_des *des, struct max_des_link *link, + enum max_serdes_gmsl_version version); +}; + +struct max_des_priv; + +struct max_des { + struct max_des_priv *priv; + + const struct max_des_ops *ops; + + struct max_des_phy *phys; + struct max_des_pipe *pipes; + struct max_des_link *links; + const struct max_serdes_tpg_entry *tpg_entry; + enum max_serdes_tpg_pattern tpg_pattern; + + unsigned int phys_config; + enum max_serdes_gmsl_mode mode; + bool active; +}; + +int max_des_probe(struct i2c_client *client, struct max_des *des); + +int max_des_remove(struct max_des *des); + +int max_des_phy_hw_data_lanes(struct max_des *des, struct max_des_phy *phy); + +#endif // MAX_DES_H diff --git a/drivers/media/i2c/maxim-serdes/max_ser.c b/drivers/media/i2c/maxim-serdes/max_ser.c new file mode 100644 index 00000000000000..9c58627dc60cfb --- /dev/null +++ b/drivers/media/i2c/maxim-serdes/max_ser.c @@ -0,0 +1,2142 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Maxim GMSL2 Serializer Driver + * + * Copyright (C) 2025 Analog Devices Inc. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "max_ser.h" +#include "max_serdes.h" + +#define MAX_SER_NUM_LINKS 1 +#define MAX_SER_NUM_PHYS 1 + +struct max_ser_priv { + struct max_ser *ser; + struct device *dev; + struct i2c_client *client; + + struct i2c_atr *atr; + struct i2c_mux_core *mux; + + struct media_pad *pads; + struct max_serdes_source *sources; + u64 *streams_masks; + u32 double_bpps; + + struct v4l2_subdev sd; + struct v4l2_async_notifier nf; + struct v4l2_ctrl_handler ctrl_handler; +}; + +struct max_ser_route_hw { + struct max_serdes_source *source; + struct max_ser_pipe *pipe; + struct v4l2_mbus_frame_desc_entry entry; + bool is_tpg; +}; + +static inline struct max_ser_priv *sd_to_priv(struct v4l2_subdev *sd) +{ + return container_of(sd, struct max_ser_priv, sd); +} + +static inline struct max_ser_priv *nf_to_priv(struct v4l2_async_notifier *nf) +{ + return container_of(nf, struct max_ser_priv, nf); +} + +static inline struct max_ser_priv *ctrl_to_priv(struct v4l2_ctrl_handler *handler) +{ + return container_of(handler, struct max_ser_priv, ctrl_handler); +} + +static inline bool max_ser_pad_is_sink(struct max_ser *ser, u32 pad) +{ + return pad < ser->ops->num_phys; +} + +static inline bool max_ser_pad_is_source(struct max_ser *ser, u32 pad) +{ + return pad >= ser->ops->num_phys && + pad < ser->ops->num_phys + MAX_SER_NUM_LINKS; +} + +static inline u32 max_ser_source_pad(struct max_ser *ser) +{ + return ser->ops->num_phys; +} + +static inline bool max_ser_pad_is_tpg(struct max_ser *ser, u32 pad) +{ + return pad >= ser->ops->num_phys + MAX_SER_NUM_LINKS; +} + +static inline unsigned int max_ser_phy_to_pad(struct max_ser *ser, + struct max_ser_phy *phy) +{ + return phy->index; +} + +static inline unsigned int max_ser_num_pads(struct max_ser *ser) +{ + return ser->ops->num_phys + MAX_SER_NUM_LINKS + + (ser->ops->set_tpg ? 1 : 0); +} + +static struct max_ser_phy *max_ser_pad_to_phy(struct max_ser *ser, u32 pad) +{ + if (!max_ser_pad_is_sink(ser, pad)) + return NULL; + + return &ser->phys[pad]; +} + +static struct max_ser_pipe * +max_ser_find_phy_pipe(struct max_ser *ser, struct max_ser_phy *phy) +{ + unsigned int i; + + for (i = 0; i < ser->ops->num_pipes; i++) { + struct max_ser_pipe *pipe = &ser->pipes[i]; + + if (pipe->phy_id == phy->index) + return pipe; + } + + return NULL; +} + +static struct max_serdes_source * +max_ser_get_phy_source(struct max_ser_priv *priv, struct max_ser_phy *phy) +{ + return &priv->sources[phy->index]; +} + +static const struct max_serdes_tpg_entry * +max_ser_find_tpg_entry(struct max_ser *ser, u32 target_index, + u32 width, u32 height, u32 code, + u32 numerator, u32 denominator) +{ + const struct max_serdes_tpg_entry *entry; + unsigned int index = 0; + unsigned int i; + + for (i = 0; i < ser->ops->tpg_entries.num_entries; i++) { + entry = &ser->ops->tpg_entries.entries[i]; + + if ((width != 0 && width != entry->width) || + (height != 0 && height != entry->height) || + (code != 0 && code != entry->code) || + (numerator != 0 && numerator != entry->interval.numerator) || + (denominator != 0 && denominator != entry->interval.denominator)) + continue; + + if (index == target_index) + break; + + index++; + } + + if (i == ser->ops->tpg_entries.num_entries) + return NULL; + + return &ser->ops->tpg_entries.entries[i]; +} + +static const struct max_serdes_tpg_entry * +max_ser_find_state_tpg_entry(struct max_ser *ser, struct v4l2_subdev_state *state, + unsigned int pad) +{ + struct v4l2_mbus_framefmt *fmt; + struct v4l2_fract *in; + + fmt = v4l2_subdev_state_get_format(state, pad, MAX_SERDES_TPG_STREAM); + if (!fmt) + return NULL; + + in = v4l2_subdev_state_get_interval(state, pad, MAX_SERDES_TPG_STREAM); + if (!in) + return NULL; + + return max_ser_find_tpg_entry(ser, 0, fmt->width, fmt->height, fmt->code, + in->numerator, in->denominator); +} + +static int max_ser_get_tpg_fd_entry_state(struct max_ser *ser, + struct v4l2_subdev_state *state, + struct v4l2_mbus_frame_desc_entry *fd_entry, + unsigned int pad) +{ + const struct max_serdes_tpg_entry *entry; + + entry = max_ser_find_state_tpg_entry(ser, state, pad); + if (!entry) + return -EINVAL; + + fd_entry->stream = MAX_SERDES_TPG_STREAM; + fd_entry->flags = V4L2_MBUS_FRAME_DESC_FL_LEN_MAX; + fd_entry->length = entry->width * entry->height * entry->bpp / 8; + fd_entry->pixelcode = entry->code; + fd_entry->bus.csi2.vc = 0; + fd_entry->bus.csi2.dt = entry->dt; + + return 0; +} + +static int max_ser_tpg_route_to_hw(struct max_ser_priv *priv, + struct v4l2_subdev_state *state, + struct v4l2_subdev_route *route, + struct max_ser_route_hw *hw) +{ + struct max_ser *ser = priv->ser; + + hw->pipe = &ser->pipes[0]; + + return max_ser_get_tpg_fd_entry_state(ser, state, &hw->entry, + route->sink_pad); +} + +static int max_ser_route_to_hw(struct max_ser_priv *priv, + struct v4l2_subdev_state *state, + struct v4l2_subdev_route *route, + struct max_ser_route_hw *hw) +{ + struct max_ser *ser = priv->ser; + struct v4l2_mbus_frame_desc fd; + struct max_ser_phy *phy; + unsigned int i; + int ret; + + memset(hw, 0, sizeof(*hw)); + + hw->is_tpg = max_ser_pad_is_tpg(ser, route->sink_pad); + if (hw->is_tpg) + return max_ser_tpg_route_to_hw(priv, state, route, hw); + + phy = max_ser_pad_to_phy(ser, route->sink_pad); + if (!phy) + return -ENOENT; + + hw->pipe = max_ser_find_phy_pipe(ser, phy); + if (!hw->pipe) + return -ENOENT; + + hw->source = max_ser_get_phy_source(priv, phy); + if (!hw->source->sd) + return 0; + + ret = v4l2_subdev_call(hw->source->sd, pad, get_frame_desc, + hw->source->pad, &fd); + if (ret) + return ret; + + for (i = 0; i < fd.num_entries; i++) + if (fd.entry[i].stream == route->sink_stream) + break; + + if (i == fd.num_entries) + return -ENOENT; + + hw->entry = fd.entry[i]; + + return 0; +} + +static int max_ser_phy_set_active(struct max_ser *ser, struct max_ser_phy *phy, + bool active) +{ + int ret; + + if (ser->ops->set_phy_active) { + ret = ser->ops->set_phy_active(ser, phy, active); + if (ret) + return ret; + } + + phy->active = active; + + return 0; +} + +static int max_ser_set_pipe_dts(struct max_ser_priv *priv, struct max_ser_pipe *pipe, + unsigned int *dts, unsigned int num_dts) +{ + struct max_ser *ser = priv->ser; + unsigned int i; + int ret; + + if (!ser->ops->set_pipe_dt || !ser->ops->set_pipe_dt_en) + return 0; + + for (i = 0; i < num_dts; i++) { + ret = ser->ops->set_pipe_dt(ser, pipe, i, dts[i]); + if (ret) + return ret; + + ret = ser->ops->set_pipe_dt_en(ser, pipe, i, true); + if (ret) + return ret; + } + + if (num_dts == pipe->num_dts) + return 0; + + for (i = num_dts; i < ser->ops->num_dts_per_pipe; i++) { + ret = ser->ops->set_pipe_dt_en(ser, pipe, i, false); + if (ret) + return ret; + } + + return 0; +} + +static int max_ser_set_pipe_mode(struct max_ser_priv *priv, struct max_ser_pipe *pipe, + struct max_ser_pipe_mode *mode) +{ + struct max_ser *ser = priv->ser; + + if (mode->bpp == pipe->mode.bpp && + mode->soft_bpp == pipe->mode.soft_bpp && + mode->dbl8 == pipe->mode.dbl8 && + mode->dbl10 == pipe->mode.dbl10 && + mode->dbl12 == pipe->mode.dbl12) + return 0; + + if (!ser->ops->set_pipe_mode) + return 0; + + return ser->ops->set_pipe_mode(ser, pipe, mode); +} + +static int max_ser_i2c_atr_attach_addr(struct i2c_atr *atr, u32 chan_id, + u16 addr, u16 alias) +{ + struct max_serdes_i2c_xlate xlate = { + .src = alias, + .dst = addr, + .en = true, + }; + struct max_ser_priv *priv = i2c_atr_get_driver_data(atr); + struct max_ser *ser = priv->ser; + unsigned int i; + int ret; + + for (i = 0; i < ser->ops->num_i2c_xlates; i++) + if (!ser->i2c_xlates[i].en) + break; + + if (i == ser->ops->num_i2c_xlates) { + dev_err(priv->dev, + "Reached maximum number of I2C translations\n"); + return -EINVAL; + } + + ret = ser->ops->set_i2c_xlate(ser, i, &xlate); + if (ret) + return ret; + + ser->i2c_xlates[i] = xlate; + + return 0; +} + +static void max_ser_i2c_atr_detach_addr(struct i2c_atr *atr, u32 chan_id, u16 addr) +{ + struct max_ser_priv *priv = i2c_atr_get_driver_data(atr); + struct max_ser *ser = priv->ser; + struct max_serdes_i2c_xlate xlate = { 0 }; + unsigned int i; + + /* Find index of matching I2C translation. */ + for (i = 0; i < ser->ops->num_i2c_xlates; i++) + if (ser->i2c_xlates[i].dst == addr) + break; + + WARN_ON(i == ser->ops->num_i2c_xlates); + + ser->ops->set_i2c_xlate(ser, i, &xlate); + ser->i2c_xlates[i] = xlate; +} + +static const struct i2c_atr_ops max_ser_i2c_atr_ops = { + .attach_addr = max_ser_i2c_atr_attach_addr, + .detach_addr = max_ser_i2c_atr_detach_addr, +}; + +static void max_ser_i2c_atr_deinit(struct max_ser_priv *priv) +{ + /* Deleting adapters that haven't been added does no harm. */ + i2c_atr_del_adapter(priv->atr, 0); + + i2c_atr_delete(priv->atr); +} + +static int max_ser_i2c_atr_init(struct max_ser_priv *priv) +{ + struct i2c_atr_adap_desc desc = { + .chan_id = 0, + }; + + if (!i2c_check_functionality(priv->client->adapter, + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) + return -ENODEV; + + priv->atr = i2c_atr_new(priv->client->adapter, priv->dev, + &max_ser_i2c_atr_ops, 1, 0); + if (IS_ERR(priv->atr)) + return PTR_ERR(priv->atr); + + i2c_atr_set_driver_data(priv->atr, priv); + + return i2c_atr_add_adapter(priv->atr, &desc); +} + +static int max_ser_i2c_mux_select(struct i2c_mux_core *mux, u32 chan) +{ + return 0; +} + +static void max_ser_i2c_mux_deinit(struct max_ser_priv *priv) +{ + i2c_mux_del_adapters(priv->mux); +} + +static int max_ser_i2c_mux_init(struct max_ser_priv *priv) +{ + u32 adapter; + + priv->mux = i2c_mux_alloc(priv->client->adapter, &priv->client->dev, + 1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE, + max_ser_i2c_mux_select, NULL); + if (!priv->mux) + return -ENOMEM; + + /* serializer only have 1 input */ + if (fwnode_property_present(dev_fwnode(priv->dev), "mux-adapter")) + fwnode_property_read_u32(dev_fwnode(priv->dev), "mux-adapter", &adapter); + + if (adapter < 0) + adapter = 0; + + return i2c_mux_add_adapter(priv->mux, adapter, 0); +} + +static int max_ser_i2c_adapter_init(struct max_ser_priv *priv) +{ + if (device_get_named_child_node(priv->dev, "i2c-gate") || + fwnode_property_present(dev_fwnode(priv->dev), "i2c-gate")) + return max_ser_i2c_mux_init(priv); + else + return max_ser_i2c_atr_init(priv); +} + +static void max_ser_i2c_adapter_deinit(struct max_ser_priv *priv) +{ + if (device_get_named_child_node(priv->dev, "i2c-gate") || + fwnode_property_present(dev_fwnode(priv->dev), "i2c-gate")) + max_ser_i2c_mux_deinit(priv); + else + max_ser_i2c_atr_deinit(priv); +} + +static int max_ser_set_tpg_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *fmt = &format->format; + struct max_ser_priv *priv = v4l2_get_subdevdata(sd); + struct max_ser *ser = priv->ser; + const struct max_serdes_tpg_entry *entry; + struct v4l2_fract *in; + + if (format->stream != MAX_SERDES_TPG_STREAM) + return -EINVAL; + + entry = max_ser_find_tpg_entry(ser, 0, fmt->width, fmt->height, + fmt->code, 0, 0); + if (!entry) + return -EINVAL; + + in = v4l2_subdev_state_get_interval(state, format->pad, format->stream); + if (!in) + return -EINVAL; + + in->numerator = entry->interval.numerator; + in->denominator = entry->interval.denominator; + + return 0; +} + +static int max_ser_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct max_ser_priv *priv = v4l2_get_subdevdata(sd); + struct max_ser *ser = priv->ser; + struct v4l2_mbus_framefmt *fmt; + int ret; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && ser->active) + return -EBUSY; + + /* No transcoding, source and sink formats must match. */ + if (max_ser_pad_is_source(ser, format->pad)) + return v4l2_subdev_get_fmt(sd, state, format); + + if (max_ser_pad_is_tpg(ser, format->pad)) { + ret = max_ser_set_tpg_fmt(sd, state, format); + if (ret) + return ret; + } + + fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); + if (!fmt) + return -EINVAL; + + *fmt = format->format; + + fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, + format->stream); + if (!fmt) + return -EINVAL; + + *fmt = format->format; + + return 0; +} + +static int max_ser_log_status(struct v4l2_subdev *sd) +{ + struct max_ser_priv *priv = sd_to_priv(sd); + struct max_ser *ser = priv->ser; + unsigned int i, j; + int ret; + + v4l2_info(sd, "mode: %s\n", max_serdes_gmsl_mode_str(ser->mode)); + if (ser->ops->set_tpg) { + const struct max_serdes_tpg_entry *entry = ser->tpg_entry; + + if (entry) { + v4l2_info(sd, "tpg: %ux%u@%u/%u, code: %u, dt: %u, bpp: %u\n", + entry->width, entry->height, + entry->interval.numerator, + entry->interval.denominator, + entry->code, entry->dt, entry->bpp); + } else { + v4l2_info(sd, "tpg: disabled\n"); + } + } + if (ser->ops->log_status) { + ret = ser->ops->log_status(ser); + if (ret) + return ret; + } + v4l2_info(sd, "i2c_xlates:\n"); + for (i = 0; i < ser->ops->num_i2c_xlates; i++) { + v4l2_info(sd, "\ten: %u, src: 0x%02x dst: 0x%02x\n", + ser->i2c_xlates[i].en, ser->i2c_xlates[i].src, + ser->i2c_xlates[i].dst); + if (!ser->i2c_xlates[i].en) + break; + } + v4l2_info(sd, "\n"); + if (ser->ops->set_vc_remap) { + v4l2_info(sd, "vc_remaps: %u\n", ser->num_vc_remaps); + for (j = 0; j < ser->num_vc_remaps; j++) { + v4l2_info(sd, "\tvc_remap: src: %u, dst: %u\n", + ser->vc_remaps[j].src, ser->vc_remaps[j].dst); + } + } + v4l2_info(sd, "\n"); + + for (i = 0; i < ser->ops->num_pipes; i++) { + struct max_ser_pipe *pipe = &ser->pipes[i]; + + v4l2_info(sd, "pipe: %u\n", pipe->index); + v4l2_info(sd, "\tenabled: %u\n", pipe->enabled); + + if (!pipe->enabled) { + v4l2_info(sd, "\n"); + continue; + } + + v4l2_info(sd, "\tphy_id: %u\n", pipe->phy_id); + v4l2_info(sd, "\tstream_id: %u\n", pipe->stream_id); + if (ser->ops->set_pipe_phy) + v4l2_info(sd, "\tphy_id: %u\n", pipe->phy_id); + if (ser->ops->set_pipe_dt) { + v4l2_info(sd, "\tdts: %u\n", pipe->num_dts); + for (j = 0; j < pipe->num_dts; j++) + v4l2_info(sd, "\t\tdt: 0x%02x\n", pipe->dts[j]); + } + if (ser->ops->set_pipe_vcs) + v4l2_info(sd, "\tvcs: 0x%08x\n", pipe->vcs); + if (ser->ops->set_pipe_mode) { + v4l2_info(sd, "\tdbl8: %u\n", pipe->mode.dbl8); + v4l2_info(sd, "\tdbl10: %u\n", pipe->mode.dbl10); + v4l2_info(sd, "\tdbl12: %u\n", pipe->mode.dbl12); + v4l2_info(sd, "\tsoft_bpp: %u\n", pipe->mode.soft_bpp); + v4l2_info(sd, "\tbpp: %u\n", pipe->mode.bpp); + } + if (ser->ops->log_pipe_status) { + ret = ser->ops->log_pipe_status(ser, pipe); + if (ret) + return ret; + } + v4l2_info(sd, "\n"); + } + + for (i = 0; i < ser->ops->num_phys; i++) { + struct max_ser_phy *phy = &ser->phys[i]; + + v4l2_info(sd, "phy: %u\n", phy->index); + v4l2_info(sd, "\tenabled: %u\n", phy->enabled); + + if (!phy->enabled) { + v4l2_info(sd, "\n"); + continue; + } + + v4l2_info(sd, "\tactive: %u\n", phy->active); + v4l2_info(sd, "\tnum_data_lanes: %u\n", phy->mipi.num_data_lanes); + v4l2_info(sd, "\tclock_lane: %u\n", phy->mipi.clock_lane); + v4l2_info(sd, "\tnoncontinuous_clock: %u\n", + !!(phy->mipi.flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK)); + if (ser->ops->log_phy_status) { + ret = ser->ops->log_phy_status(ser, phy); + if (ret) + return ret; + } + v4l2_info(sd, "\n"); + } + + return 0; +} + +static int max_ser_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct max_ser_priv *priv = ctrl_to_priv(ctrl->handler); + struct max_ser *ser = priv->ser; + + switch (ctrl->id) { + case V4L2_CID_TEST_PATTERN: + ser->tpg_pattern = ctrl->val; + return 0; + } + + return -EINVAL; +} + +static int max_ser_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct max_ser_priv *priv = v4l2_get_subdevdata(sd); + struct max_ser *ser = priv->ser; + const struct max_serdes_tpg_entry *entry; + + if (!max_ser_pad_is_tpg(ser, fie->pad) || + fie->stream != MAX_SERDES_TPG_STREAM) + return -EINVAL; + + entry = max_ser_find_tpg_entry(ser, fie->index, fie->width, fie->height, + fie->code, fie->interval.denominator, + fie->interval.numerator); + if (!entry) + return -EINVAL; + + fie->interval.numerator = entry->interval.numerator; + fie->interval.denominator = entry->interval.denominator; + + return 0; +} + +static int max_ser_set_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_interval *fi) +{ + struct max_ser_priv *priv = v4l2_get_subdevdata(sd); + struct max_ser *ser = priv->ser; + const struct max_serdes_tpg_entry *entry; + struct v4l2_mbus_framefmt *fmt; + struct v4l2_fract *in; + + if (!max_ser_pad_is_tpg(ser, fi->pad) || + fi->stream != MAX_SERDES_TPG_STREAM) + return -EINVAL; + + if (fi->which == V4L2_SUBDEV_FORMAT_ACTIVE && ser->active) + return -EBUSY; + + fmt = v4l2_subdev_state_get_format(state, fi->pad, fi->stream); + if (!fmt) + return -EINVAL; + + entry = max_ser_find_tpg_entry(ser, 0, fmt->width, fmt->height, + fmt->code, fi->interval.denominator, + fi->interval.numerator); + if (!entry) + return -EINVAL; + + in = v4l2_subdev_state_get_interval(state, fi->pad, fi->stream); + if (!in) + return -EINVAL; + + in->numerator = fi->interval.numerator; + in->denominator = fi->interval.denominator; + + return 0; +} + +static int max_ser_get_frame_desc_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_mbus_frame_desc *fd, + unsigned int pad) +{ + struct max_ser_priv *priv = sd_to_priv(sd); + struct max_ser *ser = priv->ser; + struct v4l2_subdev_route *route; + int ret; + + if (!max_ser_pad_is_source(ser, pad)) + return -ENOENT; + + fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; + + for_each_active_route(&state->routing, route) { + struct max_ser_route_hw hw; + + if (pad != route->source_pad) + continue; + + ret = max_ser_route_to_hw(priv, state, route, &hw); + if (ret) + return ret; + + hw.entry.stream = route->source_stream; + + fd->entry[fd->num_entries++] = hw.entry; + } + + return 0; +} + +static int max_ser_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) +{ + struct max_ser_priv *priv = sd_to_priv(sd); + struct v4l2_subdev_state *state; + int ret; + + state = v4l2_subdev_lock_and_get_active_state(&priv->sd); + + ret = max_ser_get_frame_desc_state(sd, state, fd, pad); + + v4l2_subdev_unlock_state(state); + + return ret; +} + +static int max_ser_set_tpg_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing) +{ + struct max_ser_priv *priv = sd_to_priv(sd); + struct max_ser *ser = priv->ser; + const struct max_serdes_tpg_entry *entry; + struct v4l2_mbus_framefmt fmt = { 0 }; + int ret; + + ret = max_serdes_validate_tpg_routing(routing); + if (ret) + return ret; + + entry = &ser->ops->tpg_entries.entries[0]; + + fmt.width = entry->width; + fmt.height = entry->height; + fmt.code = entry->code; + + return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &fmt); +} + +static int __max_ser_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing) +{ + struct max_ser_priv *priv = sd_to_priv(sd); + struct max_ser *ser = priv->ser; + struct v4l2_subdev_route *route; + bool is_tpg = false; + int ret; + + ret = v4l2_subdev_routing_validate(sd, routing, + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 | + V4L2_SUBDEV_ROUTING_NO_SINK_STREAM_MIX); + if (ret) + return ret; + + for_each_active_route(routing, route) { + if (max_ser_pad_is_tpg(ser, route->sink_pad)) { + is_tpg = true; + break; + } + } + + if (is_tpg) + return max_ser_set_tpg_routing(sd, state, routing); + + return v4l2_subdev_set_routing(sd, state, routing); +} + +static int max_ser_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + struct max_ser_priv *priv = sd_to_priv(sd); + struct max_ser *ser = priv->ser; + + if (which == V4L2_SUBDEV_FORMAT_ACTIVE && ser->active) + return -EBUSY; + + return __max_ser_set_routing(sd, state, routing); +} + +static int max_ser_get_pipe_vcs_dts(struct max_ser_priv *priv, + struct v4l2_subdev_state *state, + struct max_ser_pipe *pipe, + unsigned int *vcs, + unsigned int *dts, unsigned int *num_dts, + u64 *streams_masks) +{ + struct v4l2_subdev_route *route; + struct max_ser *ser = priv->ser; + unsigned int i; + int ret; + + *vcs = 0; + *num_dts = 0; + + if (ser->mode != MAX_SERDES_GMSL_PIXEL_MODE) + return 0; + + for_each_active_route(&state->routing, route) { + struct max_ser_route_hw hw; + unsigned int vc, dt; + + if (!(BIT_ULL(route->sink_stream) & streams_masks[route->sink_pad])) + continue; + + ret = max_ser_route_to_hw(priv, state, route, &hw); + if (ret) + return ret; + + if (hw.pipe != pipe) + continue; + + vc = hw.entry.bus.csi2.vc; + dt = hw.entry.bus.csi2.dt; + + if (vc >= MAX_SERDES_VC_ID_NUM) + return -E2BIG; + + *vcs |= BIT(vc); + + /* Skip already added DT. */ + for (i = 0; i < *num_dts; i++) + if (dts[i] == dt) + break; + + if (i < *num_dts) + continue; + + dts[*num_dts] = dt; + (*num_dts)++; + } + + /* + * Hardware cannot distinguish between different pairs of VC and DT, + * issue a warning. + */ + for_each_active_route(&state->routing, route) { + struct max_ser_route_hw hw; + unsigned int vc, dt; + + /* + * Skip enabled streams, we only want to check for leaks + * among the disabled streams. + */ + if ((BIT_ULL(route->sink_stream) & streams_masks[route->sink_pad])) + continue; + + ret = max_ser_route_to_hw(priv, state, route, &hw); + if (ret) + return ret; + + if (hw.pipe != pipe) + continue; + + vc = hw.entry.bus.csi2.vc; + dt = hw.entry.bus.csi2.dt; + + if (vc >= MAX_SERDES_VC_ID_NUM) + return -E2BIG; + + if (!(*vcs & BIT(vc))) + continue; + + for (i = 0; i < *num_dts; i++) + if (dts[i] == dt) + break; + + if (i == *num_dts) + continue; + + dev_warn(priv->dev, "Leaked disabled stream %u:%u with VC: %u, DT: %u", + route->source_pad, route->source_stream, vc, dt); + } + + return 0; +} + +static int max_ser_get_pipe_mode(struct max_ser_priv *priv, + struct v4l2_subdev_state *state, + struct max_ser_pipe *pipe, + struct max_ser_pipe_mode *mode) +{ + struct v4l2_subdev_route *route; + struct max_ser *ser = priv->ser; + bool force_set_bpp = false; + unsigned int doubled_bpp; + unsigned int min_bpp; + unsigned int max_bpp; + u32 bpps = 0; + int ret; + + if (ser->mode != MAX_SERDES_GMSL_PIXEL_MODE) + return 0; + + for_each_active_route(&state->routing, route) { + struct max_ser_route_hw hw; + unsigned int bpp; + + ret = max_ser_route_to_hw(priv, state, route, &hw); + if (ret) + return ret; + + if (hw.pipe != pipe) + continue; + + if (hw.is_tpg) + force_set_bpp = true; + + ret = max_serdes_get_fd_bpp(&hw.entry, &bpp); + if (ret) + return ret; + + bpps |= BIT(bpp); + } + + ret = max_serdes_process_bpps(priv->dev, bpps, priv->double_bpps, &doubled_bpp); + if (ret) + return ret; + + if (doubled_bpp == 8) + mode->dbl8 = true; + else if (doubled_bpp == 10) + mode->dbl10 = true; + else if (doubled_bpp == 12) + mode->dbl12 = true; + + if (doubled_bpp) { + bpps &= ~BIT(doubled_bpp); + bpps |= BIT(doubled_bpp * 2); + } + + min_bpp = __ffs(bpps); + max_bpp = __fls(bpps); + + if (doubled_bpp) + mode->soft_bpp = min_bpp; + + if (min_bpp != max_bpp || force_set_bpp) + mode->bpp = max_bpp; + + return 0; +} + +static int max_ser_update_pipe_enable(struct max_ser_priv *priv, + struct max_ser_pipe *pipe, + struct v4l2_subdev_state *state, + u64 *streams_masks) +{ + struct max_ser *ser = priv->ser; + struct v4l2_subdev_route *route; + bool enable = false; + int ret; + + for_each_active_route(&state->routing, route) { + struct max_ser_route_hw hw; + + if (!(BIT_ULL(route->sink_stream) & streams_masks[route->sink_pad])) + continue; + + ret = max_ser_route_to_hw(priv, state, route, &hw); + if (ret) + return ret; + + if (hw.pipe != pipe) + continue; + + enable = true; + break; + } + + if (enable == pipe->enabled) + return 0; + + ret = ser->ops->set_pipe_enable(ser, pipe, enable); + if (ret) + return ret; + + pipe->enabled = enable; + + return 0; +} + +static int max_ser_update_pipe(struct max_ser_priv *priv, + struct max_ser_pipe *pipe, + struct v4l2_subdev_state *state, + u64 *streams_masks) +{ + struct max_ser *ser = priv->ser; + struct max_ser_pipe_mode mode = { 0 }; + unsigned int num_dts; + unsigned int *dts; + unsigned int vcs; + int ret; + + if (!ser->ops->num_dts_per_pipe) + return 0; + + dts = devm_kcalloc(priv->dev, ser->ops->num_dts_per_pipe, sizeof(*dts), + GFP_KERNEL); + if (!dts) + return -ENOMEM; + + ret = max_ser_get_pipe_vcs_dts(priv, state, pipe, &vcs, dts, &num_dts, + streams_masks); + if (ret) + goto err_free_dts; + + ret = max_ser_get_pipe_mode(priv, state, pipe, &mode); + if (ret) + goto err_free_dts; + + if (ser->ops->set_pipe_vcs) { + ret = ser->ops->set_pipe_vcs(ser, pipe, vcs); + if (ret) + goto err_free_dts; + } + + ret = max_ser_set_pipe_mode(priv, pipe, &mode); + if (ret) + goto err_revert_vcs; + + ret = max_ser_set_pipe_dts(priv, pipe, dts, num_dts); + if (ret) + goto err_revert_mode; + + pipe->vcs = vcs; + pipe->mode = mode; + + if (pipe->dts) + devm_kfree(priv->dev, pipe->dts); + + pipe->dts = dts; + pipe->num_dts = num_dts; + + return 0; + +err_revert_mode: + max_ser_set_pipe_mode(priv, pipe, &pipe->mode); + +err_revert_vcs: + if (ser->ops->set_pipe_vcs) + ser->ops->set_pipe_vcs(ser, pipe, pipe->vcs); + +err_free_dts: + devm_kfree(priv->dev, dts); + + return ret; +} + +static int max_ser_update_phy(struct max_ser_priv *priv, + struct v4l2_subdev_state *state, + struct max_ser_phy *phy, u64 *streams_masks) +{ + struct max_ser *ser = priv->ser; + u32 pad = max_ser_phy_to_pad(ser, phy); + bool enable_changed = !streams_masks[pad] != !priv->streams_masks[pad]; + bool enable = !!streams_masks[pad]; + struct max_ser_pipe *pipe; + int ret; + + pipe = max_ser_find_phy_pipe(ser, phy); + if (!pipe) + return -ENOENT; + + if (!enable && enable_changed) { + ret = max_ser_phy_set_active(ser, phy, enable); + if (ret) + return ret; + } + + ret = max_ser_update_pipe(priv, pipe, state, streams_masks); + if (ret) + goto err_revert_phy_disable; + + ret = max_ser_update_pipe_enable(priv, pipe, state, streams_masks); + if (ret) + goto err_revert_pipe_update; + + if (enable && enable_changed) { + ret = max_ser_phy_set_active(ser, phy, enable); + if (ret) + goto err_revert_update_pipe_enable; + } + + return 0; + +err_revert_update_pipe_enable: + max_ser_update_pipe_enable(priv, pipe, state, priv->streams_masks); + +err_revert_pipe_update: + max_ser_update_pipe(priv, pipe, state, priv->streams_masks); + +err_revert_phy_disable: + if (!enable && enable_changed) + max_ser_phy_set_active(ser, phy, !enable); + + return ret; +} + +static int max_ser_update_phys(struct max_ser_priv *priv, + struct v4l2_subdev_state *state, + u64 *streams_masks) +{ + struct max_ser *ser = priv->ser; + unsigned int failed_update_phy_id = ser->ops->num_phys; + unsigned int i; + int ret; + + for (i = 0; i < ser->ops->num_phys; i++) { + struct max_ser_phy *phy = &ser->phys[i]; + + ret = max_ser_update_phy(priv, state, phy, streams_masks); + if (ret) { + failed_update_phy_id = i; + goto err; + } + } + + return 0; + +err: + for (i = 0; i < failed_update_phy_id; i++) { + struct max_ser_phy *phy = &ser->phys[i]; + + max_ser_update_phy(priv, state, phy, priv->streams_masks); + } + + return ret; +} + +static int max_ser_enable_disable_streams(struct max_ser_priv *priv, + struct v4l2_subdev_state *state, + u32 pad, u64 updated_streams_mask, + bool enable) +{ + struct max_ser *ser = priv->ser; + + return max_serdes_xlate_enable_disable_streams(priv->sources, 0, state, + pad, updated_streams_mask, 0, + ser->ops->num_phys, enable); +} + +static bool max_ser_is_tpg_routed(struct max_ser_priv *priv, + struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_route *route; + int ret; + + for_each_active_route(&state->routing, route) { + struct max_ser_route_hw hw; + + ret = max_ser_route_to_hw(priv, state, route, &hw); + if (ret) + return false; + + if (hw.is_tpg) + return true; + } + + return false; +} + +static int max_ser_update_tpg(struct max_ser_priv *priv, + struct v4l2_subdev_state *state, + u64 *streams_masks) +{ + const struct max_serdes_tpg_entry *entry = NULL; + struct max_ser *ser = priv->ser; + struct v4l2_subdev_route *route; + int ret; + + for_each_active_route(&state->routing, route) { + struct max_ser_route_hw hw; + + if (!(BIT_ULL(route->sink_stream) & streams_masks[route->sink_pad])) + continue; + + ret = max_ser_route_to_hw(priv, state, route, &hw); + if (ret) + return ret; + + if (!hw.is_tpg) + continue; + + entry = max_ser_find_state_tpg_entry(ser, state, route->sink_pad); + break; + } + + if (entry == ser->tpg_entry) + return 0; + + ret = ser->ops->set_tpg(ser, entry); + if (ret) + return ret; + + ser->tpg_entry = entry; + + return 0; +} + +static int max_ser_update_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 updated_streams_mask, bool enable) +{ + struct max_ser_priv *priv = v4l2_get_subdevdata(sd); + struct max_ser *ser = priv->ser; + unsigned int num_pads = max_ser_num_pads(ser); + u64 *streams_masks; + int ret; + + ret = max_serdes_get_streams_masks(priv->dev, state, pad, updated_streams_mask, + num_pads, priv->streams_masks, &streams_masks, + enable); + if (ret) + return ret; + + if (!enable) { + ret = max_ser_enable_disable_streams(priv, state, pad, + updated_streams_mask, enable); + if (ret) + goto err_free_streams_masks; + } + + ret = max_ser_update_tpg(priv, state, streams_masks); + if (ret) + goto err_revert_streams_disable; + + ret = max_ser_update_phys(priv, state, streams_masks); + if (ret) + goto err_revert_update_tpg; + + if (enable) { + ret = max_ser_enable_disable_streams(priv, state, pad, + updated_streams_mask, enable); + if (ret) + goto err_revert_phys_update; + } + + devm_kfree(priv->dev, priv->streams_masks); + priv->streams_masks = streams_masks; + ser->active = !!streams_masks[pad]; + + return 0; + +err_revert_phys_update: + max_ser_update_phys(priv, state, priv->streams_masks); + +err_revert_update_tpg: + max_ser_update_tpg(priv, state, priv->streams_masks); + +err_revert_streams_disable: + if (!enable) + max_ser_enable_disable_streams(priv, state, pad, + updated_streams_mask, !enable); + +err_free_streams_masks: + devm_kfree(priv->dev, streams_masks); + + return ret; +} + +static int max_ser_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + return max_ser_update_streams(sd, state, pad, streams_mask, true); +} + +static int max_ser_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + return max_ser_update_streams(sd, state, pad, streams_mask, false); +} + +static int max_ser_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_route routes[MAX_SER_NUM_PHYS] = { 0 }; + struct v4l2_subdev_krouting routing = { + .routes = routes, + }; + struct max_ser_priv *priv = v4l2_get_subdevdata(sd); + struct max_ser *ser = priv->ser; + unsigned int stream = 0; + unsigned int i; + + for (i = 0; i < ser->ops->num_phys; i++) { + struct max_ser_phy *phy = &ser->phys[i]; + + if (!phy->enabled) + continue; + + routing.routes[routing.num_routes++] = (struct v4l2_subdev_route) { + .sink_pad = max_ser_phy_to_pad(ser, phy), + .sink_stream = 0, + .source_pad = max_ser_source_pad(ser), + .source_stream = stream, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }; + stream++; + + /* + * The Streams API is an experimental feature. + * If multiple routes are provided here, userspace will not be + * able to configure them unless the Streams API is enabled. + * Provide a single route until it is enabled. + */ + break; + } + + return __max_ser_set_routing(sd, state, &routing); +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int max_ser_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct max_ser_priv *priv = sd_to_priv(sd); + struct max_ser *ser = priv->ser; + unsigned int val; + int ret; + + ret = ser->ops->reg_read(ser, reg->reg, &val); + if (ret) + return ret; + + reg->val = val; + reg->size = 1; + + return 0; +} + +static int max_ser_s_register(struct v4l2_subdev *sd, + const struct v4l2_dbg_register *reg) +{ + struct max_ser_priv *priv = sd_to_priv(sd); + struct max_ser *ser = priv->ser; + + return ser->ops->reg_write(ser, reg->reg, reg->val); +} +#endif + +static const struct v4l2_subdev_core_ops max_ser_core_ops = { + .log_status = max_ser_log_status, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = max_ser_g_register, + .s_register = max_ser_s_register, +#endif +}; + +static const struct v4l2_ctrl_ops max_ser_ctrl_ops = { + .s_ctrl = max_ser_s_ctrl, +}; + +static const struct v4l2_subdev_pad_ops max_ser_pad_ops = { + .enable_streams = max_ser_enable_streams, + .disable_streams = max_ser_disable_streams, + + .set_routing = max_ser_set_routing, + .get_frame_desc = max_ser_get_frame_desc, + + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = max_ser_set_fmt, + + .enum_frame_interval = max_ser_enum_frame_interval, + .get_frame_interval = v4l2_subdev_get_frame_interval, + .set_frame_interval = max_ser_set_frame_interval, +}; + +static const struct v4l2_subdev_ops max_ser_subdev_ops = { + .core = &max_ser_core_ops, + .pad = &max_ser_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops max_ser_internal_ops = { + .init_state = &max_ser_init_state, +}; + +static const struct media_entity_operations max_ser_media_ops = { + .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1, + .has_pad_interdep = v4l2_subdev_has_pad_interdep, + .link_validate = v4l2_subdev_link_validate, +}; + +static int max_ser_init(struct max_ser_priv *priv) +{ + struct max_ser *ser = priv->ser; + unsigned int i; + int ret; + + if (ser->ops->init) { + ret = ser->ops->init(ser); + if (ret) + return ret; + } + + if (ser->ops->set_tunnel_enable) { + ret = ser->ops->set_tunnel_enable(ser, false); + if (ret) + return ret; + } + + for (i = 0; i < ser->ops->num_phys; i++) { + struct max_ser_phy *phy = &ser->phys[i]; + + if (phy->enabled) { + ret = ser->ops->init_phy(ser, phy); + if (ret) + return ret; + } + + if (ser->ops->set_phy_active) { + ret = ser->ops->set_phy_active(ser, phy, false); + if (ret) + return ret; + } + } + + for (i = 0; i < ser->ops->num_pipes; i++) { + struct max_ser_pipe *pipe = &ser->pipes[i]; + struct max_ser_phy *phy = &ser->phys[pipe->phy_id]; + + ret = ser->ops->set_pipe_enable(ser, pipe, false); + if (ret) + return ret; + + if (ser->ops->set_pipe_stream_id) { + ret = ser->ops->set_pipe_stream_id(ser, pipe, pipe->stream_id); + if (ret) + return ret; + } + + if (ser->ops->set_pipe_phy) { + ret = ser->ops->set_pipe_phy(ser, pipe, phy); + if (ret) + return ret; + } + + if (ser->ops->set_pipe_vcs) { + ret = ser->ops->set_pipe_vcs(ser, pipe, 0); + if (ret) + return ret; + } + + if (ser->ops->set_pipe_mode) { + ret = ser->ops->set_pipe_mode(ser, pipe, &pipe->mode); + if (ret) + return ret; + } + + ret = max_ser_set_pipe_dts(priv, pipe, NULL, 0); + if (ret) + return ret; + } + + return 0; +} + +static int max_ser_notify_bound(struct v4l2_async_notifier *nf, + struct v4l2_subdev *subdev, + struct v4l2_async_connection *base_asc) +{ + struct max_ser_priv *priv = nf_to_priv(nf); + struct max_serdes_asc *asc = asc_to_max(base_asc); + struct max_serdes_source *source = asc->source; + u32 pad = source->index; + int ret; + + ret = media_entity_get_fwnode_pad(&subdev->entity, + source->ep_fwnode, + MEDIA_PAD_FL_SOURCE); + if (ret < 0) { + dev_err(priv->dev, "Failed to find pad for %s\n", subdev->name); + return ret; + } + + source->sd = subdev; + source->pad = ret; + + ret = media_create_pad_link(&source->sd->entity, source->pad, + &priv->sd.entity, pad, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) { + dev_err(priv->dev, "Unable to link %s:%u -> %s:%u\n", + source->sd->name, source->pad, priv->sd.name, pad); + return ret; + } + + return 0; +} + +static void max_ser_notify_unbind(struct v4l2_async_notifier *nf, + struct v4l2_subdev *subdev, + struct v4l2_async_connection *base_asc) +{ + struct max_serdes_asc *asc = asc_to_max(base_asc); + struct max_serdes_source *source = asc->source; + + source->sd = NULL; +} + +static const struct v4l2_async_notifier_operations max_ser_notify_ops = { + .bound = max_ser_notify_bound, + .unbind = max_ser_notify_unbind, +}; + +static int max_ser_v4l2_notifier_register(struct max_ser_priv *priv) +{ + struct max_ser *ser = priv->ser; + unsigned int i; + int ret; + + v4l2_async_subdev_nf_init(&priv->nf, &priv->sd); + + for (i = 0; i < ser->ops->num_phys; i++) { + struct max_ser_phy *phy = &ser->phys[i]; + struct max_serdes_source *source; + struct max_serdes_asc *asc; + + source = max_ser_get_phy_source(priv, phy); + if (!source->ep_fwnode) + continue; + + asc = v4l2_async_nf_add_fwnode(&priv->nf, source->ep_fwnode, + struct max_serdes_asc); + if (IS_ERR(asc)) { + dev_err(priv->dev, + "Failed to add subdev for source %u: %pe", i, + asc); + + v4l2_async_nf_cleanup(&priv->nf); + + return PTR_ERR(asc); + } + + asc->source = source; + } + + priv->nf.ops = &max_ser_notify_ops; + + ret = v4l2_async_nf_register(&priv->nf); + if (ret) { + dev_err(priv->dev, "Failed to register subdev notifier"); + v4l2_async_nf_cleanup(&priv->nf); + return ret; + } + + return 0; +} + +static void max_ser_v4l2_notifier_unregister(struct max_ser_priv *priv) +{ + v4l2_async_nf_unregister(&priv->nf); + v4l2_async_nf_cleanup(&priv->nf); +} + +static int max_ser_v4l2_register(struct max_ser_priv *priv) +{ + struct v4l2_subdev *sd = &priv->sd; + struct max_ser *ser = priv->ser; + void *data = i2c_get_clientdata(priv->client); + unsigned int num_pads = max_ser_num_pads(ser); + unsigned int i; + int ret; + + v4l2_i2c_subdev_init(sd, priv->client, &max_ser_subdev_ops); + i2c_set_clientdata(priv->client, data); + sd->internal_ops = &max_ser_internal_ops; + sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + sd->entity.ops = &max_ser_media_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; + + priv->pads = devm_kcalloc(priv->dev, num_pads, sizeof(*priv->pads), GFP_KERNEL); + if (!priv->pads) + return -ENOMEM; + + for (i = 0; i < num_pads; i++) { + if (max_ser_pad_is_sink(ser, i)) + priv->pads[i].flags = MEDIA_PAD_FL_SINK; + else if (max_ser_pad_is_source(ser, i)) + priv->pads[i].flags = MEDIA_PAD_FL_SOURCE; + else if (max_ser_pad_is_tpg(ser, i)) + priv->pads[i].flags = MEDIA_PAD_FL_SINK | + MEDIA_PAD_FL_INTERNAL; + else + return -EINVAL; + } + + v4l2_set_subdevdata(sd, priv); + + if (ser->ops->tpg_patterns) { + v4l2_ctrl_handler_init(&priv->ctrl_handler, 1); + priv->sd.ctrl_handler = &priv->ctrl_handler; + + v4l2_ctrl_new_std_menu_items(&priv->ctrl_handler, + &max_ser_ctrl_ops, + V4L2_CID_TEST_PATTERN, + MAX_SERDES_TPG_PATTERN_MAX, + ~ser->ops->tpg_patterns, + __ffs(ser->ops->tpg_patterns), + max_serdes_tpg_patterns); + if (priv->ctrl_handler.error) { + ret = priv->ctrl_handler.error; + goto err_free_ctrl; + } + } + + ret = media_entity_pads_init(&sd->entity, num_pads, priv->pads); + if (ret) + goto err_free_ctrl; + + ret = max_ser_v4l2_notifier_register(priv); + if (ret) + goto err_media_entity_cleanup; + + ret = v4l2_subdev_init_finalize(sd); + if (ret) + goto err_nf_cleanup; + + ret = v4l2_async_register_subdev(sd); + if (ret) + goto err_sd_cleanup; + + return 0; + +err_sd_cleanup: + v4l2_subdev_cleanup(sd); +err_nf_cleanup: + max_ser_v4l2_notifier_unregister(priv); +err_media_entity_cleanup: + media_entity_cleanup(&sd->entity); +err_free_ctrl: + v4l2_ctrl_handler_free(&priv->ctrl_handler); + + return ret; +} + +static void max_ser_v4l2_unregister(struct max_ser_priv *priv) +{ + struct v4l2_subdev *sd = &priv->sd; + + max_ser_v4l2_notifier_unregister(priv); + v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); + media_entity_cleanup(&sd->entity); +} + +static int max_ser_parse_sink_dt_endpoint(struct max_ser_priv *priv, + struct max_ser_phy *phy, + struct max_serdes_source *source, + struct fwnode_handle *fwnode) +{ + struct max_ser *ser = priv->ser; + u32 pad = max_ser_phy_to_pad(ser, phy); + struct v4l2_fwnode_endpoint v4l2_ep = { .bus_type = V4L2_MBUS_CSI2_DPHY }; + struct fwnode_handle *ep; + int ret; + + ep = fwnode_graph_get_endpoint_by_id(fwnode, pad, 0, 0); + if (!ep) + return 0; + + source->ep_fwnode = fwnode_graph_get_remote_endpoint(ep); + if (!source->ep_fwnode) { + dev_err(priv->dev, + "Failed to get remote endpoint on port %u\n", pad); + return -EINVAL; + } + + ret = v4l2_fwnode_endpoint_parse(ep, &v4l2_ep); + fwnode_handle_put(ep); + if (ret) { + dev_err(priv->dev, "Could not parse endpoint on port %u\n", pad); + return ret; + } + + phy->mipi = v4l2_ep.bus.mipi_csi2; + phy->enabled = true; + + return 0; +} + +static int max_ser_find_phys_config(struct max_ser_priv *priv) +{ + struct max_ser *ser = priv->ser; + const struct max_serdes_phys_configs *configs = &ser->ops->phys_configs; + struct max_ser_phy *phy; + unsigned int i, j; + + if (!configs->num_configs) + return 0; + + for (i = 0; i < configs->num_configs; i++) { + const struct max_serdes_phys_config *config = &configs->configs[i]; + bool matching = true; + + for (j = 0; j < ser->ops->num_phys; j++) { + phy = &ser->phys[j]; + + if (!phy->enabled) + continue; + + if (phy->mipi.num_data_lanes <= config->lanes[j]) + continue; + + matching = false; + + break; + } + + if (matching) + break; + } + + if (i == configs->num_configs) { + dev_err(priv->dev, "Invalid lane configuration\n"); + return -EINVAL; + } + + ser->phys_config = i; + + return 0; +} + +static int max_ser_parse_dt(struct max_ser_priv *priv) +{ + struct fwnode_handle *fwnode = dev_fwnode(priv->dev); + struct max_ser *ser = priv->ser; + struct max_ser_pipe *pipe; + struct max_ser_phy *phy; + unsigned int i; + int ret; + + for (i = 0; i < ser->ops->num_phys; i++) { + phy = &ser->phys[i]; + phy->index = i; + } + + for (i = 0; i < ser->ops->num_pipes; i++) { + pipe = &ser->pipes[i]; + pipe->index = i; + pipe->phy_id = i % ser->ops->num_phys; + pipe->stream_id = i % MAX_SERDES_STREAMS_NUM; + } + + for (i = 0; i < ser->ops->num_phys; i++) { + struct max_ser_phy *phy = &ser->phys[i]; + struct max_serdes_source *source; + + source = max_ser_get_phy_source(priv, phy); + source->index = i; + + ret = max_ser_parse_sink_dt_endpoint(priv, phy, source, fwnode); + if (ret) + return ret; + } + + return max_ser_find_phys_config(priv); +} + +static int max_ser_allocate(struct max_ser_priv *priv) +{ + struct max_ser *ser = priv->ser; + unsigned int num_pads = max_ser_num_pads(ser); + + ser->phys = devm_kcalloc(priv->dev, ser->ops->num_phys, + sizeof(*ser->phys), GFP_KERNEL); + if (!ser->phys) + return -ENOMEM; + + ser->pipes = devm_kcalloc(priv->dev, ser->ops->num_pipes, + sizeof(*ser->pipes), GFP_KERNEL); + if (!ser->pipes) + return -ENOMEM; + + ser->vc_remaps = devm_kcalloc(priv->dev, ser->ops->num_vc_remaps, + sizeof(*ser->vc_remaps), GFP_KERNEL); + if (!ser->vc_remaps) + return -ENOMEM; + + ser->i2c_xlates = devm_kcalloc(priv->dev, ser->ops->num_i2c_xlates, + sizeof(*ser->i2c_xlates), GFP_KERNEL); + if (!ser->i2c_xlates) + return -ENOMEM; + + priv->sources = devm_kcalloc(priv->dev, ser->ops->num_phys, + sizeof(*priv->sources), GFP_KERNEL); + if (!priv->sources) + return -ENOMEM; + + priv->streams_masks = devm_kcalloc(priv->dev, num_pads, + sizeof(*priv->streams_masks), + GFP_KERNEL); + if (!priv->streams_masks) + return -ENOMEM; + + return 0; +} + +int max_ser_probe(struct i2c_client *client, struct max_ser *ser) +{ + struct device *dev = &client->dev; + struct max_ser_priv *priv; + int ret; + + if (ser->ops->num_phys > MAX_SER_NUM_PHYS) + return -E2BIG; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->client = client; + priv->dev = dev; + priv->ser = ser; + ser->priv = priv; + ser->mode = __ffs(ser->ops->modes); + + ret = max_ser_allocate(priv); + if (ret) + return ret; + + ret = max_ser_parse_dt(priv); + if (ret) + return ret; + + ret = max_ser_init(priv); + if (ret) + return ret; + + ret = max_ser_i2c_adapter_init(priv); + if (ret) + return ret; + + ret = max_ser_v4l2_register(priv); + if (ret) + goto err_i2c_adapter_deinit; + + return 0; + +err_i2c_adapter_deinit: + max_ser_i2c_adapter_deinit(priv); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(max_ser_probe, "MAX_SERDES"); + +int max_ser_remove(struct max_ser *ser) +{ + struct max_ser_priv *priv = ser->priv; + + max_ser_v4l2_unregister(priv); + + max_ser_i2c_adapter_deinit(priv); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(max_ser_remove, "MAX_SERDES"); + +int max_ser_set_double_bpps(struct v4l2_subdev *sd, u32 double_bpps) +{ + struct max_ser_priv *priv = sd_to_priv(sd); + + priv->double_bpps = double_bpps; + + return 0; +} + +int max_ser_set_stream_id(struct v4l2_subdev *sd, unsigned int stream_id) +{ + struct max_ser_priv *priv = sd_to_priv(sd); + struct max_ser *ser = priv->ser; + struct max_ser_pipe *pipe = &ser->pipes[0]; + + if (!ser->ops->set_pipe_stream_id) + return -EOPNOTSUPP; + + return ser->ops->set_pipe_stream_id(ser, pipe, stream_id); +} + +int max_ser_get_stream_id(struct v4l2_subdev *sd, unsigned int *stream_id) +{ + struct max_ser_priv *priv = sd_to_priv(sd); + struct max_ser *ser = priv->ser; + struct max_ser_pipe *pipe = &ser->pipes[0]; + + if (!ser->ops->get_pipe_stream_id) + return -EOPNOTSUPP; + + *stream_id = ser->ops->get_pipe_stream_id(ser, pipe); + + return 0; +} + +unsigned int max_ser_get_supported_modes(struct v4l2_subdev *sd) +{ + struct max_ser_priv *priv = sd_to_priv(sd); + struct max_ser *ser = priv->ser; + struct v4l2_subdev_state *state; + unsigned int modes = ser->ops->modes; + + state = v4l2_subdev_lock_and_get_active_state(&priv->sd); + + if (max_ser_is_tpg_routed(priv, state)) + modes = BIT(ser->ops->tpg_mode); + + v4l2_subdev_unlock_state(state); + + return modes; +} + +bool max_ser_supports_vc_remap(struct v4l2_subdev *sd) +{ + struct max_ser_priv *priv = sd_to_priv(sd); + struct max_ser *ser = priv->ser; + + return !!ser->ops->set_vc_remap; +} + +int max_ser_set_mode(struct v4l2_subdev *sd, enum max_serdes_gmsl_mode mode) +{ + struct max_ser_priv *priv = sd_to_priv(sd); + struct max_ser *ser = priv->ser; + int ret; + + if (!(ser->ops->modes & BIT(mode))) + return -EINVAL; + + if (ser->mode == mode) + return 0; + + if (ser->ops->set_tunnel_enable) { + bool tunnel_enable = mode == MAX_SERDES_GMSL_TUNNEL_MODE; + + ret = ser->ops->set_tunnel_enable(ser, tunnel_enable); + if (ret) + return ret; + } + + ser->mode = mode; + + return 0; +} + +int max_ser_set_vc_remaps(struct v4l2_subdev *sd, + struct max_serdes_vc_remap *vc_remaps, + int num_vc_remaps) +{ + struct max_ser_priv *priv = sd_to_priv(sd); + struct max_ser *ser = priv->ser; + unsigned int mask = 0; + unsigned int i; + int ret; + + if (!ser->ops->set_vc_remap) + return -EOPNOTSUPP; + + if (num_vc_remaps > ser->ops->num_vc_remaps) + return -E2BIG; + + for (i = 0; i < num_vc_remaps; i++) { + ret = ser->ops->set_vc_remap(ser, i, &vc_remaps[i]); + if (ret) + return ret; + + mask |= BIT(i); + } + + ret = ser->ops->set_vc_remaps_enable(ser, mask); + if (ret) + return ret; + + for (i = 0; i < num_vc_remaps; i++) + ser->vc_remaps[i] = vc_remaps[i]; + + ser->num_vc_remaps = num_vc_remaps; + + return 0; +} + +static int max_ser_read_reg(struct i2c_adapter *adapter, u8 addr, + u16 reg, u8 *val) +{ + u8 buf[2] = { reg >> 8, reg & 0xff }; + struct i2c_msg msg[2] = { + { + .addr = addr, + .flags = 0, + .buf = buf, + .len = sizeof(buf), + }, + { + .addr = addr, + .flags = I2C_M_RD, + .buf = buf, + .len = 1, + }, + }; + int ret; + + ret = i2c_transfer(adapter, msg, ARRAY_SIZE(msg)); + if (ret < 0) + return ret; + + *val = buf[0]; + + return 0; +} + +static int max_ser_write_reg(struct i2c_adapter *adapter, u8 addr, + u16 reg, u8 val) +{ + u8 buf[3] = { reg >> 8, reg & 0xff, val }; + struct i2c_msg msg[1] = { + { + .addr = addr, + .flags = 0, + .buf = buf, + .len = sizeof(buf), + }, + }; + int ret; + + ret = i2c_transfer(adapter, msg, ARRAY_SIZE(msg)); + if (ret < 0) + return ret; + + return 0; +} + +int max_ser_reset(struct i2c_adapter *adapter, u8 addr) +{ + int ret; + u8 val; + + ret = max_ser_read_reg(adapter, addr, MAX_SER_CTRL0, &val); + if (ret) + return ret; + + val |= MAX_SER_CTRL0_RESET_ALL; + + return max_ser_write_reg(adapter, addr, MAX_SER_CTRL0, val); +} + +int max_ser_wait_for_multiple(struct i2c_adapter *adapter, u8 *addrs, + unsigned int num_addrs, u8 *current_addr) +{ + unsigned int i, j; + int ret = 0; + u8 val; + + for (i = 0; i < 10; i++) { + for (j = 0; j < num_addrs; j++) { + ret = max_ser_read_reg(adapter, addrs[j], MAX_SER_REG0, &val); + if (!ret && val) { + *current_addr = addrs[j]; + return 0; + } + + msleep(100); + } + } + + return ret; +} + +int max_ser_wait(struct i2c_adapter *adapter, u8 addr) +{ + u8 current_addr; + + return max_ser_wait_for_multiple(adapter, &addr, 1, ¤t_addr); +} + +int max_ser_fix_tx_ids(struct i2c_adapter *adapter, u8 addr) +{ + unsigned int addr_regs[] = { + MAX_SER_CFGI_INFOFR_TR3, + MAX_SER_CFGL_SPI_TR3, + MAX_SER_CFGC_CC_TR3, + MAX_SER_CFGC_GPIO_TR3, + MAX_SER_CFGL_IIC_X_TR3, + MAX_SER_CFGL_IIC_Y_TR3, + }; + unsigned int i; + int ret; + + for (i = 0; i < ARRAY_SIZE(addr_regs); i++) { + ret = max_ser_write_reg(adapter, addr, addr_regs[i], addr); + if (ret) + return ret; + } + + return 0; +} + +int max_ser_change_address(struct i2c_adapter *adapter, u8 addr, u8 new_addr) +{ + u8 val = FIELD_PREP(MAX_SER_REG0_DEV_ADDR, new_addr); + + return max_ser_write_reg(adapter, addr, MAX_SER_REG0, val); +} + +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("I2C_ATR"); diff --git a/drivers/media/i2c/maxim-serdes/max_ser.h b/drivers/media/i2c/maxim-serdes/max_ser.h new file mode 100644 index 00000000000000..a9365be5e62dd8 --- /dev/null +++ b/drivers/media/i2c/maxim-serdes/max_ser.h @@ -0,0 +1,147 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2025 Analog Devices Inc. + */ + +#ifndef MAX_SER_H +#define MAX_SER_H + +#include + +#include + +#include "max_serdes.h" + +#define MAX_SER_REG0 0x0 +#define MAX_SER_REG0_DEV_ADDR GENMASK(7, 1) + +#define MAX_SER_CTRL0 0x10 +#define MAX_SER_CTRL0_RESET_ALL BIT(7) + +#define MAX_SER_CFGI_INFOFR_TR3 0x7b +#define MAX_SER_CFGL_SPI_TR3 0x83 +#define MAX_SER_CFGC_CC_TR3 0x8b +#define MAX_SER_CFGC_GPIO_TR3 0x93 +#define MAX_SER_CFGL_IIC_X_TR3 0xa3 +#define MAX_SER_CFGL_IIC_Y_TR3 0xab + +struct max_ser_phy { + unsigned int index; + struct v4l2_mbus_config_mipi_csi2 mipi; + bool enabled; + bool active; +}; + +struct max_ser_pipe_mode { + unsigned int soft_bpp; + unsigned int bpp; + bool dbl8; + bool dbl10; + bool dbl12; +}; + +struct max_ser_pipe { + unsigned int index; + unsigned int phy_id; + unsigned int stream_id; + unsigned int *dts; + unsigned int num_dts; + unsigned int vcs; + struct max_ser_pipe_mode mode; + bool enabled; +}; + +struct max_ser; + +struct max_ser_ops { + unsigned int modes; + unsigned int num_pipes; + unsigned int num_dts_per_pipe; + unsigned int num_phys; + unsigned int num_i2c_xlates; + unsigned int num_vc_remaps; + + struct max_serdes_phys_configs phys_configs; + struct max_serdes_tpg_entries tpg_entries; + enum max_serdes_gmsl_mode tpg_mode; + unsigned int tpg_patterns; + +#ifdef CONFIG_VIDEO_ADV_DEBUG + int (*reg_read)(struct max_ser *ser, unsigned int reg, unsigned int *val); + int (*reg_write)(struct max_ser *ser, unsigned int reg, unsigned int val); +#endif + int (*log_status)(struct max_ser *ser); + int (*log_pipe_status)(struct max_ser *ser, struct max_ser_pipe *pipe); + int (*log_phy_status)(struct max_ser *ser, struct max_ser_phy *phy); + int (*init)(struct max_ser *ser); + int (*set_i2c_xlate)(struct max_ser *ser, unsigned int i, + struct max_serdes_i2c_xlate *i2c_xlate); + int (*set_tunnel_enable)(struct max_ser *ser, bool enable); + int (*set_tpg)(struct max_ser *ser, const struct max_serdes_tpg_entry *entry); + int (*init_phy)(struct max_ser *ser, struct max_ser_phy *phy); + int (*set_phy_active)(struct max_ser *ser, struct max_ser_phy *phy, + bool enable); + int (*set_pipe_enable)(struct max_ser *ser, struct max_ser_pipe *pipe, + bool enable); + int (*set_pipe_dt)(struct max_ser *ser, struct max_ser_pipe *pipe, + unsigned int i, unsigned int dt); + int (*set_pipe_dt_en)(struct max_ser *ser, struct max_ser_pipe *pipe, + unsigned int i, bool enable); + int (*set_pipe_vcs)(struct max_ser *ser, struct max_ser_pipe *pipe, + unsigned int vcs); + int (*set_pipe_mode)(struct max_ser *ser, struct max_ser_pipe *pipe, + struct max_ser_pipe_mode *mode); + int (*set_vc_remap)(struct max_ser *ser, unsigned int i, + struct max_serdes_vc_remap *vc_remap); + int (*set_vc_remaps_enable)(struct max_ser *ser, unsigned int mask); + int (*set_pipe_stream_id)(struct max_ser *ser, struct max_ser_pipe *pipe, + unsigned int stream_id); + unsigned int (*get_pipe_stream_id)(struct max_ser *ser, struct max_ser_pipe *pipe); + int (*set_pipe_phy)(struct max_ser *ser, struct max_ser_pipe *pipe, + struct max_ser_phy *phy); +}; + +struct max_ser_priv; + +struct max_ser { + struct max_ser_priv *priv; + + const struct max_ser_ops *ops; + + struct max_serdes_i2c_xlate *i2c_xlates; + + struct max_ser_phy *phys; + struct max_ser_pipe *pipes; + const struct max_serdes_tpg_entry *tpg_entry; + enum max_serdes_tpg_pattern tpg_pattern; + + struct max_serdes_vc_remap *vc_remaps; + unsigned int num_vc_remaps; + + unsigned int phys_config; + unsigned int active; + enum max_serdes_gmsl_mode mode; +}; + +int max_ser_probe(struct i2c_client *client, struct max_ser *ser); + +int max_ser_remove(struct max_ser *ser); + +int max_ser_set_double_bpps(struct v4l2_subdev *sd, u32 double_bpps); +unsigned int max_ser_get_supported_modes(struct v4l2_subdev *sd); +int max_ser_set_mode(struct v4l2_subdev *sd, enum max_serdes_gmsl_mode mode); +bool max_ser_supports_vc_remap(struct v4l2_subdev *sd); +int max_ser_set_stream_id(struct v4l2_subdev *sd, unsigned int stream_id); +int max_ser_get_stream_id(struct v4l2_subdev *sd, unsigned int *stream_id); +int max_ser_set_vc_remaps(struct v4l2_subdev *sd, struct max_serdes_vc_remap *vc_remaps, + int num_vc_remaps); + +int max_ser_reset(struct i2c_adapter *adapter, u8 addr); +int max_ser_wait(struct i2c_adapter *adapter, u8 addr); +int max_ser_wait_for_multiple(struct i2c_adapter *adapter, u8 *addrs, + unsigned int num_addrs, u8 *current_addr); + +int max_ser_change_address(struct i2c_adapter *adapter, u8 addr, u8 new_addr); +int max_ser_fix_tx_ids(struct i2c_adapter *adapter, u8 addr); + +#endif // MAX_SER_H diff --git a/drivers/media/i2c/maxim-serdes/max_serdes.c b/drivers/media/i2c/maxim-serdes/max_serdes.c new file mode 100644 index 00000000000000..bed70b8ce99a44 --- /dev/null +++ b/drivers/media/i2c/maxim-serdes/max_serdes.c @@ -0,0 +1,413 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 Analog Devices Inc. + */ + +#include +#include +#include +#include + +#include + +#include