Hello,
I have a working custom camera( MIPI-CSI2: 2Lane) connected to the Jetson TX2 camera expansion header.
pixel format is RAW10, framerate are 30fps/60fps, and the resolution are 2608x1960 and 1304x980.
I’ve built a device tree and a driver for camera based on the Sensor Driver Programming Guide.
I failed to be loaded device driver. ( it’s not appears a /dev/video0 entry)
-
how to debugging device tree and device driver?
It is hard to find where the device tree failed to load from.
How do I enable debugging messages in kernel code? -
I modified from OV5693 code. but custom camera sensor doesn’t eeprom.
I modified tegra186-quill-camera-plugin-manager.dtsi
as Sensor Driver Programming Guide document.
"Using Plugin Manager
If your camera module has onboard EEPROM and has a valid camera ID programmed, Plugin Manager can
be used. If your device module does not meet this requirement, use the main platform device tree instead by
following the directions in Using Main Platform Device Tree File. "Shouldn’t the Plug-in manager.dtsi file be used for sensors without EEPROM?
here is modified tegra186-quill-camera-plugin-manager.dtsi.
/* s5k5e8 camera board */
fragment-e3327@0 {
ids = "3327-*";
/* Camera 0: Module */
override@0 {
target = <&e3327_cam0>;
_overlay_ {
status = "okay";
};
};
override@1 {
target = <&cam_module0>;
_overlay_ {
status = "okay";
badge = "3327_front_s5k5e8";
position = "rear";
orientation = "1";
};
};
override@2 {
target = <&cam_module0_drivernode0>;
_overlay_ {
status = "okay";
pcl_id = "v4l2_sensor";
devname = "s5k5e8 2-0020";
proc-device-tree = "/proc/device-tree/i2c@3180000/s5k5e8_c@20";
};
};
/* Camera 0: VI */
override@3 {
target = <&vi_base>;
_overlay_ {
num-channels=<1>;
};
};
override@4 {
target = <&vi_port0>;
_overlay_ {
status = "okay";
};
};
override@5 {
target = <&e3327_vi_in0>;
_overlay_ {
status = "okay";
port-index = <0>;
bus-width = <2>;
remote-endpoint = <&e3327_csi_out0>;
};
};
/* Camera 0: CSI */
override@6 {
target = <&csi_chan0>;
_overlay_ {
status = "okay";
};
};
override@7 {
target = <&csi_chan0_port0>;
_overlay_ {
status = "okay";
};
};
override@8 {
target = <&e3327_csi_in0>;
_overlay_ {
status = "okay";
port-index = <0>;
bus-width = <2>;
remote-endpoint = <&e3327_s5k5e8_out0>;
};
};
override@9 {
target = <&csi_chan0_port1>;
_overlay_ {
status = "okay";
};
};
override@10 {
target = <&e3327_csi_out0>;
_overlay_ {
status = "okay";
remote-endpoint = <&e3327_vi_in0>;
};
};
/* tegra-camera-platform settings */
override@11 {
target = <&tcp>;
_overlay_ {
num_csi_lanes = <2>;
max_lane_speed = <1500000>;
min_bits_per_pixel = <10>;
vi_peak_byte_per_pixel = <2>;
vi_bw_margin_pct = <25>;
isp_peak_byte_per_pixel = <5>;
isp_bw_margin_pct = <25>;
};
};
/* GPIO */
override@12 {
target = <&{/gpio@2200000}>;
_overlay_ {
camera-control-input {
status = "disabled";
};
camera-control-output-low {
gpio-hog;
gpios = <CAM1_RST_L 0 CAM0_PWDN 0>;
label = "cam0-rst", "cam0-pwdn";
output-low;
status = "okay";
};
camera-control-output-high {
status = "disabled";
};
};
};
};
here is device tree custom camera sensor
/ {
host1x {
vi@15700000 {
num-channels = <1>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
e3327_vi_in0: endpoint {
port-index = <2>;
bus-width = <2>;
remote-endpoint = <&e3327_csi_out0>;
};
};
};
};
nvcsi@150c0000 {
num-channels = <1>;
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
e3327_csi_in0: endpoint@0 {
port-index = <2>;
bus-width = <2>;
remote-endpoint = <&e3327_s5k5e8_out0>;
};
};
port@1 {
reg = <1>;
e3327_csi_out0: endpoint@1 {
remote-endpoint = <&e3327_vi_in0>;
};
};
};
};
};
};
i2c@3180000 {
s5k5e8_c@20 {
compatible = "nvidia,s5k5e8";
/* I2C device address */
reg = <0x20>;
/* V4L2 device node location */
devnode = "video0";
/* Physical dimensions of sensor */
physical_w = "4.675";
physical_h = "3.633";
/* Define any required hw resources needed by driver */
/* ie. clocks, io pins, power sources */
avdd-reg = "vana";
iovdd-reg = "vif";
/* Sensor output flip settings */
vertical-flip = "true";
mode0 { // S5K5E8_MODE_2608X1960
mclk_khz = "24000";
num_lanes = "2";
tegra_sinterface = "serial_c";
phy_mode = "DPHY";
discontinuous_clk = "no";
dpcm_enable = "false";
cil_settletime = "0";
active_w = "2608";
active_h = "1960";
mode_type = "bayer";
pixel_phase = "grbg";
csi_pixel_bit_depth = "10";
readout_orientation = "90";
line_length = "2856";
inherent_gain = "1";
mclk_multiplier = "7";
pix_clk_hz = "170000000";
gain_factor = "10";
min_gain_val = "1";/* 1DB*/
max_gain_val = "16";/* 16DB*/
step_gain_val = "1";
default_gain = "10";
min_hdr_ratio = "";
max_hdr_ratio = "";
framerate_factor = "1000000";
min_framerate = "2000000";/*1.816577 */
max_framerate = "30000000";/*30*/
step_framerate = "1";
default_framerate = "30000000";
exposure_factor = "1000000";
min_exp_time = "34";/* us */
max_exp_time = "279384";/* us */
step_exp_time = "1";
default_exp_time = "33334";/* us */
embedded_metadata_height = "0";
};
mode1 { //S5K5E8_MODE_1304X980
mclk_khz = "24000";
num_lanes = "2";
tegra_sinterface = "serial_c";
phy_mode = "DPHY";
discontinuous_clk = "no";
dpcm_enable = "false";
cil_settletime = "0";
active_w = "1340";
active_h = "980";
mode_type = "bayer";
pixel_phase = "grbg";
csi_pixel_bit_depth = "10";
readout_orientation = "90";
line_length = "2856";
inherent_gain = "1";
mclk_multiplier = "7";
pix_clk_hz = "170000000";
gain_factor = "10";
min_gain_val = "1";/* 1DB*/
max_gain_val = "16";/* 16DB*/
step_gain_val = "1";
default_gain = "10";
min_hdr_ratio = "";
max_hdr_ratio = "";
framerate_factor = "1000000";
min_framerate = "1816577";/*1.816577 */
max_framerate = "60000000";/*30*/
step_framerate = "1";
default_framerate = "60000000";
exposure_factor = "1000000";
min_exp_time = "34";/* us */
max_exp_time = "279384";/* us */
step_exp_time = "1";
default_exp_time = "33334";/* us */
embedded_metadata_height = "0";
};
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
e3327_s5k5e8_out0: endpoint {
port-index = <2>;
bus-width = <2>;
remote-endpoint = <&e3327_csi_in0>;
};
};
};
};
};
tegra-camera-platform {
compatible = "nvidia, tegra-camera-platform";
num_csi_lanes = <2>;
max_lane_speed = <1500000>;
min_bits_per_pixel = <10>;
vi_peak_byte_per_pixel = <2>;
vi_bw_margin_pct = <25>;
max_pixel_rate = <170000>;
isp_peak_byte_per_pixel = <5>;
isp_bw_margin_pct = <25>;
/**
* The general guideline for naming badge_info contains 3 parts, and is as follows,
* The first part is the camera_board_id for the module; if the module is in a FFD
* platform, then use the platform name for this part.
* The second part contains the position of the module, ex. “rear” or “front”.
* The third part contains the last 6 characters of a part number which is found
* in the module's specsheet from the vender.
*/
modules {
module0 {
badge = "e3327_front_s5k5e8";
position = "rear";
orientation = "1";
drivernode0 {
/* Declare PCL support driver (classically known as guid) */
pcl_id = "v4l2_sensor";
/* Driver v4l2 device name */
devname = "s5k5e8 2-0020";
/* Declare the device-tree hierarchy to driver instance */
proc-device-tree = "/proc/device-tree/i2c@3180000/s5k5e8_c@20";
};
};
};
};
};
here is custom device driver code in <b>/drivers/media/i2c</b>
static const u32 ctrl_cid_list[] = {
TEGRA_CAMERA_CID_GAIN,
TEGRA_CAMERA_CID_EXPOSURE,
TEGRA_CAMERA_CID_FRAME_RATE,
TEGRA_CAMERA_CID_GROUP_HOLD,
};
struct s5k5e8 {
// struct camera_common_eeprom_data eeprom[S5K5E8_EEPROM_NUM_BLOCKS];
// u8 eeprom_buf[S5K5E8_EEPROM_SIZE];
// u8 otp_buf[S5K5E8_OTP_SIZE];
struct i2c_client *i2c_client;
struct v4l2_subdev *subdev;
u8 fuse_id[S5K5E8_FUSE_ID_SIZE];
const char *devname;
struct dentry *debugfs_dir;
struct mutex streaming_lock;
bool streaming;
s32 group_hold_prev;
u32 frame_length;
bool group_hold_en;
struct camera_common_i2c i2c_dev;
struct camera_common_data *s_data;
struct tegracam_device *tc_dev;
};
static struct regmap_config s5k5e8_regmap_config = {
.reg_bits = 16,
.val_bits = 8,
};
static inline void s5k5e8_get_frame_length_regs(s5k5e8_reg *regs,
u32 frame_length)
{
regs->addr = S5K5E8_FRAME_LENGTH_ADDR_MSB; //0x0340
regs->val = (frame_length >> 8) & 0xff;
(regs + 1)->addr = S5K5E8_FRAME_LENGTH_ADDR_LSB; //0x0341
(regs + 1)->val = (frame_length) & 0xff;
}
static inline void s5k5e8_get_coarse_time_regs(s5k5e8_reg *regs,
u32 coarse_time)
{
regs->addr = S5K5E8_COARSE_TIME_ADDR_MSB; //0x0202
regs->val = (coarse_time >> 8) & 0xff;
(regs + 1)->addr = S5K5E8_COARSE_TIME_ADDR_LSB; //0x0203
(regs + 1)->val = (coarse_time ) & 0xff;
}
static inline void s5k5e8_get_gain_regs(s5k5e8_reg *regs,
u16 gain)
{
regs->addr = S5K5E8_GAIN_ADDR_MSB; //0x0204
regs->val = (gain >> 8) & 0xff;
(regs + 1)->addr = S5K5E8_GAIN_ADDR_LSB; //0x0205
(regs + 1)->val = (gain) & 0xff;
}
static int test_mode;
module_param(test_mode, int, 0644); //0644 ???
static inline int s5k5e8_read_reg(struct camera_common_data *s_data,
u16 addr, u8 *val)
{
int err = 0;
u32 reg_val = 0;
err = regmap_read(s_data->regmap, addr, ®_val);
*val = reg_val & 0xFF;
return err;
}
static int s5k5e8_write_reg(struct camera_common_data *s_data, u16 addr, u8 val)
{
int err;
struct device *dev = s_data->dev;
err = regmap_write(s_data->regmap, addr, val);
if (err)
dev_err(dev, "%s: i2c write failed, 0x%x = %x\n",
__func__, addr, val);
return err;
}
static int s5k5e8_write_table(struct s5k5e8 *priv,
const s5k5e8_reg table[])
{
struct camera_common_data *s_data = priv->s_data;
return regmap_util_write_table_8(s_data->regmap,
table,
NULL, 0,
S5K5E8_TABLE_WAIT_MS,
S5K5E8_TABLE_END);
}
static void s5k5e8_gpio_set(struct camera_common_data *s_data,
unsigned int gpio, int val)
{
struct camera_common_pdata *pdata = s_data->pdata;
if (pdata && pdata->use_cam_gpio)
cam_gpio_ctrl(s_data->dev, gpio, val, 1);
else {
if (gpio_cansleep(gpio))
gpio_set_value_cansleep(gpio, val);
else
gpio_set_value(gpio, val);
}
}
static int s5k5e8_power_on(struct camera_common_data *s_data)
{
int err = 0;
struct camera_common_power_rail *pw = s_data->power;
struct camera_common_pdata *pdata = s_data->pdata;
struct device *dev = s_data->dev;
dev_dbg(dev, "%s: power on\n", __func__);
if (pdata && pdata->power_on) {
err = pdata->power_on(pw);
if (err)
dev_err(dev, "%s failed.\n", __func__);
else
pw->state = SWITCH_ON;
return err;
}
/* sleeps calls in the sequence below are for internal device
* signal propagation as specified by sensor vendor
*/
if (pw->avdd)
err = regulator_enable(pw->avdd);
if (err)
goto s5k5e8_avdd_fail;
if (pw->iovdd)
err = regulator_enable(pw->iovdd);
if (err)
goto s5k5e8_iovdd_fail;
usleep_range(1, 2);
if (gpio_is_valid(pw->pwdn_gpio))
s5k5e8_gpio_set(s_data, pw->pwdn_gpio, 1);
/*
* datasheet 2.9: reset requires ~2ms settling time
* a power on reset is generated after core power becomes stable
*/
usleep_range(2000, 2010);
if (gpio_is_valid(pw->reset_gpio))
s5k5e8_gpio_set(s_data, pw->reset_gpio, 1);
/* datasheet fig 2-9: t3 */
usleep_range(2000, 2010);
pw->state = SWITCH_ON;
return 0;
s5k5e8_iovdd_fail:
regulator_disable(pw->avdd);
s5k5e8_avdd_fail:
dev_err(dev, "%s failed.\n", __func__);
return -ENODEV;
}
static int s5k5e8_power_off(struct camera_common_data *s_data)
{
int err = 0;
struct camera_common_power_rail *pw = s_data->power;
struct device *dev = s_data->dev;
struct camera_common_pdata *pdata = s_data->pdata;
dev_dbg(dev, "%s: power off\n", __func__);
if (pdata && pdata->power_off) {
err = pdata->power_off(pw);
if (!err) {
goto power_off_done;
} else {
dev_err(dev, "%s failed.\n", __func__);
return err;
}
}
/* sleeps calls in the sequence below are for internal device
* signal propagation as specified by sensor vendor
*/
usleep_range(21, 25);
if (gpio_is_valid(pw->pwdn_gpio))
s5k5e8_gpio_set(s_data, pw->pwdn_gpio, 0);
usleep_range(1, 2);
if (gpio_is_valid(pw->reset_gpio))
s5k5e8_gpio_set(s_data, pw->reset_gpio, 0);
/* datasheet 2.9: reset requires ~2ms settling time*/
usleep_range(2000, 2010);
if (pw->iovdd)
regulator_disable(pw->iovdd);
if (pw->avdd)
regulator_disable(pw->avdd);
power_off_done:
pw->state = SWITCH_OFF;
return 0;
}
static int s5k5e8_power_put(struct tegracam_device *tc_dev)
{
struct camera_common_data *s_data = tc_dev->s_data;
struct camera_common_power_rail *pw = s_data->power;
struct camera_common_pdata *pdata = s_data->pdata;
struct device *dev = tc_dev->dev;
if (unlikely(!pw))
return -EFAULT;
if (pdata && pdata->use_cam_gpio)
cam_gpio_deregister(dev, pw->pwdn_gpio);
else {
if (gpio_is_valid(pw->pwdn_gpio))
gpio_free(pw->pwdn_gpio);
if (gpio_is_valid(pw->reset_gpio))
gpio_free(pw->reset_gpio);
}
return 0;
}
static int s5k5e8_power_get(struct tegracam_device *tc_dev)
{
struct camera_common_data *s_data = tc_dev->s_data;
struct camera_common_power_rail *pw = s_data->power;
struct camera_common_pdata *pdata = s_data->pdata;
struct device *dev = tc_dev->dev;
const char *mclk_name;
const char *parentclk_name;
struct clk *parent;
int err = 0, ret = 0;
if (!pdata) {
dev_err(dev, "pdata missing\n");
return -EFAULT;
}
mclk_name = pdata->mclk_name ?
pdata->mclk_name : "cam_mclk1";
pw->mclk = devm_clk_get(dev, mclk_name);
if (IS_ERR(pw->mclk)) {
dev_err(dev, "unable to get clock %s\n", mclk_name);
return PTR_ERR(pw->mclk);
}
parentclk_name = pdata->parentclk_name;
if (parentclk_name) {
parent = devm_clk_get(dev, parentclk_name);
if (IS_ERR(parent)) {
dev_err(dev, "unable to get parent clcok %s",
parentclk_name);
} else
clk_set_parent(pw->mclk, parent);
}
/* analog 2.8v */
err |= camera_common_regulator_get(dev,
&pw->avdd, pdata->regulators.avdd);
/* IO 1.8v */
err |= camera_common_regulator_get(dev,
&pw->iovdd, pdata->regulators.iovdd);
if (!err) {
pw->reset_gpio = pdata->reset_gpio;
pw->pwdn_gpio = pdata->pwdn_gpio;
}
if (pdata->use_cam_gpio) {
err = cam_gpio_register(dev, pw->pwdn_gpio);
if (err)
dev_err(dev, "%s ERR can't register cam gpio %u!\n",
__func__, pw->pwdn_gpio);
} else {
if (gpio_is_valid(pw->pwdn_gpio)) {
ret = gpio_request(pw->pwdn_gpio, "cam_pwdn_gpio");
if (ret < 0) {
dev_dbg(dev, "%s can't request pwdn_gpio %d\n",
__func__, ret);
}
gpio_direction_output(pw->pwdn_gpio, 1);
}
if (gpio_is_valid(pw->reset_gpio)) {
ret = gpio_request(pw->reset_gpio, "cam_reset_gpio");
if (ret < 0) {
dev_dbg(dev, "%s can't request reset_gpio %d\n",
__func__, ret);
}
gpio_direction_output(pw->reset_gpio, 1);
}
}
pw->state = SWITCH_OFF;
return err;
}
static int s5k5e8_set_gain(struct tegracam_device *tc_dev, s64 val);
static int s5k5e8_set_frame_rate(struct tegracam_device *tc_dev, s64 val);
//static int s5k5e8_set_exposure(struct tegracam_device *tc_dev, s64 val);
//static int s5k5e8_set_exposure_short(struct tegracam_device *tc_dev, s64 val);
static const struct of_device_id s5k5e8_of_match[] = {
{
.compatible = "nvidia,s5k5e8", },
{ },
};
static int s5k5e8_set_group_hold(struct tegracam_device *tc_dev, bool val)
{
int err;
struct s5k5e8 *priv = tc_dev->priv;
int gh_prev = switch_ctrl_qmenu[priv->group_hold_prev];
struct device *dev = tc_dev->dev;
if (priv->group_hold_en == true && gh_prev == SWITCH_OFF) {
camera_common_i2c_aggregate(&priv->i2c_dev, true);
/* enter group hold */
err = s5k5e8_write_reg(priv->s_data,
S5K5E8_GROUP_HOLD_ADDR, val); //0x0104
if (err)
goto fail;
priv->group_hold_prev = 1;
dev_dbg(dev, "%s: enter group hold\n", __func__);
} else if (priv->group_hold_en == false && gh_prev == SWITCH_ON) {
/* leave group hold */
err = s5k5e8_write_reg(priv->s_data,
S5K5E8_GROUP_HOLD_ADDR, 0x00);
if (err)
goto fail;
err = s5k5e8_write_reg(priv->s_data,
S5K5E8_GROUP_HOLD_ADDR, 0x00);
if (err)
goto fail;
camera_common_i2c_aggregate(&priv->i2c_dev, false);
priv->group_hold_prev = 0;
dev_dbg(dev, "%s: leave group hold\n", __func__);
}
return 0;
fail:
dev_dbg(dev, "%s: Group hold control error\n", __func__);
return err;
}
static int s5k5e8_set_gain(struct tegracam_device *tc_dev, s64 val)
{
struct camera_common_data *s_data = tc_dev->s_data;
struct s5k5e8 *priv = (struct s5k5e8 *)tc_dev->priv;
struct device *dev = tc_dev->dev;
const struct sensor_mode_properties *mode =
&s_data->sensor_props.sensor_modes[s_data->mode_prop_idx];
s5k5e8_reg reg_list[2];
int err;
u16 gain;
int i;
if (!priv->group_hold_prev)
s5k5e8_set_group_hold(tc_dev, 1);
/* translate value */
gain = (u16) (((val * 16) +
(mode->control_properties.gain_factor / 2)) /
mode->control_properties.gain_factor);
s5k5e8_get_gain_regs(reg_list, gain);
dev_dbg(dev, "%s: gain %d val: %lld\n", __func__, gain, val);
for (i = 0; i < 2; i++) {
err = s5k5e8_write_reg(s_data, reg_list[i].addr,
reg_list[i].val);
if (err)
goto fail;
}
return 0;
fail:
dev_dbg(dev, "%s: GAIN control error\n", __func__);
return err;
}
static int s5k5e8_set_frame_rate(struct tegracam_device *tc_dev, s64 val)
{
struct camera_common_data *s_data = tc_dev->s_data;
struct device *dev = tc_dev->dev;
struct s5k5e8 *priv = tc_dev->priv;
const struct sensor_mode_properties *mode =
&s_data->sensor_props.sensor_modes[s_data->mode_prop_idx];
s5k5e8_reg reg_list[2];
int err;
u32 frame_length;
int i;
if (!priv->group_hold_prev)
s5k5e8_set_group_hold(tc_dev, 1);
frame_length = mode->signal_properties.pixel_clock.val *
mode->control_properties.framerate_factor /
mode->image_properties.line_length / val;
s5k5e8_get_frame_length_regs(reg_list, frame_length);
dev_dbg(dev, "%s: val: %d\n", __func__, frame_length);
for (i = 0; i < 2; i++) {
err = s5k5e8_write_reg(s_data, reg_list[i].addr,
reg_list[i].val);
if (err)
goto fail;
}
priv->frame_length = frame_length;
return 0;
fail:
dev_dbg(dev, "%s: FRAME_LENGTH control error\n", __func__);
return err;
}
static int s5k5e8_set_exposure(struct tegracam_device *tc_dev, s64 val)
{
struct camera_common_data *s_data = tc_dev->s_data;
struct device *dev = tc_dev->dev;
struct s5k5e8 *priv = tc_dev->priv;
const s32 max_coarse_time = priv->frame_length - S5K5E8_MAX_COARSE_DIFF;
const struct sensor_mode_properties *mode =
&s_data->sensor_props.sensor_modes[s_data->mode_prop_idx];
s5k5e8_reg reg_list[2];
int err;
u32 coarse_time;
int i;
if (!priv->group_hold_prev)
s5k5e8_set_group_hold(tc_dev, 1);
coarse_time = (u32)(((mode->signal_properties.pixel_clock.val*val)
/mode->image_properties.line_length)/
mode->control_properties.exposure_factor);
if (coarse_time < S5K5E8_MIN_EXPOSURE_COARSE)
coarse_time = S5K5E8_MIN_EXPOSURE_COARSE;
else if (coarse_time > max_coarse_time)
coarse_time = max_coarse_time;
s5k5e8_get_coarse_time_regs(reg_list, coarse_time);
dev_dbg(dev, "%s: val: %d\n", __func__, coarse_time);
for (i = 0; i < 2; i++) {
err = s5k5e8_write_reg(s_data, reg_list[i].addr,
reg_list[i].val);
if (err)
goto fail;
}
return 0;
fail:
dev_dbg(dev, "%s: COARSE_TIME control error\n", __func__);
return err;
}
MODULE_DEVICE_TABLE(of, s5k5e8_of_match);
static struct camera_common_pdata *s5k5e8_parse_dt(struct tegracam_device
*tc_dev)
{
struct device *dev = tc_dev->dev;
struct device_node *node = dev->of_node;
struct camera_common_pdata *board_priv_pdata;
const struct of_device_id *match;
int gpio;
int err;
struct camera_common_pdata *ret = NULL;
dev_err(dev, "s5k5e8 enter_parse_dt\n");
if (!node){
return NULL;
}
match = of_match_device(s5k5e8_of_match, dev);
if (!match) {
dev_err(dev, "Failed to find matching dt id\n");
return NULL;
}
board_priv_pdata = devm_kzalloc(dev,
sizeof(*board_priv_pdata), GFP_KERNEL);
if (!board_priv_pdata)
return NULL;
err = camera_common_parse_clocks(dev,
board_priv_pdata);
if (err) {
dev_err(dev, "Failed to find clocks\n");
goto error;
}
gpio = of_get_named_gpio(node, "pwdn-gpios", 0);
if (gpio < 0) {
if (gpio == -EPROBE_DEFER) {
ret = ERR_PTR(-EPROBE_DEFER);
goto error;
}
gpio = 0;
}
board_priv_pdata->pwdn_gpio = (unsigned int)gpio;
gpio = of_get_named_gpio(node, "reset-gpios", 0);
if (gpio < 0) {
/* reset-gpio is not absolutely needed */
if (gpio == -EPROBE_DEFER) {
ret = ERR_PTR(-EPROBE_DEFER);
goto error;
}
dev_dbg(dev, "reset gpios not in DT\n");
gpio = 0;
}
board_priv_pdata->reset_gpio = (unsigned int)gpio;
board_priv_pdata->use_cam_gpio =
of_property_read_bool(node, "cam, use-cam-gpio");
err = of_property_read_string(node, "avdd-reg",
&board_priv_pdata->regulators.avdd);
if (err) {
dev_err(dev, "avdd-reg not in DT\n");
goto error;
}
err = of_property_read_string(node, "iovdd-reg",
&board_priv_pdata->regulators.iovdd);
if (err) {
dev_err(dev, "iovdd-reg not in DT\n");
goto error;
}
// board_priv_pdata->has_eeprom =
// of_property_read_bool(node, "has-eeprom");
board_priv_pdata->v_flip = of_property_read_bool(node, "vertical-flip");
board_priv_pdata->h_mirror = of_property_read_bool(node,
"horizontal-mirror");
return board_priv_pdata;
error:
devm_kfree(dev, board_priv_pdata);
return ret;
}
static int s5k5e8_set_mode(struct tegracam_device *tc_dev)
{
struct s5k5e8 *priv = (struct s5k5e8 *)tegracam_get_privdata(tc_dev);
struct camera_common_data *s_data = tc_dev->s_data;
int err;
err = s5k5e8_write_table(priv, mode_table[s_data->mode_prop_idx]);
if (err)
return err;
return 0;
}
static int s5k5e8_start_streaming(struct tegracam_device *tc_dev)
{
struct s5k5e8 *priv = (struct s5k5e8 *)tegracam_get_privdata(tc_dev);
struct camera_common_data *s_data = tc_dev->s_data;
// struct camera_common_pdata *pdata = s_data->pdata;
struct device *dev = s_data->dev;
int err;
// u8 val;
mutex_lock(&priv->streaming_lock);
err = s5k5e8_write_table(priv, mode_table[S5K5E8_MODE_START_STREAM]);
if (err) {
mutex_unlock(&priv->streaming_lock);
goto exit;
} else {
priv->streaming = true;
mutex_unlock(&priv->streaming_lock);
}
/*
if (pdata->v_flip) {
s5k5e8_read_reg(s_data, S5K5E8_TIMING_REG20, &val);
s5k5e8_write_reg(s_data, S5K5E8_TIMING_REG20,
val | VERTICAL_FLIP);
}
if (pdata->h_mirror) {
s5k5e8_read_reg(s_data, S5K5E8_TIMING_REG21, &val);
s5k5e8_write_reg(s_data, S5K5E8_TIMING_REG21,
val | HORIZONTAL_MIRROR_MASK);
} else {
s5k5e8_read_reg(s_data, OV5693_TIMING_REG21, &val);
s5k5e8_write_reg(s_data, OV5693_TIMING_REG21,
val & (~HORIZONTAL_MIRROR_MASK));
}
*/
if (test_mode)
err = s5k5e8_write_table(priv,
mode_table[S5K5E8_MODE_TEST_PATTERN]);
return 0;
exit:
dev_err(dev, "%s: error starting stream\n", __func__);
return err;
}
static int s5k5e8_stop_streaming(struct tegracam_device *tc_dev)
{
struct camera_common_data *s_data = tc_dev->s_data;
struct s5k5e8 *priv = (struct s5k5e8 *)tegracam_get_privdata(tc_dev);
struct device *dev = s_data->dev;
u32 frame_time;
int err;
mutex_lock(&priv->streaming_lock);
err = s5k5e8_write_table(priv,
mode_table[S5K5E8_MODE_STOP_STREAM]);
if (err) {
mutex_unlock(&priv->streaming_lock);
goto exit;
} else {
priv->streaming = false;
mutex_unlock(&priv->streaming_lock);
}
/*
* Wait for one frame to make sure sensor is set to
* software standby in V-blank
*
* frame_time = frame length rows * Tline
* Tline = line length / pixel clock (in MHz)
*/
frame_time = priv->frame_length *
S5K5E8_DEFAULT_LINE_LENGTH / S5K5E8_DEFAULT_PIXEL_CLOCK;
usleep_range(frame_time, frame_time + 1000);
return 0;
exit:
dev_err(dev, "%s: error stopping stream\n", __func__);
return err;
}
static struct camera_common_sensor_ops s5k5e8_common_ops = {
.numfrmfmts = ARRAY_SIZE(s5k5e8_frmfmt),
.frmfmt_table = s5k5e8_frmfmt,
.power_on = s5k5e8_power_on,
.power_off = s5k5e8_power_off,
.write_reg = s5k5e8_write_reg,
.read_reg = s5k5e8_read_reg,
.parse_dt = s5k5e8_parse_dt,
.power_get = s5k5e8_power_get,
.power_put = s5k5e8_power_put,
.set_mode = s5k5e8_set_mode,
.start_streaming = s5k5e8_start_streaming,
.stop_streaming = s5k5e8_stop_streaming,
};
static int s5k5e8_debugfs_streaming_show(void *data, u64 *val)
{
struct s5k5e8 *priv = data;
mutex_lock(&priv->streaming_lock);
*val = priv->streaming;
mutex_unlock(&priv->streaming_lock);
return 0;
}
static int s5k5e8_debugfs_streaming_write(void *data, u64 val)
{
int err = 0;
struct s5k5e8 *priv = data;
struct i2c_client *client = priv->i2c_client;
bool enable = (val != 0);
int mode_index = enable ?
(S5K5E8_MODE_START_STREAM) : (S5K5E8_MODE_STOP_STREAM);
dev_info(&client->dev, "%s: %s sensor\n",
__func__, (enable ? "enabling" : "disabling"));
mutex_lock(&priv->streaming_lock);
err = s5k5e8_write_table(priv, mode_table[mode_index]);
if (err) {
dev_err(&client->dev, "%s: error setting sensor streaming\n",
__func__);
goto done;
}
priv->streaming = enable;
done:
mutex_unlock(&priv->streaming_lock);
return err;
}
DEFINE_SIMPLE_ATTRIBUTE(s5k5e8_debugfs_streaming_fops,
s5k5e8_debugfs_streaming_show,
s5k5e8_debugfs_streaming_write,
"%lld\n");
static void s5k5e8_debugfs_remove(struct s5k5e8 *priv);
static int s5k5e8_debugfs_create(struct s5k5e8 *priv)
{
int err = 0;
struct i2c_client *client = priv->i2c_client;
const char *devnode;
char debugfs_dir[16];
err = of_property_read_string(client->dev.of_node, "devnode", &devnode);
if (err) {
dev_err(&client->dev, "devnode not in DT\n");
return err;
}
snprintf(debugfs_dir, sizeof(debugfs_dir), "camera-%s", devnode);
priv->debugfs_dir = debugfs_create_dir(debugfs_dir, NULL);
if (priv->debugfs_dir == NULL)
return -ENOMEM;
if (!debugfs_create_file("streaming", 0644, priv->debugfs_dir, priv,
&s5k5e8_debugfs_streaming_fops))
goto error;
return 0;
error:
s5k5e8_debugfs_remove(priv);
return -ENOMEM;
}
static struct tegracam_ctrl_ops s5k5e8_ctrl_ops = {
.numctrls = ARRAY_SIZE(ctrl_cid_list),
.ctrl_cid_list = ctrl_cid_list,
// .string_ctrl_size = {S5K5E8_EEPROM_STR_SIZE,
// S5K5E8_FUSE_ID_STR_SIZE,
// S5K5E8_OTP_STR_SIZE},
.set_gain = s5k5e8_set_gain,
.set_exposure = s5k5e8_set_exposure,
// .set_exposure_short = s5k5e8_set_exposure_short,
.set_frame_rate = s5k5e8_set_frame_rate,
.set_group_hold = s5k5e8_set_group_hold,
// .fill_string_ctrl = s5k5e8_fill_string_ctrl,
};
static int s5k5e8_board_setup(struct s5k5e8 *priv)
{
struct camera_common_data *s_data = priv->s_data;
struct device *dev = s_data->dev;
// bool eeprom_ctrl = 0;
int err = 0;
dev_dbg(dev, "%s++\n", __func__);
/* eeprom interface */
/*
err = s5k5e8_eeprom_device_init(priv);
if (err && s_data->pdata->has_eeprom)
dev_err(dev,
"Failed to allocate eeprom reg map: %d\n", err);
eeprom_ctrl = !err;
*/
err = camera_common_mclk_enable(s_data);
if (err) {
dev_err(dev,
"Error %d turning on mclk\n", err);
return err;
}
err = s5k5e8_power_on(s_data);
if (err) {
dev_err(dev,
"Error %d during power on sensor\n", err);
return err;
}
// error:
// s5k5e8_power_off(s_data);
// camera_common_mclk_disable(s_data);
return err;
}
static void s5k5e8_debugfs_remove(struct s5k5e8 *priv)
{
debugfs_remove_recursive(priv->debugfs_dir);
priv->debugfs_dir = NULL;
}
static int s5k5e8_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
dev_dbg(&client->dev, "%s:\n", __func__);
return 0;
}
static const struct v4l2_subdev_internal_ops s5k5e8_subdev_internal_ops = {
.open = s5k5e8_open,
};
static int s5k5e8_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct device_node *node = client->dev.of_node;
struct tegracam_device *tc_dev;
struct s5k5e8 *priv;
int err;
const struct of_device_id *match;
dev_info(dev, "probing v4l2 sensor.\n");
match = of_match_device(s5k5e8_of_match, dev);
if (!match) {
dev_err(dev, "No device match found\n");
return -ENODEV;
}
if (!IS_ENABLED(CONFIG_OF) || !node)
return -EINVAL;
priv = devm_kzalloc(dev,
sizeof(struct s5k5e8), GFP_KERNEL);
if (!priv)
return -ENOMEM;
tc_dev = devm_kzalloc(dev,
sizeof(struct tegracam_device), GFP_KERNEL);
if (!tc_dev)
return -ENOMEM;
priv->i2c_client = tc_dev->client = client;
tc_dev->dev = dev;
strncpy(tc_dev->name, "s5k5e8", sizeof(tc_dev->name));
tc_dev->dev_regmap_config = &s5k5e8_regmap_config;
tc_dev->sensor_ops = &s5k5e8_common_ops;
tc_dev->v4l2sd_internal_ops = &s5k5e8_subdev_internal_ops;
tc_dev->tcctrl_ops = &s5k5e8_ctrl_ops;
err = tegracam_device_register(tc_dev);
if (err) {
dev_err(dev, "tegra camera driver registration failed\n");
return err;
}
priv->tc_dev = tc_dev;
priv->s_data = tc_dev->s_data;
priv->subdev = &tc_dev->s_data->subdev;
tegracam_set_privdata(tc_dev, (void *)priv);
mutex_init(&priv->streaming_lock);
err = s5k5e8_board_setup(priv);
if (err) {
dev_err(dev, "board setup failed\n");
return err;
}
err = tegracam_v4l2subdev_register(tc_dev, true);
if (err) {
dev_err(dev, "tegra camera subdev registration failed\n");
return err;
}
err = s5k5e8_debugfs_create(priv);
if (err) {
dev_err(dev, "error creating debugfs interface");
s5k5e8_debugfs_remove(priv);
return err;
}
dev_dbg(dev, "Detected S5K5E8 sensor\n");
return 0;
}
static int
s5k5e8_remove(struct i2c_client *client)
{
struct camera_common_data *s_data = to_camera_common_data(&client->dev);
struct s5k5e8 *priv = (struct s5k5e8 *)s_data->priv;
s5k5e8_debugfs_remove(priv);
tegracam_v4l2subdev_unregister(priv->tc_dev);
s5k5e8_power_put(priv->tc_dev);
tegracam_device_unregister(priv->tc_dev);
mutex_destroy(&priv->streaming_lock);
return 0;
}
static const struct i2c_device_id s5k5e8_id[] = {
{ "s5k5e8", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, s5k5e8_id);
static struct i2c_driver s5k5e8_i2c_driver = {
.driver = {
.name = "s5k5e8",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(s5k5e8_of_match),
},
.probe = s5k5e8_probe,
.remove = s5k5e8_remove,
.id_table = s5k5e8_id,
};
module_i2c_driver(s5k5e8_i2c_driver);
MODULE_DESCRIPTION("Media Controller driver for Samsung S5K5E8");
MODULE_AUTHOR("NVIDIA Corporation");
MODULE_LICENSE("GPL v2");