Getting /dev/video* to show up for an LKM

I’ve built a V4L2 driver based on kernel_source/drivers/media/i2c/ov5693.c

I attempted to make the module loadable by adding an i2c_new_device call to the __init function which correctly detects the device and calls probe. But a /dev/video* device is not being created. What do I need to do to make that happen?

Here are the contents of my __init function:

static struct i2c_client *daxc02_client;

static int __init daxc02_module_init(void)
        int retval;
        struct i2c_adapter *adapter;

        pr_info("daxc02: initializing driver.\n");

        retval = i2c_add_driver(&daxc02_i2c_driver);
        if(retval) return retval;

        adapter = i2c_get_adapter(6);
        if (!adapter) return -EINVAL;

        daxc02_client = i2c_new_device(adapter, &daxc02_camera_i2c_device);
        if (!daxc02_client) return -EINVAL;

        return 0;

And my probe:

static int daxc02_probe(struct i2c_client *client, const struct i2c_device_id *id)
	struct camera_common_data *common_data;
        struct mt9m021_platform_data *mt9m021_pdata = client->dev.platform_data;
	struct daxc02 *priv;
        struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
	char debugfs_name[10];
	int err;

	pr_info("daxc02: probing v4l2 sensor.\n");

	common_data = devm_kzalloc(&client->dev, sizeof(struct camera_common_data), GFP_KERNEL);
        if (!common_data) return -ENOMEM;

	priv = devm_kzalloc(&client->dev,
			    sizeof(struct daxc02) + sizeof(struct v4l2_ctrl *) *
        if (!priv) return -ENOMEM;

        priv->pdata = daxc02_parse_dt(client);
	if (!priv->pdata)
		dev_err(&client->dev, "unable to get platform data\n");
		return -EFAULT;

        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
            dev_warn(&client->dev, "i2c-adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
            return -EIO;

	common_data->numlanes = 4;
	common_data->csi_port = 2;
	common_data->ops		= &daxc02_common_ops;
	common_data->ctrl_handler	= &priv->ctrl_handler;
	common_data->i2c_client		= client;
	common_data->power		= &priv->power;
	common_data->ctrls		= priv->ctrls;
	common_data->priv		= (void *)priv;
	common_data->numctrls		= ARRAY_SIZE(ctrl_config_list);
	common_data->def_width		= MT9M021_PIXEL_ARRAY_WIDTH;
	common_data->def_height		= MT9M021_PIXEL_ARRAY_HEIGHT;
	common_data->fmt_width		= common_data->def_width;
	common_data->fmt_height		= common_data->def_height;
	common_data->def_clk_freq	= MT9M021_TARGET_FREQ;

	priv->i2c_client = client;
	priv->s_data			= common_data;
	priv->subdev			= &common_data->subdev;
	priv->subdev->dev		= &client->dev;
	priv->s_data->dev		= &client->dev;

        priv->subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;

        priv->mt9m021_pdata             = mt9m021_pdata;
        priv->crop.width                = MT9M021_WINDOW_WIDTH_MAX;
        priv->crop.height               = MT9M021_WINDOW_HEIGHT_MAX;
        priv->crop.left                 = MT9M021_COLUMN_START_DEF;
        priv->                  = MT9M021_ROW_START_DEF;

        priv->format.code               = V4L2_MBUS_FMT_SGRBG12_1X12;
        priv->format.width              = MT9M021_WINDOW_WIDTH_DEF;
        priv->format.height             = MT9M021_WINDOW_HEIGHT_DEF;
        priv->format.field              = V4L2_FIELD_NONE;
        priv->format.colorspace         = V4L2_COLORSPACE_SRGB;

	err = daxc02_power_get(priv);
	if (err) return err;

	sprintf(debugfs_name, "daxc02_%c", common_data->csi_port + 'a');
	dev_dbg(&client->dev, "%s: name %s\n", __func__, debugfs_name);
	camera_common_create_debugfs(common_data, debugfs_name);

	v4l2_i2c_subdev_init(priv->subdev, client, &daxc02_subdev_ops);

	err = daxc02_ctrls_init(priv);
	if (err) return err;

	priv->subdev->internal_ops = &mt9m021_subdev_internal_ops;
	priv->subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |

        #if defined(CONFIG_MEDIA_CONTROLLER)
	priv->pad.flags = MEDIA_PAD_FL_SOURCE;
	priv->subdev->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
	priv->subdev->entity.ops = &daxc02_media_ops;
	err = media_entity_init(&priv->subdev->entity, 1, &priv->pad, 0);
	if (err < 0)
		dev_err(&client->dev, "unable to init media entity\n");
		return err;

	err = v4l2_async_register_subdev(priv->subdev);
	if (err) return err;

	pr_info("daxc02: probe successful.\n");
	return 0;

Most likely a device tree issue. Did you create a new entry for your driver in the device tree?

I did not mess with the device tree except to pull out the numlanes, csi_port, and clk values I needed. I wasn’t sure if I needed to for a module.

Do I just make a new .dtsi file based on the ones that already exist? There are so many entries for the ov5693 that it’s hard to determine where to put my entry.

What role does video_register_device and the video_device structure play? I wondered if I needed to call that based on the instructions here (which are over 10 years old, so perhaps not the most relevant):


Edit: Found some helpful examples in this thread:

Yeah this device tree business is pretty complicated. I’m new to it myself so don’t fully understand how it works.
What i found is that by default only one of the sensors is enabled in the device tree.
You can use this entry as reference for your own device tree entry. Ofcourse you will have to put it in the right spot depending on which i2c bus you are using. Then there are some links you have to make under vi ports and cam modules.
Just grep the device tree for the enabled sensors label and you should be able to go from there.

You’re exactly right, a lot is either deleted or status = “disabled”

looks like these three blobs…

all have non-disabled stuff for OV5693.

Now I have to figure out what these camera-pcl profiles and tegra-camera-platform modules stuff is and if I need to fill that out or just stick with the i2c and vi parts.

I’d like to recommend you guys to read the release documentation,

Chapter ‘Sensor Driver Programming Guide’

The documentation I had must have been old, thanks Conan that’s exactly what I needed!