I am working on camera device driver for imx296 based Raspberry Pi Global Shutter Camera.
I am currently working on it as a loadable module. (using insmod)
kernel version - 5.15.148.
L4T R36.4.7
I started with using v4l2 lib for driver and then switched to tegracam based driver.
Its my first time working with camera driver.
there are two version
- One with v4l2 based lib. In that it is calling get_format function from some ops and after returning from function the kernel is crashing.
static int imx296_probe(struct i2c_client * const struct i2c_device_id *id)
{
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
struct imx296 *sensor;
int ret;
unsigned long clk_rate;
int i;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_warn(&adapter->dev,
"I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n");
return -EIO;
}
sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL);
if (!sensor)
return -ENOMEM;
sensor->dev = &client->dev;
i2c_set_clientdata(client, sensor);
clk_rate = 37125000;
for (i = 0; i < ARRAY_SIZE(imx296_clk_params); ++i) {
if (clk_rate == imx296_clk_params[i].freq) {
sensor->clk_params = &imx296_clk_params[i];
TRACE_PRINT(sensor->dev, "got valid clock");
break;
}
}
if (!sensor->clk_params) {
dev_err(sensor->dev, "unsupported clock rate %lu\n", clk_rate);
return -EINVAL;
}
ret = imx296_gpio_init(sensor);
if (ret < 0) {
dev_err(&client->dev, "GPIO init failed: %d\n", ret);
return ret;
}
sensor->regmap = devm_regmap_init_i2c(client, &imx296_regmap_config);
if (IS_ERR(sensor->regmap))
goto err_gpio;
ret = imx296_identify_model(sensor);
if (ret < 0)
goto err_gpio;
sensor->mode = &supported_modes[0];
/* Initialize the V4L2 subdev. */
ret = imx296_subdev_init(sensor);
if (ret < 0)
goto err_gpio;
/*
* Enable runtime PM. As the device has been powered manually, mark it
* as active, and increase the usage count without resuming the device.
*/
pm_runtime_set_active(sensor->dev);
pm_runtime_get_noresume(sensor->dev);
pm_runtime_enable(sensor->dev);
/* Register the V4L2 subdev. */
sensor->subdev.dev = &client->dev;
sensor->subdev.fwnode = client->dev.fwnode; // CRITICAL
dev_info(&client->dev, "Registering as: %s\n", sensor->subdev.name);
ret = v4l2_async_register_subdev(&sensor->subdev);
if (ret < 0)
goto err_pm;
/*
* Finally, enable autosuspend and decrease the usage count. The device
* will get suspended after the autosuspend delay, turning the power
* off.
*/
pm_runtime_set_autosuspend_delay(sensor->dev, 1000);
pm_runtime_use_autosuspend(sensor->dev);
pm_runtime_put_autosuspend(sensor->dev);
return 0;
err_pm:
pm_runtime_disable(sensor->dev);
pm_runtime_put_noidle(sensor->dev);
imx296_subdev_cleanup(sensor);
err_power:
imx296_power_off(sensor);
err_gpio:
imx296_gpio_cleanup(sensor);
return ret;
}
- Another one with tegracam library. In this version, the probe is crashing in tegracam_device_register func.
static int imx296_probe(struct i2c_client *client, const struct i2c_device_id *id) ;
static int imx296_probe(struct i2c_client *client, const struct i2c_device_id *id) {
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
struct imx296 *sensor;
struct tegracam_device *tc_dev;
int ret;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_warn(&adapter->dev, "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n");
return -EIO;
}
sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL);
if (!sensor) return -ENOMEM;
sensor->client = client;
sensor->dev = &client->dev;
TRACE_PRINT(sensor->dev, "before reg");
// Fix regulator names to match DT
sensor->avdd = devm_regulator_get(&client->dev, "imx296_avdd");
if (IS_ERR(sensor->avdd)) return PTR_ERR(sensor->avdd);
sensor->dvdd = devm_regulator_get(&client->dev, "imx296_dvdd");
if (IS_ERR(sensor->dvdd)) return PTR_ERR(sensor->dvdd);
sensor->iovdd = devm_regulator_get(&client->dev, "imx296_iovdd");
if (IS_ERR(sensor->iovdd)) return PTR_ERR(sensor->iovdd);
// Get clock
sensor->mclk = devm_clk_get(&client->dev, "extperiph1");
if (IS_ERR(sensor->mclk)) return PTR_ERR(sensor->mclk);
TRACE_PRINT(sensor->dev, "before gpio");
// Manual GPIO 486 init
ret = gpio_request(486, "imx296-reset");
if (ret < 0) return ret;
TRACE_PRINT(sensor->dev, "before gpio setting");
gpio_direction_output(486, 0); // Assert
usleep_range(20000, 30000);
gpio_set_value(486, 1); // Deassert
msleep(300); // Boot time
TRACE_PRINT(sensor->dev, "before i2c regmap");
sensor->regmap = devm_regmap_init_i2c(client, &imx296_regmap_config);
if (IS_ERR(sensor->regmap)) {
ret = PTR_ERR(sensor->regmap);
gpio_free(486);
return ret;
}
TRACE_PRINT(sensor->dev, "before identify model");
ret = imx296_identify_model(sensor);
if (ret < 0) {
gpio_free(486);
return ret;
}
TRACE_PRINT(sensor->dev, "before tc_dev alloc");
tc_dev = devm_kzalloc(&client->dev, sizeof(*tc_dev), GFP_KERNEL);
if (!tc_dev) {
gpio_free(486);
return -ENOMEM;
}
tc_dev->client = client;
tc_dev->dev = &client->dev;
tc_dev->dev_regmap_config = &imx296_regmap_config;
tc_dev->sensor_ops = &imx296_sensor_ops;
tc_dev->sensor_ops->numfrmfmts = ARRAY_SIZE(imx296_frmfmt);
tc_dev->tcctrl_ops = &imx296_ctrl_ops;
tc_dev->s_data = devm_kzalloc(&client->dev, sizeof(*tc_dev->s_data), GFP_KERNEL);
if (!tc_dev->s_data) {
gpio_free(486);
return -ENOMEM;
}
tc_dev->s_data->dev = &client->dev;
tc_dev->s_data->colorfmt = imx296_color_fmts;
tc_dev->s_data->numfmts = ARRAY_SIZE(imx296_color_fmts);
tc_dev->s_data->frmfmt = imx296_frmfmt;
tc_dev->s_data->numfmts = ARRAY_SIZE(imx296_frmfmt);
// Critical: Sensor props
tc_dev->s_data->sensor_props.sensor_modes = imx296_mode_tbl;
tc_dev->s_data->sensor_props.num_modes = ARRAY_SIZE(imx296_mode_tbl);
tc_dev->s_data->def_mode = 0;
tc_dev->s_data->def_width = IMX296_WIDTH;
tc_dev->s_data->def_height = IMX296_HEIGHT;
TRACE_PRINT(sensor->dev, "before client data");
i2c_set_clientdata(client, tc_dev);
TRACE_PRINT(sensor->dev, "before set private");
tegracam_set_privdata(tc_dev, (void *)sensor);
// Power on before register
ret = imx296_power_on(tc_dev->s_data);
if (ret) {
gpio_free(486);
return ret;
}
TRACE_PRINT(sensor->dev, "before register");
ret = tegracam_device_register(tc_dev);
if (ret) {
dev_err(&client->dev, "tegracam_device_register failed: %d\n", ret);
imx296_power_off(tc_dev->s_data);
gpio_free(486);
return ret;
}
imx296_power_off(tc_dev->s_data); // Off until streaming
return 0;
}
So i have two questions.
- Which one should i continue with, v4l2 or tegracam version?
- And how should i proceed with the decided version?