diff --git a/arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dts b/arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dts index e030627159c6bc012b44f80180033f0a5a516929..c81c9488828d9bf91273430f883a5b0ac97296d2 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dts +++ b/arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dts @@ -414,9 +414,65 @@ }; &i2c4 { - i2c-scl-rising-time-ns = <600>; - i2c-scl-falling-time-ns = <20>; +/// i2c-scl-rising-time-ns = <600>; +/// i2c-scl-falling-time-ns = <20>; + clock-frequency = <400000>; + status = "okay"; + +/* + rpicamv2: camera@10 { + compatible = "sony,imx219"; + reg = <0x10>; + clocks = <&rpicamclk2>; + pinctrl-names = "default"; + pinctrl-0 = <&enable>; + status = "disabled"; + + rpicamclk2: camera-clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <25000000>; + }; + + port { + csi1_out_2: endpoint { + remote-endpoint = <&csi1_ep1>; + clock-lanes = <0>; + data-lanes = <1 2>; + }; + }; + }; +*/ + + rpicamv1: camera@36 { + compatible = "ovti,ov5647"; + reg = <0x36>; + clocks = <&rpicamclk1>; + pinctrl-names = "default"; + status = "okay"; + + /* CAM_GPIO is an enable GPIO and + * CAM_MCLK is actually controlling the red LED + */ + pinctrl-0 = <&enable>, <&camled>; + enable-gpios = <&gpio1 RK_PB5 GPIO_ACTIVE_HIGH>; + + /* The camera module has an internal clock */ + rpicamclk1: camera-clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <25000000>; + }; + + port { + csi1_out_1: endpoint { + remote-endpoint = <&csi1_ep1>; + clock-lanes = <0>; + data-lanes = <1 2>; + }; + }; + }; }; &i2s0 { @@ -482,6 +538,17 @@ rockchip,pins = <4 RK_PD1 RK_FUNC_GPIO &pcfg_pull_none>; }; }; + + cam_pins { + enable: enable-pins { + rockchip,pins = + <1 RK_PB5 RK_FUNC_GPIO &pcfg_pull_none>; + }; + camled: cam_led_pins { + rockchip,pins = + <2 RK_PB3 RK_FUNC_GPIO &pcfg_pull_up>; + }; + }; }; &pwm2 { @@ -610,3 +677,28 @@ &vopl_mmu { status = "okay"; }; + +&i2c6 { + status = "okay"; +}; + +&i2c4 { +}; + +&mipi_dphy_rx0 { + status = "okay"; +}; + +&isp0 { + status = "okay"; + + ports { + port@0 { + csi1_ep1: endpoint@1 { + reg = <1>; + remote-endpoint = <&csi1_out_1>; + data-lanes = <1 2>; + }; + }; + }; +}; diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 79ce9ec6fc1be1e612af10ec96cbbb166502033c..1ad17c6755cb50b8dbba0d392e5c3d8131788aa9 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -575,6 +575,18 @@ config VIDEO_IMX214 To compile this driver as a module, choose M here: the module will be called imx214. +config VIDEO_IMX219 + tristate "Sony IMX219 sensor support" + depends on GPIOLIB && I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + depends on V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the Sony + IMX214 camera. + + To compile this driver as a module, choose M here: the + module will be called imx219. + config VIDEO_IMX258 tristate "Sony IMX258 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index fd4ea86dedd5df448ced54e00cfc061cc77a26fe..f1dece3de91f9376d2a805203344ba4f845f04d7 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -109,6 +109,7 @@ obj-$(CONFIG_VIDEO_ML86V7667) += ml86v7667.o obj-$(CONFIG_VIDEO_OV2659) += ov2659.o obj-$(CONFIG_VIDEO_TC358743) += tc358743.o obj-$(CONFIG_VIDEO_IMX214) += imx214.o +obj-$(CONFIG_VIDEO_IMX219) += imx219.o obj-$(CONFIG_VIDEO_IMX258) += imx258.o obj-$(CONFIG_VIDEO_IMX274) += imx274.o obj-$(CONFIG_VIDEO_IMX319) += imx319.o diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 4589631798c968a41116169ccdd9e6cd9b26b800..13e19e973aa2a0f060d3e84572fc33295db206aa 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -32,26 +32,33 @@ #include <media/v4l2-fwnode.h> #include <media/v4l2-image-sizes.h> #include <media/v4l2-mediabus.h> +#include <media/v4l2-ctrls.h> #define SENSOR_NAME "ov5647" +#define REG_NULL 0xffff + #define MIPI_CTRL00_CLOCK_LANE_GATE BIT(5) #define MIPI_CTRL00_BUS_IDLE BIT(2) #define MIPI_CTRL00_CLOCK_LANE_DISABLE BIT(0) #define OV5647_SW_STANDBY 0x0100 #define OV5647_SW_RESET 0x0103 -#define OV5647_REG_CHIPID_H 0x300A -#define OV5647_REG_CHIPID_L 0x300B -#define OV5640_REG_PAD_OUT 0x300D +#define OV5647_REG_CHIPID_H 0x300a +#define OV5647_REG_CHIPID_L 0x300b +#define OV5647_REG_PAD_OUT 0x300d + +#define OV5647_REG_TIMING_DVPHO 0x3808 +#define OV5647_REG_TIMING_DVPVO 0x380a +#define OV5647_REG_TIMING_HTS 0x380c +#define OV5647_REG_TIMING_VTS 0x380e +#define OV5647_REG_TIMING_TC_REG20 0x3820 +#define OV5647_REG_TIMING_TC_REG21 0x3821 + #define OV5647_REG_FRAME_OFF_NUMBER 0x4202 #define OV5647_REG_MIPI_CTRL00 0x4800 #define OV5647_REG_MIPI_CTRL14 0x4814 -#define REG_TERM 0xfffe -#define VAL_TERM 0xfe -#define REG_DLY 0xffff - #define OV5647_ROW_START 0x01 #define OV5647_ROW_START_MIN 0 #define OV5647_ROW_START_MAX 2004 @@ -77,6 +84,15 @@ struct regval_list { u8 data; }; +struct ov5647_mode { + unsigned int width; + unsigned int height; + unsigned int max_fps; + unsigned int htot; + unsigned int vtot; + const struct regval_list *reg_list; +}; + struct ov5647 { struct v4l2_subdev sd; struct media_pad pad; @@ -86,6 +102,10 @@ struct ov5647 { unsigned int height; int power_count; struct clk *xclk; + struct v4l2_ctrl *pixel_rate; + const struct ov5647_mode *cur_mode; + struct v4l2_ctrl_handler ctrl_handler; + struct gpio_desc *enable_gpio; }; static inline struct ov5647 *to_state(struct v4l2_subdev *sd) @@ -97,71 +117,34 @@ static struct regval_list sensor_oe_disable_regs[] = { {0x3000, 0x00}, {0x3001, 0x00}, {0x3002, 0x00}, + {REG_NULL, 0x00} }; static struct regval_list sensor_oe_enable_regs[] = { {0x3000, 0x0f}, {0x3001, 0xff}, {0x3002, 0xe4}, + {REG_NULL, 0x00} }; -static struct regval_list ov5647_640x480[] = { - {0x0100, 0x00}, - {0x0103, 0x01}, - {0x3034, 0x08}, - {0x3035, 0x21}, - {0x3036, 0x46}, - {0x303c, 0x11}, - {0x3106, 0xf5}, - {0x3821, 0x07}, - {0x3820, 0x41}, - {0x3827, 0xec}, +static struct regval_list ov5647_init_regs[] = { + /* upstream */ {0x370c, 0x0f}, - {0x3612, 0x59}, - {0x3618, 0x00}, {0x5000, 0x06}, - {0x5001, 0x01}, - {0x5002, 0x41}, {0x5003, 0x08}, {0x5a00, 0x08}, {0x3000, 0x00}, {0x3001, 0x00}, {0x3002, 0x00}, - {0x3016, 0x08}, - {0x3017, 0xe0}, - {0x3018, 0x44}, - {0x301c, 0xf8}, {0x301d, 0xf0}, {0x3a18, 0x00}, {0x3a19, 0xf8}, {0x3c01, 0x80}, {0x3b07, 0x0c}, - {0x380c, 0x07}, - {0x380d, 0x68}, - {0x380e, 0x03}, - {0x380f, 0xd8}, - {0x3814, 0x31}, - {0x3815, 0x31}, - {0x3708, 0x64}, - {0x3709, 0x52}, - {0x3808, 0x02}, - {0x3809, 0x80}, - {0x380a, 0x01}, - {0x380b, 0xE0}, - {0x3801, 0x00}, - {0x3802, 0x00}, - {0x3803, 0x00}, - {0x3804, 0x0a}, - {0x3805, 0x3f}, - {0x3806, 0x07}, - {0x3807, 0xa1}, - {0x3811, 0x08}, - {0x3813, 0x02}, {0x3630, 0x2e}, {0x3632, 0xe2}, {0x3633, 0x23}, {0x3634, 0x44}, - {0x3636, 0x06}, {0x3620, 0x64}, {0x3621, 0xe0}, {0x3600, 0x37}, @@ -175,12 +158,6 @@ static struct regval_list ov5647_640x480[] = { {0x3f05, 0x02}, {0x3f06, 0x10}, {0x3f01, 0x0a}, - {0x3a08, 0x01}, - {0x3a09, 0x27}, - {0x3a0a, 0x00}, - {0x3a0b, 0xf6}, - {0x3a0d, 0x04}, - {0x3a0e, 0x03}, {0x3a0f, 0x58}, {0x3a10, 0x50}, {0x3a1b, 0x58}, @@ -188,55 +165,271 @@ static struct regval_list ov5647_640x480[] = { {0x3a11, 0x60}, {0x3a1f, 0x28}, {0x4001, 0x02}, - {0x4004, 0x02}, {0x4000, 0x09}, - {0x4837, 0x24}, + {0x5001, 0x01}, /* auto white balance */ + {0x5002, 0x41}, + {0x3011, 0x62}, + /* mipi */ + {0x3016, 0x08}, + {0x3017, 0xe0}, + {0x3018, 0x44}, + {0x3034, 0x08}, + {0x3106, 0xf5}, + {REG_NULL, 0x00} +}; + +static struct regval_list ov5647_640x480[] = { + {0x3035, 0x21}, + {0x3036, 0x60}, + {0x303c, 0x11}, + {0x3821, 0x07}, + {0x3820, 0x41}, + {0x3827, 0xec}, /* ? */ + {0x370c, 0x0f}, /* ? */ + {0x3612, 0x59}, + {0x3618, 0x00}, + {0x301c, 0xf8}, /* ? */ + {0x380c, 0x07}, /* 0768 */ + {0x380d, 0x68}, + {0x380e, 0x03}, /* 03d8 */ + {0x380f, 0xd8}, + {0x3814, 0x31}, + {0x3815, 0x31}, + {0x3708, 0x64}, + {0x3709, 0x52}, + {0x3808, 0x02}, /* width = 640 */ + {0x3809, 0x80}, + {0x380a, 0x01}, /* heigth = 480 */ + {0x380b, 0xE0}, + {0x3800, 0x00}, /* X Start */ + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x0a}, + {0x3805, 0x3f}, + {0x3806, 0x07}, + {0x3807, 0xa1}, + {0x3811, 0x08}, + {0x3813, 0x02}, + {0x3636, 0x06}, + {0x3a08, 0x01}, + {0x3a09, 0x27}, + {0x3a0a, 0x00}, + {0x3a0b, 0xf6}, + {0x3a0d, 0x04}, + {0x3a0e, 0x03}, + {0x4004, 0x02}, + {0x4837, 0x0b}, /* MIPI pclk period */ {0x4050, 0x6e}, {0x4051, 0x8f}, - {0x0100, 0x01}, + {REG_NULL, 0x00} +}; + +static struct regval_list ov5647_1280x960[] = { + {0x3035, 0x21}, /* PLL */ + {0x3036, 0x60}, /* PLL */ + {0x303c, 0x11}, /* PLL */ + {0x3821, 0x07}, /* ISP mirror on, Sensor mirror on, H bin on */ + {0x3820, 0x41}, /* ISP flip off, Sensor flip off, V bin on */ + {0x3612, 0x59}, /* analog control */ + {0x3618, 0x00}, /* analog control */ + {0x3814, 0x31}, /* X INC */ + {0x3815, 0x31}, /* Y INC */ + {0x3708, 0x64}, /* analog control */ + {0x3709, 0x52}, /* analog control */ + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x06}, + {0x3804, 0x0a}, + {0x3805, 0x3f}, + {0x3806, 0x07}, + {0x3807, 0x9d}, + /* banding filter */ + {0x3a08, 0x01}, /* B50 */ + {0x3a09, 0x27}, /* B50 */ + {0x3a0a, 0x00}, /* B60 */ + {0x3a0b, 0xf6}, /* B60 */ + {0x3a0d, 0x04}, /* B60 max */ + {0x3a0e, 0x03}, /* B50 max */ + {0x4004, 0x02}, /* black line number */ + {0x4837, 0x0b}, /* MIPI pclk period */ + {REG_NULL, 0x00} +}; + +static struct regval_list ov5647_2592x1944[] = { + {0x3035, 0x21}, + {0x3036, 0x70}, + {0x303c, 0x11}, + {0x3612, 0x5b}, + {0x3618, 0x04}, + {0x380c, 0x0a}, + {0x380d, 0x8c}, + {0x380e, 0x07}, + {0x380f, 0xb6}, + {0x3814, 0x11}, + {0x3815, 0x11}, + {0x3708, 0x64}, + {0x3709, 0x12}, + {0x3808, 0x0a}, + {0x3809, 0x20}, + {0x380a, 0x07}, + {0x380b, 0x98}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x04}, + {0x3804, 0x0a}, + {0x3805, 0x33}, + {0x3806, 0x07}, + {0x3807, 0xa3}, + {0x3a08, 0x01}, + {0x3a09, 0x28}, + {0x3a0a, 0x00}, + {0x3a0b, 0xf6}, + {0x3a0d, 0x08}, + {0x3a0e, 0x06}, + {0x4004, 0x04}, + {0x4837, 0x0b}, /* MIPI pclk period */ + {REG_NULL, 0x00} +}; + +static const struct ov5647_mode supported_modes[] = { + { + .width = 1280, + .height = 960, + .max_fps = 45, + .htot = 1896, + .vtot = 984, + .reg_list = ov5647_1280x960, + }, + { + .width = 2592, + .height = 1944, + .max_fps = 15, + .htot = 2592, + .vtot = 1944, + .reg_list = ov5647_2592x1944, + }, }; static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val) { - int ret; - unsigned char data[3] = { reg >> 8, reg & 0xff, val}; struct i2c_client *client = v4l2_get_subdevdata(sd); + struct i2c_msg msg; + u8 buf[3]; + int ret; - ret = i2c_master_send(client, data, 3); - if (ret < 0) - dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n", - __func__, reg); + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + buf[2] = val; - return ret; + msg.addr = client->addr; + msg.flags = client->flags; + msg.buf = buf; + msg.len = sizeof(buf); + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) { + dev_err(&client->dev, "%s: error: reg=%x, val=%x\n", + __func__, reg, val); + return ret; + } + + return 0; } static int ov5647_read(struct v4l2_subdev *sd, u16 reg, u8 *val) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct i2c_msg msg[2]; + u8 buf[2]; int ret; - unsigned char data_w[2] = { reg >> 8, reg & 0xff }; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + + msg[0].addr = client->addr; + msg[0].flags = client->flags; + msg[0].buf = buf; + msg[0].len = sizeof(buf); + + msg[1].addr = client->addr; + msg[1].flags = client->flags | I2C_M_RD; + msg[1].buf = buf; + msg[1].len = 1; + + ret = i2c_transfer(client->adapter, msg, 2); + if (ret < 0) { + dev_err(&client->dev, "%s: error: reg=%x\n", + __func__, reg); + return ret; + } + + *val = buf[0]; + return 0; +} + +static int ov5647_read16(struct v4l2_subdev *sd, u16 reg, u16 *val) +{ struct i2c_client *client = v4l2_get_subdevdata(sd); + struct i2c_msg msg[4]; + u8 buflo[2], bufhi[2]; + int ret; + + buflo[0] = reg >> 8; + buflo[1] = reg & 0xff; + bufhi[0] = (reg + 1) >> 8; + bufhi[1] = (reg + 1) & 0xff; + + msg[0].addr = client->addr; + msg[0].flags = client->flags; + msg[0].buf = buflo; + msg[0].len = sizeof(buflo); + + msg[1].addr = client->addr; + msg[1].flags = client->flags | I2C_M_RD; + msg[1].buf = buflo; + msg[1].len = 1; - ret = i2c_master_send(client, data_w, 2); + msg[2].addr = client->addr; + msg[2].flags = client->flags; + msg[2].buf = bufhi; + msg[2].len = sizeof(bufhi); + + msg[3].addr = client->addr; + msg[3].flags = client->flags | I2C_M_RD; + msg[3].buf = bufhi; + msg[3].len = 1; + + ret = i2c_transfer(client->adapter, msg, 4); if (ret < 0) { - dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n", + dev_err(&client->dev, "%s: error: reg=%x\n", __func__, reg); return ret; } - ret = i2c_master_recv(client, val, 1); - if (ret < 0) - dev_dbg(&client->dev, "%s: i2c read error, reg: %x\n", - __func__, reg); + *val = ((u16)bufhi[0] << 8) | (u16)buflo; + return 0; +} - return ret; +static int ov5647_write16(struct v4l2_subdev *sd, u16 reg, u16 val) +{ + int ret; + + ret = ov5647_write(sd, reg, val >> 8); + if (ret) + return ret; + + return ov5647_write(sd, reg + 1, val & 0xff); } static int ov5647_write_array(struct v4l2_subdev *sd, - struct regval_list *regs, int array_size) + const struct regval_list *regs) { int i, ret; - for (i = 0; i < array_size; i++) { + for (i = 0; regs[i].addr != REG_NULL; i++) { ret = ov5647_write(sd, regs[i].addr, regs[i].data); if (ret < 0) return ret; @@ -245,6 +438,51 @@ static int ov5647_write_array(struct v4l2_subdev *sd, return 0; } +static int ov5647_mod_reg(struct v4l2_subdev *sd, u16 reg, + u8 mask, u8 val) +{ + u8 readval; + int ret; + + ret = ov5647_read(sd, reg, &readval); + if (ret) + return ret; + + readval &= ~mask; + val &= mask; + val |= readval; + + return ov5647_write(sd, reg, val); +} + +static int ov5647_sw_reset(struct v4l2_subdev *sd) +{ + int ret; + + ret = ov5647_write(sd, OV5647_SW_RESET, 0x01); + if (ret < 0) + return ret; + msleep(5); + return 0; +} + +static int ov5647_set_sw_standby(struct v4l2_subdev *sd, bool standby) +{ + int ret; + u8 rdval; + + ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval); + if (ret < 0) + return ret; + + if (standby) + rdval &= ~0x01; + else + rdval |= 0x01; + + return ov5647_write(sd, OV5647_SW_STANDBY, rdval); +} + static int ov5647_set_virtual_channel(struct v4l2_subdev *sd, int channel) { u8 channel_id; @@ -270,13 +508,21 @@ static int ov5647_stream_on(struct v4l2_subdev *sd) if (ret < 0) return ret; - return ov5647_write(sd, OV5640_REG_PAD_OUT, 0x00); + ret = ov5647_write(sd, OV5647_REG_PAD_OUT, 0x00); + if (ret < 0) + return ret; + + return ov5647_set_sw_standby(sd, false); } static int ov5647_stream_off(struct v4l2_subdev *sd) { int ret; + ret = ov5647_set_sw_standby(sd, true); + if (ret < 0) + return ret; + ret = ov5647_write(sd, OV5647_REG_MIPI_CTRL00, MIPI_CTRL00_CLOCK_LANE_GATE | MIPI_CTRL00_BUS_IDLE | MIPI_CTRL00_CLOCK_LANE_DISABLE); if (ret < 0) @@ -286,57 +532,62 @@ static int ov5647_stream_off(struct v4l2_subdev *sd) if (ret < 0) return ret; - return ov5647_write(sd, OV5640_REG_PAD_OUT, 0x01); + return ov5647_write(sd, OV5647_REG_PAD_OUT, 0x01); } -static int set_sw_standby(struct v4l2_subdev *sd, bool standby) +static int ov5647_set_timings(struct v4l2_subdev *sd, + const struct ov5647_mode *mode) { int ret; - u8 rdval; - ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval); + ret = ov5647_write16(sd, OV5647_REG_TIMING_DVPHO, mode->width); if (ret < 0) return ret; - if (standby) - rdval &= ~0x01; - else - rdval |= 0x01; + ret = ov5647_write16(sd, OV5647_REG_TIMING_DVPVO, mode->height); + if (ret < 0) + return ret; - return ov5647_write(sd, OV5647_SW_STANDBY, rdval); + ret = ov5647_write16(sd, OV5647_REG_TIMING_HTS, mode->htot); + if (ret < 0) + return ret; + + return ov5647_write16(sd, OV5647_REG_TIMING_VTS, mode->vtot); } static int __sensor_init(struct v4l2_subdev *sd) { - int ret; - u8 resetval, rdval; struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov5647 *sensor = to_state(sd); + int ret; - ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval); + ret = ov5647_set_sw_standby(sd, true); if (ret < 0) return ret; - ret = ov5647_write_array(sd, ov5647_640x480, - ARRAY_SIZE(ov5647_640x480)); + ret = ov5647_sw_reset(sd); + if (ret < 0) + return ret; + + ret = ov5647_write_array(sd, ov5647_init_regs); if (ret < 0) { dev_err(&client->dev, "write sensor default regs error\n"); return ret; } - ret = ov5647_set_virtual_channel(sd, 0); - if (ret < 0) + ret = ov5647_write_array(sd, sensor->cur_mode->reg_list); + if (ret < 0) { + dev_err(&client->dev, "write sensor mode regs error\n"); return ret; + } - ret = ov5647_read(sd, OV5647_SW_STANDBY, &resetval); + ret = ov5647_set_timings(sd, sensor->cur_mode); if (ret < 0) return ret; - if (!(resetval & 0x01)) { - dev_err(&client->dev, "Device was in SW standby"); - ret = ov5647_write(sd, OV5647_SW_STANDBY, 0x01); - if (ret < 0) - return ret; - } + ret = ov5647_set_virtual_channel(sd, 0); + if (ret < 0) + return ret; /* * stream off to make the clock lane into LP-11 state. @@ -361,8 +612,10 @@ static int ov5647_sensor_power(struct v4l2_subdev *sd, int on) goto out; } - ret = ov5647_write_array(sd, sensor_oe_enable_regs, - ARRAY_SIZE(sensor_oe_enable_regs)); + gpiod_set_value_cansleep(ov5647->enable_gpio, 1); + msleep(20); + + ret = ov5647_write_array(sd, sensor_oe_enable_regs); if (ret < 0) { clk_disable_unprepare(ov5647->xclk); dev_err(&client->dev, @@ -380,18 +633,12 @@ static int ov5647_sensor_power(struct v4l2_subdev *sd, int on) } else if (!on && ov5647->power_count == 1) { dev_dbg(&client->dev, "OV5647 power off\n"); - ret = ov5647_write_array(sd, sensor_oe_disable_regs, - ARRAY_SIZE(sensor_oe_disable_regs)); - + ret = ov5647_write_array(sd, sensor_oe_disable_regs); if (ret < 0) dev_dbg(&client->dev, "disable oe failed\n"); - ret = set_sw_standby(sd, true); - - if (ret < 0) - dev_dbg(&client->dev, "soft stby failed\n"); - clk_disable_unprepare(ov5647->xclk); + gpiod_set_value_cansleep(ov5647->enable_gpio, 0); } /* Update the power count. */ @@ -451,6 +698,91 @@ static const struct v4l2_subdev_video_ops ov5647_subdev_video_ops = { .s_stream = ov5647_s_stream, }; +static int ov5647_get_reso_dist(const struct ov5647_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); +} + +static const struct ov5647_mode * +ov5647_find_best_fit(struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + int i; + + for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { + dist = ov5647_get_reso_dist(&supported_modes[i], framefmt); + if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + + return &supported_modes[cur_best_fit]; +} + +static int ov5647_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ov5647 *ov5647 = to_state(sd); + const struct ov5647_mode *mode; + s64 pixel_rate; + int ret = 0; + + mutex_lock(&ov5647->lock); + + mode = ov5647_find_best_fit(fmt); + fmt->format.code = MEDIA_BUS_FMT_SBGGR8_1X8; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; + } else { + ov5647->cur_mode = mode; + pixel_rate = mode->vtot * mode->htot * mode->max_fps; + ret = v4l2_ctrl_s_ctrl_int64(ov5647->pixel_rate, + pixel_rate); + if (ret < 0) + goto err_unlock; + pr_info("new mode is %dx%d\n", mode->width, mode->height); + pr_info("pixel rate is now %lld\n", pixel_rate); + } + +err_unlock: + mutex_unlock(&ov5647->lock); + + return ret; +} + +static int ov5647_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct ov5647 *ov5647 = to_state(sd); + const struct ov5647_mode *mode = ov5647->cur_mode; + + mutex_lock(&ov5647->lock); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = MEDIA_BUS_FMT_SBGGR8_1X8; + fmt->format.field = V4L2_FIELD_NONE; + } + + mutex_unlock(&ov5647->lock); + + return 0; +} + static int ov5647_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) @@ -463,8 +795,29 @@ static int ov5647_enum_mbus_code(struct v4l2_subdev *sd, return 0; } +static int ov5647_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + if (fse->code != MEDIA_BUS_FMT_SBGGR8_1X8) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = supported_modes[fse->index].width; + fse->max_height = supported_modes[fse->index].height; + fse->min_height = supported_modes[fse->index].height; + + return 0; +} + static const struct v4l2_subdev_pad_ops ov5647_subdev_pad_ops = { .enum_mbus_code = ov5647_enum_mbus_code, + .enum_frame_size = ov5647_enum_frame_sizes, + .get_fmt = ov5647_get_fmt, + .set_fmt = ov5647_set_fmt, }; static const struct v4l2_subdev_ops ov5647_subdev_ops = { @@ -475,33 +828,23 @@ static const struct v4l2_subdev_ops ov5647_subdev_ops = { static int ov5647_detect(struct v4l2_subdev *sd) { - u8 read; - int ret; struct i2c_client *client = v4l2_get_subdevdata(sd); + u16 chip_id; + int ret; - ret = ov5647_write(sd, OV5647_SW_RESET, 0x01); - if (ret < 0) - return ret; - - ret = ov5647_read(sd, OV5647_REG_CHIPID_H, &read); - if (ret < 0) - return ret; - - if (read != 0x56) { - dev_err(&client->dev, "ID High expected 0x56 got %x", read); + ret = ov5647_read16(sd, OV5647_REG_CHIPID_H, &chip_id); + if (ret) { + dev_err(&client->dev, "%s: failed to read chip identifier\n", + __func__); return -ENODEV; } - ret = ov5647_read(sd, OV5647_REG_CHIPID_L, &read); - if (ret < 0) - return ret; - - if (read != 0x47) { - dev_err(&client->dev, "ID Low expected 0x47 got %x", read); - return -ENODEV; + if (chip_id != 0x5647) { + dev_err(&client->dev, "%s: wrong chip identifier, expected 0x5647, got 0x%x\n", + __func__, chip_id); + ret = -ENXIO; } - - return ov5647_write(sd, OV5647_SW_RESET, 0x00); + return 0; } static int ov5647_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) @@ -532,7 +875,7 @@ static const struct v4l2_subdev_internal_ops ov5647_subdev_internal_ops = { static int ov5647_parse_dt(struct device_node *np) { - struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 }; + struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = V4L2_MBUS_CSI2_DPHY }; struct device_node *ep; int ret; @@ -544,6 +887,7 @@ static int ov5647_parse_dt(struct device_node *np) ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &bus_cfg); of_node_put(ep); + return ret; } @@ -552,14 +896,16 @@ static int ov5647_probe(struct i2c_client *client, { struct device *dev = &client->dev; struct ov5647 *sensor; - int ret; struct v4l2_subdev *sd; + struct v4l2_ctrl_handler *handler; struct device_node *np = client->dev.of_node; u32 xclk_freq; + int ret; sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); if (!sensor) return -ENOMEM; + sensor->cur_mode = &supported_modes[0]; if (IS_ENABLED(CONFIG_OF) && np) { ret = ov5647_parse_dt(np); @@ -582,28 +928,54 @@ static int ov5647_probe(struct i2c_client *client, return -EINVAL; } + sensor->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH); + if (IS_ERR(sensor->enable_gpio)) { + dev_err(dev, "cannot get enable gpio\n"); + return PTR_ERR(sensor->enable_gpio); + } + msleep(20); + mutex_init(&sensor->lock); sd = &sensor->sd; v4l2_i2c_subdev_init(sd, client, &ov5647_subdev_ops); + + handler = &sensor->ctrl_handler; + ret = v4l2_ctrl_handler_init(handler, 1); + if (ret) + return ret; + sensor->pixel_rate = v4l2_ctrl_new_std(handler, NULL, + V4L2_CID_PIXEL_RATE, + 1, INT_MAX, 1, 1); + sensor->sd.ctrl_handler = handler; + sensor->sd.internal_ops = &ov5647_subdev_internal_ops; sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - sensor->pad.flags = MEDIA_PAD_FL_SOURCE; sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; ret = media_entity_pads_init(&sd->entity, 1, &sensor->pad); - if (ret < 0) + if (ret < 0) { + dev_err(dev, "cannot register media entity\n"); goto mutex_remove; + } - ret = ov5647_detect(sd); + ret = ov5647_sw_reset(sd); if (ret < 0) + return ret; + + ret = ov5647_detect(sd); + if (ret < 0) { + dev_err(&client->dev, "not detected!"); goto error; + } + + /* TODO: power off here */ ret = v4l2_async_register_subdev(sd); if (ret < 0) goto error; - dev_dbg(dev, "OmniVision OV5647 camera driver probed\n"); + dev_info(dev, "OV5647 detected at address 0x%02x\n", client->addr); return 0; error: media_entity_cleanup(&sd->entity);