mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-10-31 05:54:26 -04:00 
			
		
		
		
	Also removes random module and switches to new bcm2711 thermal driver. Boot tested on RPi 4B v1.1 4G. Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
		
			
				
	
	
		
			408 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			408 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From 9e584d9de3387588bf455d3c45ec6a092bfa4266 Mon Sep 17 00:00:00 2001
 | |
| From: David Plowman <david.plowman@raspberrypi.com>
 | |
| Date: Wed, 29 Jan 2020 15:30:53 +0000
 | |
| Subject: [PATCH] media: ov5647: Add basic support for multiple sensor
 | |
|  modes.
 | |
| 
 | |
| Specifically:
 | |
| 
 | |
| Added a structure ov5647_mode and a list of supported_modes (though no
 | |
| actual new modes as yet). The state object points to the "current mode".
 | |
| 
 | |
| ov5647_enum_mbus_code, ov5647_enum_frame_size, ov5647_set_fmt and
 | |
| ov5647_get_fmt all needed upgrading to cope with multiple modes.
 | |
| 
 | |
| __sensor_init (which writes all the registers) is now called by
 | |
| ov5647_stream_on (once the mode is known) rather than by
 | |
| ov5647_sensor_power.
 | |
| 
 | |
| Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
 | |
| Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
 | |
| ---
 | |
|  drivers/media/i2c/ov5647.c | 268 ++++++++++++++++++++++++++++---------
 | |
|  1 file changed, 202 insertions(+), 66 deletions(-)
 | |
| 
 | |
| --- a/drivers/media/i2c/ov5647.c
 | |
| +++ b/drivers/media/i2c/ov5647.c
 | |
| @@ -86,13 +86,17 @@ struct regval_list {
 | |
|  	u8 data;
 | |
|  };
 | |
|  
 | |
| +struct ov5647_mode {
 | |
| +	struct v4l2_mbus_framefmt	format;
 | |
| +	struct regval_list		*reg_list;
 | |
| +	unsigned int			num_regs;
 | |
| +};
 | |
| +
 | |
|  struct ov5647 {
 | |
|  	struct v4l2_subdev		sd;
 | |
|  	struct media_pad		pad;
 | |
|  	struct mutex			lock;
 | |
| -	struct v4l2_mbus_framefmt	format;
 | |
| -	unsigned int			width;
 | |
| -	unsigned int			height;
 | |
| +	const struct ov5647_mode	*mode;
 | |
|  	int				power_count;
 | |
|  	struct clk			*xclk;
 | |
|  	struct gpio_desc		*pwdn;
 | |
| @@ -207,6 +211,32 @@ static struct regval_list ov5647_640x480
 | |
|  	{0x0100, 0x01},
 | |
|  };
 | |
|  
 | |
| +static struct ov5647_mode supported_modes_8bit[] = {
 | |
| +	/*
 | |
| +	 * Original 8-bit VGA mode
 | |
| +	 * Uncentred crop (top left quarter) from 2x2 binned 1296x972 image.
 | |
| +	 */
 | |
| +	{
 | |
| +		{
 | |
| +			.code = MEDIA_BUS_FMT_SBGGR8_1X8,
 | |
| +			.colorspace = V4L2_COLORSPACE_SRGB,
 | |
| +			.field = V4L2_FIELD_NONE,
 | |
| +			.width = 640,
 | |
| +			.height = 480
 | |
| +		},
 | |
| +		ov5647_640x480,
 | |
| +		ARRAY_SIZE(ov5647_640x480)
 | |
| +	},
 | |
| +	/* more modes below here... */
 | |
| +};
 | |
| +
 | |
| +static struct ov5647_mode supported_modes_10bit[] = {
 | |
| +	/* no 10-bit modes yet */
 | |
| +};
 | |
| +
 | |
| +/* Use original 8-bit VGA mode as default. */
 | |
| +#define OV5647_DEFAULT_MODE (&supported_modes_8bit[0])
 | |
| +
 | |
|  static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val)
 | |
|  {
 | |
|  	int ret;
 | |
| @@ -293,12 +323,55 @@ static int ov5647_set_virtual_channel(st
 | |
|  	return ov5647_write(sd, OV5647_REG_MIPI_CTRL14, channel_id | (channel << 6));
 | |
|  }
 | |
|  
 | |
| +static int __sensor_init(struct v4l2_subdev *sd)
 | |
| +{
 | |
| +	int ret;
 | |
| +	u8 resetval, rdval;
 | |
| +	struct i2c_client *client = v4l2_get_subdevdata(sd);
 | |
| +	struct ov5647 *state = to_state(sd);
 | |
| +
 | |
| +	ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval);
 | |
| +	if (ret < 0)
 | |
| +		return ret;
 | |
| +
 | |
| +	ret = ov5647_write_array(sd, state->mode->reg_list,
 | |
| +				 state->mode->num_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)
 | |
| +		return ret;
 | |
| +
 | |
| +	ret = ov5647_read(sd, OV5647_SW_STANDBY, &resetval);
 | |
| +	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;
 | |
| +	}
 | |
| +
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
|  static int ov5647_stream_on(struct v4l2_subdev *sd)
 | |
|  {
 | |
| +	struct i2c_client *client = v4l2_get_subdevdata(sd);
 | |
|  	struct ov5647 *ov5647 = to_state(sd);
 | |
|  	u8 val = MIPI_CTRL00_BUS_IDLE;
 | |
|  	int ret;
 | |
|  
 | |
| +	ret = __sensor_init(sd);
 | |
| +	if (ret < 0) {
 | |
| +		dev_err(&client->dev, "sensor_init failed\n");
 | |
| +		return ret;
 | |
| +	}
 | |
| +
 | |
|  	if (ov5647->flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK)
 | |
|  		val |= MIPI_CTRL00_CLOCK_LANE_GATE |
 | |
|  		       MIPI_CTRL00_LINE_SYNC_ENABLE;
 | |
| @@ -347,44 +420,6 @@ static int set_sw_standby(struct v4l2_su
 | |
|  	return ov5647_write(sd, OV5647_SW_STANDBY, rdval);
 | |
|  }
 | |
|  
 | |
| -static int __sensor_init(struct v4l2_subdev *sd)
 | |
| -{
 | |
| -	int ret;
 | |
| -	u8 resetval, rdval;
 | |
| -	struct i2c_client *client = v4l2_get_subdevdata(sd);
 | |
| -
 | |
| -	ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval);
 | |
| -	if (ret < 0)
 | |
| -		return ret;
 | |
| -
 | |
| -	ret = ov5647_write_array(sd, ov5647_640x480,
 | |
| -					ARRAY_SIZE(ov5647_640x480));
 | |
| -	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)
 | |
| -		return ret;
 | |
| -
 | |
| -	ret = ov5647_read(sd, OV5647_SW_STANDBY, &resetval);
 | |
| -	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;
 | |
| -	}
 | |
| -
 | |
| -	/*
 | |
| -	 * stream off to make the clock lane into LP-11 state.
 | |
| -	 */
 | |
| -	return ov5647_stream_off(sd);
 | |
| -}
 | |
| -
 | |
|  static int ov5647_sensor_power(struct v4l2_subdev *sd, int on)
 | |
|  {
 | |
|  	int ret = 0;
 | |
| @@ -408,7 +443,7 @@ static int ov5647_sensor_power(struct v4
 | |
|  		}
 | |
|  
 | |
|  		ret = ov5647_write_array(sd, sensor_oe_enable_regs,
 | |
| -				ARRAY_SIZE(sensor_oe_enable_regs));
 | |
| +					 ARRAY_SIZE(sensor_oe_enable_regs));
 | |
|  		if (ret < 0) {
 | |
|  			clk_disable_unprepare(ov5647->xclk);
 | |
|  			dev_err(&client->dev,
 | |
| @@ -416,7 +451,10 @@ static int ov5647_sensor_power(struct v4
 | |
|  			goto out;
 | |
|  		}
 | |
|  
 | |
| -		ret = __sensor_init(sd);
 | |
| +		/*
 | |
| +		 * Ensure streaming off to make clock lane go into LP-11 state.
 | |
| +		 */
 | |
| +		ret = ov5647_stream_off(sd);
 | |
|  		if (ret < 0) {
 | |
|  			clk_disable_unprepare(ov5647->xclk);
 | |
|  			dev_err(&client->dev,
 | |
| @@ -427,7 +465,7 @@ static int ov5647_sensor_power(struct v4
 | |
|  		dev_dbg(&client->dev, "OV5647 power off\n");
 | |
|  
 | |
|  		ret = ov5647_write_array(sd, sensor_oe_disable_regs,
 | |
| -				ARRAY_SIZE(sensor_oe_disable_regs));
 | |
| +					 ARRAY_SIZE(sensor_oe_disable_regs));
 | |
|  
 | |
|  		if (ret < 0)
 | |
|  			dev_dbg(&client->dev, "disable oe failed\n");
 | |
| @@ -489,10 +527,19 @@ static const struct v4l2_subdev_core_ops
 | |
|  
 | |
|  static int ov5647_s_stream(struct v4l2_subdev *sd, int enable)
 | |
|  {
 | |
| +	struct ov5647 *state = to_state(sd);
 | |
| +	int ret = 0;
 | |
| +
 | |
| +	mutex_lock(&state->lock);
 | |
| +
 | |
|  	if (enable)
 | |
| -		return ov5647_stream_on(sd);
 | |
| +		ret = ov5647_stream_on(sd);
 | |
|  	else
 | |
| -		return ov5647_stream_off(sd);
 | |
| +		ret = ov5647_stream_off(sd);
 | |
| +
 | |
| +	mutex_unlock(&state->lock);
 | |
| +
 | |
| +	return ret;
 | |
|  }
 | |
|  
 | |
|  static const struct v4l2_subdev_video_ops ov5647_subdev_video_ops = {
 | |
| @@ -503,38 +550,127 @@ static int ov5647_enum_mbus_code(struct
 | |
|  				struct v4l2_subdev_pad_config *cfg,
 | |
|  				struct v4l2_subdev_mbus_code_enum *code)
 | |
|  {
 | |
| -	if (code->index > 0)
 | |
| +	if (code->index == 0 && ARRAY_SIZE(supported_modes_8bit))
 | |
| +		code->code = MEDIA_BUS_FMT_SBGGR8_1X8;
 | |
| +	else if (code->index == 0 && ARRAY_SIZE(supported_modes_8bit) == 0 &&
 | |
| +		 ARRAY_SIZE(supported_modes_10bit))
 | |
| +		code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
 | |
| +	else if (code->index == 1 && ARRAY_SIZE(supported_modes_8bit) &&
 | |
| +		 ARRAY_SIZE(supported_modes_10bit))
 | |
| +		code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
 | |
| +	else
 | |
|  		return -EINVAL;
 | |
|  
 | |
| -	code->code = MEDIA_BUS_FMT_SBGGR8_1X8;
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +static int ov5647_enum_frame_size(struct v4l2_subdev *sd,
 | |
| +				  struct v4l2_subdev_pad_config *cfg,
 | |
| +				  struct v4l2_subdev_frame_size_enum *fse)
 | |
| +{
 | |
| +	struct ov5647_mode *mode = NULL;
 | |
| +
 | |
| +	if (fse->code == MEDIA_BUS_FMT_SBGGR8_1X8) {
 | |
| +		if (fse->index >= ARRAY_SIZE(supported_modes_8bit))
 | |
| +			return -EINVAL;
 | |
| +		mode = &supported_modes_8bit[fse->index];
 | |
| +	} else if (fse->code == MEDIA_BUS_FMT_SBGGR10_1X10) {
 | |
| +		if (fse->index >= ARRAY_SIZE(supported_modes_10bit))
 | |
| +			return -EINVAL;
 | |
| +		mode = &supported_modes_10bit[fse->index];
 | |
| +	} else {
 | |
| +		return -EINVAL;
 | |
| +	}
 | |
| +
 | |
| +	fse->min_width = mode->format.width;
 | |
| +	fse->max_width = fse->min_width;
 | |
| +	fse->min_height = mode->format.height;
 | |
| +	fse->max_height = fse->min_height;
 | |
| +
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +static int ov5647_set_fmt(struct v4l2_subdev *sd,
 | |
| +			  struct v4l2_subdev_pad_config *cfg,
 | |
| +			  struct v4l2_subdev_format *format)
 | |
| +{
 | |
| +	struct v4l2_mbus_framefmt *fmt = &format->format;
 | |
| +	struct ov5647 *state = to_state(sd);
 | |
| +	struct v4l2_mbus_framefmt *framefmt;
 | |
| +	const struct ov5647_mode *mode_8bit, *mode_10bit, *mode = NULL;
 | |
| +
 | |
| +	if (format->pad != 0)
 | |
| +		return -EINVAL;
 | |
| +
 | |
| +	mutex_lock(&state->lock);
 | |
| +
 | |
| +	/*
 | |
| +	 * Try to respect any given pixel format, otherwise try for a 10-bit
 | |
| +	 * mode.
 | |
| +	 */
 | |
| +	mode_8bit = v4l2_find_nearest_size(supported_modes_8bit,
 | |
| +					   ARRAY_SIZE(supported_modes_8bit),
 | |
| +					   format.width, format.height,
 | |
| +					   format->format.width,
 | |
| +					   format->format.height);
 | |
| +	mode_10bit = v4l2_find_nearest_size(supported_modes_10bit,
 | |
| +					    ARRAY_SIZE(supported_modes_10bit),
 | |
| +					    format.width, format.height,
 | |
| +					    format->format.width,
 | |
| +					    format->format.height);
 | |
| +	if (format->format.code == MEDIA_BUS_FMT_SBGGR8_1X8 && mode_8bit)
 | |
| +		mode = mode_8bit;
 | |
| +	else if (format->format.code == MEDIA_BUS_FMT_SBGGR10_1X10 &&
 | |
| +		 mode_10bit)
 | |
| +		mode = mode_10bit;
 | |
| +	else if (mode_10bit)
 | |
| +		mode = mode_10bit;
 | |
| +	else
 | |
| +		mode = mode_8bit;
 | |
| +
 | |
| +	if (!mode)
 | |
| +		return -EINVAL;
 | |
| +
 | |
| +	*fmt = mode->format;
 | |
| +	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
 | |
| +		framefmt = v4l2_subdev_get_try_format(sd, cfg, format->pad);
 | |
| +		*framefmt = format->format;
 | |
| +	} else {
 | |
| +		state->mode = mode;
 | |
| +	}
 | |
| +
 | |
| +	mutex_unlock(&state->lock);
 | |
|  
 | |
|  	return 0;
 | |
|  }
 | |
|  
 | |
| -static int ov5647_set_get_fmt(struct v4l2_subdev *sd,
 | |
| -			      struct v4l2_subdev_pad_config *cfg,
 | |
| -			      struct v4l2_subdev_format *format)
 | |
| +static int ov5647_get_fmt(struct v4l2_subdev *sd,
 | |
| +			  struct v4l2_subdev_pad_config *cfg,
 | |
| +			  struct v4l2_subdev_format *format)
 | |
|  {
 | |
|  	struct v4l2_mbus_framefmt *fmt = &format->format;
 | |
| +	struct ov5647 *state = to_state(sd);
 | |
|  
 | |
|  	if (format->pad != 0)
 | |
|  		return -EINVAL;
 | |
|  
 | |
| -	/* Only one format is supported, so return that */
 | |
| -	memset(fmt, 0, sizeof(*fmt));
 | |
| -	fmt->code = MEDIA_BUS_FMT_SBGGR8_1X8;
 | |
| -	fmt->colorspace = V4L2_COLORSPACE_SRGB;
 | |
| -	fmt->field = V4L2_FIELD_NONE;
 | |
| -	fmt->width = 640;
 | |
| -	fmt->height = 480;
 | |
| +	mutex_lock(&state->lock);
 | |
| +
 | |
| +	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
 | |
| +		*fmt = *v4l2_subdev_get_try_format(sd, cfg, format->pad);
 | |
| +	else
 | |
| +		*fmt = state->mode->format;
 | |
| +
 | |
| +	mutex_unlock(&state->lock);
 | |
|  
 | |
|  	return 0;
 | |
|  }
 | |
|  
 | |
|  static const struct v4l2_subdev_pad_ops ov5647_subdev_pad_ops = {
 | |
|  	.enum_mbus_code = ov5647_enum_mbus_code,
 | |
| -	.set_fmt =	  ov5647_set_get_fmt,
 | |
| -	.get_fmt =	  ov5647_set_get_fmt,
 | |
| +	.set_fmt =	  ov5647_set_fmt,
 | |
| +	.get_fmt =	  ov5647_get_fmt,
 | |
| +	.enum_frame_size = ov5647_enum_frame_size,
 | |
|  };
 | |
|  
 | |
|  static const struct v4l2_subdev_ops ov5647_subdev_ops = {
 | |
| @@ -580,18 +716,15 @@ static int ov5647_open(struct v4l2_subde
 | |
|  				v4l2_subdev_get_try_format(sd, fh->pad, 0);
 | |
|  	struct v4l2_rect *crop =
 | |
|  				v4l2_subdev_get_try_crop(sd, fh->pad, 0);
 | |
| +	struct ov5647 *state = to_state(sd);
 | |
|  
 | |
|  	crop->left = OV5647_COLUMN_START_DEF;
 | |
|  	crop->top = OV5647_ROW_START_DEF;
 | |
|  	crop->width = OV5647_WINDOW_WIDTH_DEF;
 | |
|  	crop->height = OV5647_WINDOW_HEIGHT_DEF;
 | |
|  
 | |
| -	format->code = MEDIA_BUS_FMT_SBGGR8_1X8;
 | |
| -
 | |
| -	format->width = OV5647_WINDOW_WIDTH_DEF;
 | |
| -	format->height = OV5647_WINDOW_HEIGHT_DEF;
 | |
| -	format->field = V4L2_FIELD_NONE;
 | |
| -	format->colorspace = V4L2_COLORSPACE_SRGB;
 | |
| +	/* Set the default format to the same as the sensor. */
 | |
| +	*format = state->mode->format;
 | |
|  
 | |
|  	return 0;
 | |
|  }
 | |
| @@ -660,6 +793,9 @@ static int ov5647_probe(struct i2c_clien
 | |
|  
 | |
|  	mutex_init(&sensor->lock);
 | |
|  
 | |
| +	/* Set the default mode before we init the subdev */
 | |
| +	sensor->mode = OV5647_DEFAULT_MODE;
 | |
| +
 | |
|  	sd = &sensor->sd;
 | |
|  	v4l2_i2c_subdev_init(sd, client, &ov5647_subdev_ops);
 | |
|  	sensor->sd.internal_ops = &ov5647_subdev_internal_ops;
 |