]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
media: pci: mgb4: Add support for GMSL1 modules
authorMartin Tůma <martin.tuma@digiteqautomotive.com>
Fri, 16 Jan 2026 12:55:53 +0000 (13:55 +0100)
committerHans Verkuil <hverkuil+cisco@kernel.org>
Sat, 17 Jan 2026 08:08:02 +0000 (09:08 +0100)
Add support for GMSL1 modules. GMSL1 modules have 2 inputs, 2 outputs and
require a separate FW (id #3).

Signed-off-by: Martin Tůma <martin.tuma@digiteqautomotive.com>
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
drivers/media/pci/mgb4/mgb4_core.c
drivers/media/pci/mgb4/mgb4_core.h
drivers/media/pci/mgb4/mgb4_sysfs.h
drivers/media/pci/mgb4/mgb4_sysfs_in.c
drivers/media/pci/mgb4/mgb4_sysfs_out.c
drivers/media/pci/mgb4/mgb4_vin.c
drivers/media/pci/mgb4/mgb4_vout.c

index 3ce6b717ca32048e230da1635c52a43a28ab8e63..a7cb8dc50b53c20306bbcdf77a3ba6b37982079d 100644 (file)
@@ -381,6 +381,18 @@ static int get_serial_number(struct mgb4_dev *mgbdev)
        return 0;
 }
 
+static const char *module_type_str(struct mgb4_dev *mgbdev)
+{
+       if (MGB4_IS_FPDL3(mgbdev))
+               return "FPDL3";
+       else if (MGB4_IS_GMSL3(mgbdev))
+               return "GMSL3";
+       else if (MGB4_IS_GMSL1(mgbdev))
+               return "GMSL1";
+       else
+               return "UNKNOWN";
+}
+
 static int get_module_version(struct mgb4_dev *mgbdev)
 {
        struct device *dev = &mgbdev->pdev->dev;
@@ -402,19 +414,21 @@ static int get_module_version(struct mgb4_dev *mgbdev)
        }
 
        mgbdev->module_version = ~((u32)version) & 0xff;
-       if (!(MGB4_IS_FPDL3(mgbdev) || MGB4_IS_GMSL(mgbdev))) {
+       if (!(MGB4_IS_FPDL3(mgbdev) ||
+             MGB4_IS_GMSL3(mgbdev) ||
+             MGB4_IS_GMSL1(mgbdev))) {
                dev_err(dev, "unknown module type\n");
                return -EINVAL;
        }
        fw_version = mgb4_read_reg(&mgbdev->video, 0xC4) >> 24;
        if ((MGB4_IS_FPDL3(mgbdev) && fw_version != 1) ||
-           (MGB4_IS_GMSL(mgbdev) && fw_version != 2)) {
+           (MGB4_IS_GMSL3(mgbdev) && fw_version != 2) ||
+           (MGB4_IS_GMSL1(mgbdev) && fw_version != 3)) {
                dev_err(dev, "module/firmware type mismatch\n");
                return -EINVAL;
        }
 
-       dev_info(dev, "%s module detected\n",
-                MGB4_IS_FPDL3(mgbdev) ? "FPDL3" : "GMSL");
+       dev_info(dev, "%s module detected\n", module_type_str(mgbdev));
 
        return 0;
 }
index cc24068400a29054c365e5e4fee2a899548364f5..06ef8bb4408955a2db4dc0f1d62e2897192dc700 100644 (file)
 #define MGB4_VIN_DEVICES  2
 #define MGB4_VOUT_DEVICES 2
 
-#define MGB4_IS_GMSL(mgbdev) \
+#define MGB4_IS_GMSL1(mgbdev) \
+       (((mgbdev)->module_version >> 4) == 6)
+#define MGB4_IS_GMSL3(mgbdev) \
        ((((mgbdev)->module_version >> 4) >= 2) && \
         (((mgbdev)->module_version >> 4) <= 4))
 #define MGB4_IS_FPDL3(mgbdev) \
        (((mgbdev)->module_version >> 4) == 1)
 #define MGB4_HAS_VOUT(mgbdev) \
-       ((((mgbdev)->module_version >> 4) >= 1) && \
-        (((mgbdev)->module_version >> 4) <= 3))
+       (((((mgbdev)->module_version >> 4) >= 1) && \
+         (((mgbdev)->module_version >> 4) <= 3)) || \
+        ((((mgbdev)->module_version >> 4) == 6)))
 
 struct mgb4_dma_channel {
        struct dma_chan *chan;
index 017d82c0624e920de76f891a3881a9a3d3f8d263..fa7fd232c2c258e5141b2ff32a7480459291ba95 100644 (file)
 
 extern struct attribute *mgb4_pci_attrs[];
 extern struct attribute *mgb4_fpdl3_in_attrs[];
-extern struct attribute *mgb4_gmsl_in_attrs[];
+extern struct attribute *mgb4_gmsl3_in_attrs[];
+extern struct attribute *mgb4_gmsl1_in_attrs[];
 extern struct attribute *mgb4_fpdl3_out_attrs[];
-extern struct attribute *mgb4_gmsl_out_attrs[];
+extern struct attribute *mgb4_gmsl3_out_attrs[];
+extern struct attribute *mgb4_gmsl1_out_attrs[];
 
 #endif
index 9626fa59e3d3e62f66b18161f709f6e0a9526cfd..4cd4addcd0a572168827203dcb1338b770f1afd3 100644 (file)
@@ -36,10 +36,13 @@ static ssize_t oldi_lane_width_show(struct device *dev,
        u32 config;
        int ret;
 
-       i2c_reg = MGB4_IS_GMSL(mgbdev) ? 0x1CE : 0x49;
-       i2c_mask = MGB4_IS_GMSL(mgbdev) ? 0x0E : 0x03;
-       i2c_single_val = MGB4_IS_GMSL(mgbdev) ? 0x00 : 0x02;
-       i2c_dual_val = MGB4_IS_GMSL(mgbdev) ? 0x0E : 0x00;
+       if (MGB4_IS_GMSL1(mgbdev))
+               return sprintf(buf, "0\n");
+
+       i2c_reg = MGB4_IS_GMSL3(mgbdev) ? 0x1CE : 0x49;
+       i2c_mask = MGB4_IS_GMSL3(mgbdev) ? 0x0E : 0x03;
+       i2c_single_val = MGB4_IS_GMSL3(mgbdev) ? 0x00 : 0x02;
+       i2c_dual_val = MGB4_IS_GMSL3(mgbdev) ? 0x0E : 0x00;
 
        mutex_lock(&mgbdev->i2c_lock);
        ret = mgb4_i2c_read_byte(&vindev->deser, i2c_reg);
@@ -79,21 +82,24 @@ static ssize_t oldi_lane_width_store(struct device *dev,
        if (ret)
                return ret;
 
+       if (MGB4_IS_GMSL1(mgbdev))
+               return val ? -EINVAL : count;
+
        switch (val) {
        case 0: /* single */
                fpga_data = 0;
-               i2c_data = MGB4_IS_GMSL(mgbdev) ? 0x00 : 0x02;
+               i2c_data = MGB4_IS_GMSL3(mgbdev) ? 0x00 : 0x02;
                break;
        case 1: /* dual */
                fpga_data = 1U << 9;
-               i2c_data = MGB4_IS_GMSL(mgbdev) ? 0x0E : 0x00;
+               i2c_data = MGB4_IS_GMSL3(mgbdev) ? 0x0E : 0x00;
                break;
        default:
                return -EINVAL;
        }
 
-       i2c_reg = MGB4_IS_GMSL(mgbdev) ? 0x1CE : 0x49;
-       i2c_mask = MGB4_IS_GMSL(mgbdev) ? 0x0E : 0x03;
+       i2c_reg = MGB4_IS_GMSL3(mgbdev) ? 0x1CE : 0x49;
+       i2c_mask = MGB4_IS_GMSL3(mgbdev) ? 0x0E : 0x03;
 
        mutex_lock(&mgbdev->i2c_lock);
        ret = mgb4_i2c_mask_byte(&vindev->deser, i2c_reg, i2c_mask, i2c_data);
@@ -102,7 +108,7 @@ static ssize_t oldi_lane_width_store(struct device *dev,
                return -EIO;
        mgb4_mask_reg(&mgbdev->video, vindev->config->regs.config, 1U << 9,
                      fpga_data);
-       if (MGB4_IS_GMSL(mgbdev)) {
+       if (MGB4_IS_GMSL3(mgbdev)) {
                /* reset input link */
                mutex_lock(&mgbdev->i2c_lock);
                ret = mgb4_i2c_mask_byte(&vindev->deser, 0x10, 1U << 5, 1U << 5);
@@ -745,7 +751,7 @@ struct attribute *mgb4_fpdl3_in_attrs[] = {
        NULL
 };
 
-struct attribute *mgb4_gmsl_in_attrs[] = {
+struct attribute *mgb4_gmsl3_in_attrs[] = {
        &dev_attr_input_id.attr,
        &dev_attr_link_status.attr,
        &dev_attr_stream_status.attr,
@@ -770,3 +776,26 @@ struct attribute *mgb4_gmsl_in_attrs[] = {
        &dev_attr_gmsl_fec.attr,
        NULL
 };
+
+struct attribute *mgb4_gmsl1_in_attrs[] = {
+       &dev_attr_input_id.attr,
+       &dev_attr_link_status.attr,
+       &dev_attr_stream_status.attr,
+       &dev_attr_video_width.attr,
+       &dev_attr_video_height.attr,
+       &dev_attr_hsync_status.attr,
+       &dev_attr_vsync_status.attr,
+       &dev_attr_oldi_lane_width.attr,
+       &dev_attr_color_mapping.attr,
+       &dev_attr_hsync_gap_length.attr,
+       &dev_attr_vsync_gap_length.attr,
+       &dev_attr_pclk_frequency.attr,
+       &dev_attr_hsync_width.attr,
+       &dev_attr_vsync_width.attr,
+       &dev_attr_hback_porch.attr,
+       &dev_attr_hfront_porch.attr,
+       &dev_attr_vback_porch.attr,
+       &dev_attr_vfront_porch.attr,
+       &dev_attr_frequency_range.attr,
+       NULL
+};
index 573aa61c69d4b9ad3fc56b2dc857262450506817..5769f3ca6c2ff610bd43d91ce0f326a1f2361bdf 100644 (file)
@@ -665,6 +665,7 @@ static ssize_t pclk_frequency_store(struct device *dev,
 {
        struct video_device *vdev = to_video_device(dev);
        struct mgb4_vout_dev *voutdev = video_get_drvdata(vdev);
+       struct mgb4_dev *mgbdev = voutdev->mgbdev;
        unsigned long val;
        int ret;
        unsigned int dp;
@@ -679,14 +680,16 @@ static ssize_t pclk_frequency_store(struct device *dev,
                return -EBUSY;
        }
 
-       dp = (val > 50000) ? 1 : 0;
+       dp = (MGB4_IS_FPDL3(mgbdev) && val > 50000) ? 1 : 0;
        voutdev->freq = mgb4_cmt_set_vout_freq(voutdev, val >> dp) << dp;
-
-       mgb4_mask_reg(&voutdev->mgbdev->video, voutdev->config->regs.config,
-                     0x10, dp << 4);
-       mutex_lock(&voutdev->mgbdev->i2c_lock);
-       ret = mgb4_i2c_mask_byte(&voutdev->ser, 0x4F, 1 << 6, ((~dp) & 1) << 6);
-       mutex_unlock(&voutdev->mgbdev->i2c_lock);
+       mgb4_mask_reg(&mgbdev->video, voutdev->config->regs.config, 0x10,
+                     dp << 4);
+       if (MGB4_IS_FPDL3(mgbdev)) {
+               mutex_lock(&mgbdev->i2c_lock);
+               ret = mgb4_i2c_mask_byte(&voutdev->ser, 0x4F, 1 << 6,
+                                        ((~dp) & 1) << 6);
+               mutex_unlock(&mgbdev->i2c_lock);
+       }
 
        mutex_unlock(voutdev->vdev.lock);
 
@@ -731,7 +734,7 @@ struct attribute *mgb4_fpdl3_out_attrs[] = {
        NULL
 };
 
-struct attribute *mgb4_gmsl_out_attrs[] = {
+struct attribute *mgb4_gmsl3_out_attrs[] = {
        &dev_attr_output_id.attr,
        &dev_attr_video_source.attr,
        &dev_attr_display_width.attr,
@@ -739,3 +742,22 @@ struct attribute *mgb4_gmsl_out_attrs[] = {
        &dev_attr_frame_rate.attr,
        NULL
 };
+
+struct attribute *mgb4_gmsl1_out_attrs[] = {
+       &dev_attr_output_id.attr,
+       &dev_attr_video_source.attr,
+       &dev_attr_display_width.attr,
+       &dev_attr_display_height.attr,
+       &dev_attr_frame_rate.attr,
+       &dev_attr_hsync_polarity.attr,
+       &dev_attr_vsync_polarity.attr,
+       &dev_attr_de_polarity.attr,
+       &dev_attr_pclk_frequency.attr,
+       &dev_attr_hsync_width.attr,
+       &dev_attr_vsync_width.attr,
+       &dev_attr_hback_porch.attr,
+       &dev_attr_hfront_porch.attr,
+       &dev_attr_vback_porch.attr,
+       &dev_attr_vfront_porch.attr,
+       NULL
+};
index 4b38076486ffcf1bf0ad2ea9adde4834db91c76f..be55491b52b2427b19a4684e58e2ae1b9dd034cd 100644 (file)
@@ -32,7 +32,8 @@
 #include "mgb4_vin.h"
 
 ATTRIBUTE_GROUPS(mgb4_fpdl3_in);
-ATTRIBUTE_GROUPS(mgb4_gmsl_in);
+ATTRIBUTE_GROUPS(mgb4_gmsl3_in);
+ATTRIBUTE_GROUPS(mgb4_gmsl1_in);
 
 static const struct mgb4_vin_config vin_cfg[] = {
        {0, 0, 0, 6, {0x10, 0x00, 0x04, 0x08, 0x1C, 0x14, 0x18, 0x20, 0x24, 0x28, 0xE8}},
@@ -44,23 +45,31 @@ static const struct i2c_board_info fpdl3_deser_info[] = {
        {I2C_BOARD_INFO("deserializer2", 0x36)},
 };
 
-static const struct i2c_board_info gmsl_deser_info[] = {
+static const struct i2c_board_info gmsl3_deser_info[] = {
        {I2C_BOARD_INFO("deserializer1", 0x4C)},
        {I2C_BOARD_INFO("deserializer2", 0x2A)},
 };
 
+static const struct i2c_board_info gmsl1_deser_info[] = {
+       {I2C_BOARD_INFO("deserializer1", 0x2C)},
+       {I2C_BOARD_INFO("deserializer2", 0x6C)},
+};
+
 static const struct mgb4_i2c_kv fpdl3_i2c[] = {
        {0x06, 0xFF, 0x04}, {0x07, 0xFF, 0x01}, {0x45, 0xFF, 0xE8},
        {0x49, 0xFF, 0x00}, {0x34, 0xFF, 0x00}, {0x23, 0xFF, 0x00}
 };
 
-static const struct mgb4_i2c_kv gmsl_i2c[] = {
+static const struct mgb4_i2c_kv gmsl3_i2c[] = {
        {0x01, 0x03, 0x03}, {0x300, 0x0C, 0x0C}, {0x03, 0xC0, 0xC0},
        {0x1CE, 0x0E, 0x0E}, {0x11, 0x05, 0x00}, {0x05, 0xC0, 0x40},
        {0x307, 0x0F, 0x00}, {0xA0, 0x03, 0x00}, {0x3E0, 0x07, 0x07},
        {0x308, 0x01, 0x01}, {0x10, 0x20, 0x20}, {0x300, 0x40, 0x40}
 };
 
+static const struct mgb4_i2c_kv gmsl1_i2c[] = {
+};
+
 static const struct v4l2_dv_timings_cap video_timings_cap = {
        .type = V4L2_DV_BT_656_1120,
        .bt = {
@@ -796,22 +805,29 @@ static irqreturn_t err_handler(int irq, void *ctx)
 
 static int deser_init(struct mgb4_vin_dev *vindev, int id)
 {
-       int rv, addr_size;
-       size_t values_count;
-       const struct mgb4_i2c_kv *values;
-       const struct i2c_board_info *info;
+       int rv, addr_size = 0;
+       size_t count = 0;
+       const struct mgb4_i2c_kv *values = NULL;
+       const struct i2c_board_info *info = NULL;
        struct device *dev = &vindev->mgbdev->pdev->dev;
 
-       if (MGB4_IS_GMSL(vindev->mgbdev)) {
-               info = &gmsl_deser_info[id];
+       if (MGB4_IS_GMSL3(vindev->mgbdev)) {
+               info = &gmsl3_deser_info[id];
                addr_size = 16;
-               values = gmsl_i2c;
-               values_count = ARRAY_SIZE(gmsl_i2c);
-       } else {
+               values = gmsl3_i2c;
+               count = ARRAY_SIZE(gmsl3_i2c);
+       } else if (MGB4_IS_FPDL3(vindev->mgbdev)) {
                info = &fpdl3_deser_info[id];
                addr_size = 8;
                values = fpdl3_i2c;
-               values_count = ARRAY_SIZE(fpdl3_i2c);
+               count = ARRAY_SIZE(fpdl3_i2c);
+       } else if (MGB4_IS_GMSL1(vindev->mgbdev)) {
+               info = &gmsl1_deser_info[id];
+               addr_size = 8;
+               values = gmsl1_i2c;
+               count = ARRAY_SIZE(gmsl1_i2c);
+       } else {
+               return -EINVAL;
        }
 
        rv = mgb4_i2c_init(&vindev->deser, vindev->mgbdev->i2c_adap, info,
@@ -820,7 +836,7 @@ static int deser_init(struct mgb4_vin_dev *vindev, int id)
                dev_err(dev, "failed to create deserializer\n");
                return rv;
        }
-       rv = mgb4_i2c_configure(&vindev->deser, values, values_count);
+       rv = mgb4_i2c_configure(&vindev->deser, values, count);
        if (rv < 0) {
                dev_err(dev, "failed to configure deserializer\n");
                goto err_i2c_dev;
@@ -838,11 +854,12 @@ static void fpga_init(struct mgb4_vin_dev *vindev)
 {
        struct mgb4_regs *video = &vindev->mgbdev->video;
        const struct mgb4_vin_regs *regs = &vindev->config->regs;
+       int dp = MGB4_IS_GMSL1(vindev->mgbdev) ? 0 : 1;
 
        mgb4_write_reg(video, regs->config, 0x00000001);
        mgb4_write_reg(video, regs->sync, 0x03E80002);
        mgb4_write_reg(video, regs->padding, 0x00000000);
-       mgb4_write_reg(video, regs->config, 1U << 9);
+       mgb4_write_reg(video, regs->config, dp << 9);
 }
 
 static void create_debugfs(struct mgb4_vin_dev *vindev)
@@ -890,10 +907,21 @@ static void create_debugfs(struct mgb4_vin_dev *vindev)
 #endif
 }
 
+static const struct attribute_group **module_groups(struct mgb4_dev *mgbdev)
+{
+       if (MGB4_IS_FPDL3(mgbdev))
+               return mgb4_fpdl3_in_groups;
+       else if (MGB4_IS_GMSL3(mgbdev))
+               return mgb4_gmsl3_in_groups;
+       else if (MGB4_IS_GMSL1(mgbdev))
+               return mgb4_gmsl1_in_groups;
+       else
+               return NULL;
+}
+
 struct mgb4_vin_dev *mgb4_vin_create(struct mgb4_dev *mgbdev, int id)
 {
        int rv;
-       const struct attribute_group **groups;
        struct mgb4_vin_dev *vindev;
        struct pci_dev *pdev = mgbdev->pdev;
        struct device *dev = &pdev->dev;
@@ -914,14 +942,13 @@ struct mgb4_vin_dev *mgb4_vin_create(struct mgb4_dev *mgbdev, int id)
        INIT_WORK(&vindev->dma_work, dma_transfer);
        INIT_WORK(&vindev->err_work, signal_change);
 
-       /* IRQ callback */
+       /* IRQ callbacks */
        vin_irq = xdma_get_user_irq(mgbdev->xdev, vindev->config->vin_irq);
        rv = request_irq(vin_irq, vin_handler, 0, "mgb4-vin", vindev);
        if (rv) {
                dev_err(dev, "failed to register vin irq handler\n");
                goto err_alloc;
        }
-       /* Error IRQ callback */
        err_irq = xdma_get_user_irq(mgbdev->xdev, vindev->config->err_irq);
        rv = request_irq(err_irq, err_handler, 0, "mgb4-err", vindev);
        if (rv) {
@@ -986,9 +1013,7 @@ struct mgb4_vin_dev *mgb4_vin_create(struct mgb4_dev *mgbdev, int id)
        }
 
        /* Module sysfs attributes */
-       groups = MGB4_IS_GMSL(mgbdev)
-         ? mgb4_gmsl_in_groups : mgb4_fpdl3_in_groups;
-       rv = device_add_groups(&vindev->vdev.dev, groups);
+       rv = device_add_groups(&vindev->vdev.dev, module_groups(mgbdev));
        if (rv) {
                dev_err(dev, "failed to create sysfs attributes\n");
                goto err_video_dev;
@@ -1014,7 +1039,6 @@ err_alloc:
 
 void mgb4_vin_free(struct mgb4_vin_dev *vindev)
 {
-       const struct attribute_group **groups;
        int vin_irq = xdma_get_user_irq(vindev->mgbdev->xdev,
                                        vindev->config->vin_irq);
        int err_irq = xdma_get_user_irq(vindev->mgbdev->xdev,
@@ -1025,9 +1049,7 @@ void mgb4_vin_free(struct mgb4_vin_dev *vindev)
        free_irq(vin_irq, vindev);
        free_irq(err_irq, vindev);
 
-       groups = MGB4_IS_GMSL(vindev->mgbdev)
-         ? mgb4_gmsl_in_groups : mgb4_fpdl3_in_groups;
-       device_remove_groups(&vindev->vdev.dev, groups);
+       device_remove_groups(&vindev->vdev.dev, module_groups(vindev->mgbdev));
 
        mgb4_i2c_free(&vindev->deser);
        video_unregister_device(&vindev->vdev);
index fd93fbbaf755592c80571b6202c249de87a3fb07..44e9565d4d06727ff88e2bfe273dc3440cbfaa03 100644 (file)
@@ -25,7 +25,8 @@
 #include "mgb4_vout.h"
 
 ATTRIBUTE_GROUPS(mgb4_fpdl3_out);
-ATTRIBUTE_GROUPS(mgb4_gmsl_out);
+ATTRIBUTE_GROUPS(mgb4_gmsl3_out);
+ATTRIBUTE_GROUPS(mgb4_gmsl1_out);
 
 static const struct mgb4_vout_config vout_cfg[] = {
        {0, 0, 8, {0x78, 0x60, 0x64, 0x68, 0x74, 0x6C, 0x70, 0x7C, 0xE0}},
@@ -37,10 +38,18 @@ static const struct i2c_board_info fpdl3_ser_info[] = {
        {I2C_BOARD_INFO("serializer2", 0x16)},
 };
 
+static const struct i2c_board_info gmsl1_ser_info[] = {
+       {I2C_BOARD_INFO("serializer1", 0x24)},
+       {I2C_BOARD_INFO("serializer2", 0x22)},
+};
+
 static const struct mgb4_i2c_kv fpdl3_i2c[] = {
        {0x05, 0xFF, 0x04}, {0x06, 0xFF, 0x01}, {0xC2, 0xFF, 0x80}
 };
 
+static const struct mgb4_i2c_kv gmsl1_i2c[] = {
+};
+
 static const struct v4l2_dv_timings_cap video_timings_cap = {
        .type = V4L2_DV_BT_656_1120,
        .bt = {
@@ -634,12 +643,24 @@ static irqreturn_t handler(int irq, void *ctx)
 
 static int ser_init(struct mgb4_vout_dev *voutdev, int id)
 {
-       int rv;
-       const struct i2c_board_info *info = &fpdl3_ser_info[id];
        struct mgb4_i2c_client *ser = &voutdev->ser;
        struct device *dev = &voutdev->mgbdev->pdev->dev;
+       const struct i2c_board_info *info = NULL;
+       const struct mgb4_i2c_kv *values = NULL;
+       size_t count = 0;
+       int rv;
+
+       if (MGB4_IS_FPDL3(voutdev->mgbdev)) {
+               info = &fpdl3_ser_info[id];
+               values = fpdl3_i2c;
+               count = ARRAY_SIZE(fpdl3_i2c);
+       } else if (MGB4_IS_GMSL1(voutdev->mgbdev)) {
+               info = &gmsl1_ser_info[id];
+               values = gmsl1_i2c;
+               count = ARRAY_SIZE(gmsl1_i2c);
+       }
 
-       if (MGB4_IS_GMSL(voutdev->mgbdev))
+       if (!info)
                return 0;
 
        rv = mgb4_i2c_init(ser, voutdev->mgbdev->i2c_adap, info, 8);
@@ -647,7 +668,7 @@ static int ser_init(struct mgb4_vout_dev *voutdev, int id)
                dev_err(dev, "failed to create serializer\n");
                return rv;
        }
-       rv = mgb4_i2c_configure(ser, fpdl3_i2c, ARRAY_SIZE(fpdl3_i2c));
+       rv = mgb4_i2c_configure(ser, values, count);
        if (rv < 0) {
                dev_err(dev, "failed to configure serializer\n");
                goto err_i2c_dev;
@@ -665,18 +686,19 @@ static void fpga_init(struct mgb4_vout_dev *voutdev)
 {
        struct mgb4_regs *video = &voutdev->mgbdev->video;
        const struct mgb4_vout_regs *regs = &voutdev->config->regs;
+       int dp = MGB4_IS_GMSL1(voutdev->mgbdev) ? 0 : 1;
+       u32 source = (voutdev->config->id + MGB4_VIN_DEVICES) << 2;
 
-       mgb4_write_reg(video, regs->config, 0x00000011);
+       mgb4_write_reg(video, regs->config, 0x00000001);
        mgb4_write_reg(video, regs->resolution, (1280 << 16) | 640);
        mgb4_write_reg(video, regs->hsync, 0x00283232);
        mgb4_write_reg(video, regs->vsync, 0x40141F1E);
        mgb4_write_reg(video, regs->frame_limit, MGB4_HW_FREQ / 60);
        mgb4_write_reg(video, regs->padding, 0x00000000);
 
-       voutdev->freq = mgb4_cmt_set_vout_freq(voutdev, 61150 >> 1) << 1;
+       voutdev->freq = mgb4_cmt_set_vout_freq(voutdev, 61150 >> dp) << dp;
 
-       mgb4_write_reg(video, regs->config,
-                      (voutdev->config->id + MGB4_VIN_DEVICES) << 2 | 1 << 4);
+       mgb4_write_reg(video, regs->config, source | dp << 4);
 }
 
 static void create_debugfs(struct mgb4_vout_dev *voutdev)
@@ -720,10 +742,21 @@ static void create_debugfs(struct mgb4_vout_dev *voutdev)
 #endif
 }
 
+static const struct attribute_group **module_groups(struct mgb4_dev *mgbdev)
+{
+       if (MGB4_IS_FPDL3(mgbdev))
+               return mgb4_fpdl3_out_groups;
+       else if (MGB4_IS_GMSL3(mgbdev))
+               return mgb4_gmsl3_out_groups;
+       else if (MGB4_IS_GMSL1(mgbdev))
+               return mgb4_gmsl1_out_groups;
+       else
+               return NULL;
+}
+
 struct mgb4_vout_dev *mgb4_vout_create(struct mgb4_dev *mgbdev, int id)
 {
        int rv, irq;
-       const struct attribute_group **groups;
        struct mgb4_vout_dev *voutdev;
        struct pci_dev *pdev = mgbdev->pdev;
        struct device *dev = &pdev->dev;
@@ -804,9 +837,7 @@ struct mgb4_vout_dev *mgb4_vout_create(struct mgb4_dev *mgbdev, int id)
        }
 
        /* Module sysfs attributes */
-       groups = MGB4_IS_GMSL(mgbdev)
-         ? mgb4_gmsl_out_groups : mgb4_fpdl3_out_groups;
-       rv = device_add_groups(&voutdev->vdev.dev, groups);
+       rv = device_add_groups(&voutdev->vdev.dev, module_groups(mgbdev));
        if (rv) {
                dev_err(dev, "failed to create sysfs attributes\n");
                goto err_video_dev;
@@ -830,15 +861,10 @@ err_alloc:
 
 void mgb4_vout_free(struct mgb4_vout_dev *voutdev)
 {
-       const struct attribute_group **groups;
        int irq = xdma_get_user_irq(voutdev->mgbdev->xdev, voutdev->config->irq);
 
        free_irq(irq, voutdev);
-
-       groups = MGB4_IS_GMSL(voutdev->mgbdev)
-         ? mgb4_gmsl_out_groups : mgb4_fpdl3_out_groups;
-       device_remove_groups(&voutdev->vdev.dev, groups);
-
+       device_remove_groups(&voutdev->vdev.dev, module_groups(voutdev->mgbdev));
        mgb4_i2c_free(&voutdev->ser);
        video_unregister_device(&voutdev->vdev);
        v4l2_device_unregister(&voutdev->v4l2dev);