All of lore.kernel.org
 help / color / mirror / Atom feed
From: Sylwester Nawrocki <sylvester.nawrocki@gmail.com>
To: Hans Verkuil <hverkuil@xs4all.nl>
Cc: linux-media@vger.kernel.org
Subject: Re: [PATCH RFC v1 2/2] V4L: Add driver for OV9650/52 image sensors
Date: Mon, 14 Jan 2013 23:26:42 +0100	[thread overview]
Message-ID: <50F48622.8060704@gmail.com> (raw)
In-Reply-To: <201301141045.44403.hverkuil@xs4all.nl>

On 01/14/2013 10:45 AM, Hans Verkuil wrote:
> On Mon January 14 2013 00:14:39 Sylwester Nawrocki wrote:
...
>> I've checked the datasheets and the gain ceiling control is supported by
>> virtually every Omnivision sensor: OV2655, OV3640, OV5630, OV9650, OV9655,
>> OV7690, with even identical range 2x...128x.
>>
>> The _OV965X prefix for the control doesn't seem right then. Should I make
>> it something (ugly) like V4L2_CID_OVXXXX_GAIN_CEILING ?
>
> In that case it would make sense to make this a documented chipset control.
> See e.g. the cx2341x and mfc51 MPEG controls:
>
> http://hverkuil.home.xs4all.nl/spec/media.html#mpeg-controls
>
> I'd drop the XXXX in that case.

That makes sense. I'm not sure if I'll manage to complete all this in time
for v3.9. I guess it would be OK to postpone adding these 2 private 
controls
to the next kernel release ? In fact they are only "nice to have" ones.

>> And should ranges be reserved for each driver ?
>
> Both, actually. Chipset specific controls get a range, and so do driver specific
> controls.

OK. I will likely need to create such a control set for Exynos4412/Exynos5
camera ISP. There should be not many of them but I suspect we'll need some.

>> Or maybe only per
>> manufacturer?
...
>>>> +static int ov965x_set_gain(struct ov965x *ov965x, int auto_gain, bool init)
>>>> +{
>>>> +	struct i2c_client *client = ov965x->client;
>>>> +	struct ov965x_ctrls *ctrls =&ov965x->ctrls;
>>>> +	int ret = 0;
>>>> +	u8 reg;
>>>> +	/*
>>>> +	 * For manual mode we need to disable AGC first, so
>>>> +	 * gain value in REG_VREF, REG_GAIN is not overwritten.
>>>> +	 */
>>>> +	if (ctrls->auto_gain->is_new || init) {
>>>> +		ret = ov965x_read(client, REG_COM8,&reg);
>>>> +		if (ret<   0)
>>>> +			return ret;
>>>> +		if (ctrls->auto_gain->val)
>>>> +			reg |= COM8_AGC;
>>>> +		else
>>>> +			reg&= ~COM8_AGC;
>>>> +		ret = ov965x_write(client, REG_COM8, reg);
>>>> +		if (ret<   0)
>>>> +			return ret;
>>>> +	}
>>>> +
>>>> +	if ((ctrls->gain->is_new || init)&&   !auto_gain) {
>>>> +		unsigned int gain = ctrls->gain->val;
>>>> +		unsigned int rgain;
>>>> +		int m;
>>>> +		/*
>>>> +		 * Convert gain control value to the sensor's gain
>>>> +		 * registers (VREF[7:6], GAIN[7:0]) format.
>>>> +		 */
>>>> +		for (m = 6; m>= 0; m--)
>>>> +			if (gain>= (1<<   m) * 16)
>>>> +				break;
>>>> +		rgain = (gain - ((1<<   m) * 16)) / (1<<   m);
>>>> +		rgain |= (((1<<   m) - 1)<<   4);
>>>> +
>>>> +		ret = ov965x_write(client, REG_GAIN, rgain&   0xff);
>>>> +		if (ret<   0)
>>>> +			return ret;
>>>> +		ret = ov965x_read(client, REG_VREF,&reg);
>>>> +		if (ret<   0)
>>>> +			return ret;
>>>> +		reg&= ~VREF_GAIN_MASK;
>>>> +		reg |= (((rgain>>   8)&   0x3)<<   6);
>>>> +		ret = ov965x_write(client, REG_VREF, reg);
>>>> +		if (ret<   0)
>>>> +			return ret;
>>>> +		/* Return updated control's value to userspace */
>>>> +		ctrls->gain->val = (1<<   m) * (16 + (rgain&   0xf));
>>>> +	}
>>>> +
>>>> +	if (ctrls->gain_ceiling->is_new || init) {
>>>> +		u8 gc = ctrls->gain_ceiling->val;
>>>> +		ret = ov965x_read(client, REG_COM9,&reg);
>>>> +		if (!ret) {
>>>> +			reg&= ~COM9_GAIN_CEIL_MASK;
>>>> +			reg |= ((gc&   0x07)<<   4);
>>>> +			ret = ov965x_write(client, REG_COM9, reg);
>>>> +		}
>>>> +	}
>>>> +	if (auto_gain)
>>>> +		ctrls->gain->flags |= CTRL_FLAG_READ_ONLY_VOLATILE;
>>>> +	else
>>>> +		ctrls->gain->flags&= ~CTRL_FLAG_READ_ONLY_VOLATILE;
>>>> +
>>>> +	return ret;
>>>> +}
>> ...
>>>> +static int ov965x_set_exposure(struct ov965x *ov965x, int exp, bool init)
>>>> +{
>>>> +	struct i2c_client *client = ov965x->client;
>>>> +	struct ov965x_ctrls *ctrls =&ov965x->ctrls;
>>>> +	bool auto_exposure = (exp == V4L2_EXPOSURE_AUTO);
>>>> +	int ret;
>>>> +	u8 reg;
>>>> +
>>>> +	if (ctrls->auto_exp->is_new || init) {
>>>> +		ret = ov965x_read(client, REG_COM8,&reg);
>>>> +		if (ret<   0)
>>>> +			return ret;
>>>> +		if (auto_exposure)
>>>> +			reg |= (COM8_AEC | COM8_AGC);
>>>> +		else
>>>> +			reg&= ~(COM8_AEC | COM8_AGC);
>>>> +		ret = ov965x_write(client, REG_COM8, reg);
>>>> +		if (ret<   0)
>>>> +			return ret;
>>>> +	}
>>>> +
>>>> +	if (!auto_exposure&&   (ctrls->exposure->is_new || init)) {
>>>> +		unsigned int exposure = (ctrls->exposure->val * 100)
>>>> +					 / ov965x->exp_row_interval;
>>>> +		/*
>>>> +		 * Manual exposure value
>>>> +		 * [b15:b0] - AECHM (b15:b10), AECH (b9:b2), COM1 (b1:b0)
>>>> +		 */
>>>> +		ret = ov965x_write(client, REG_COM1, exposure&   0x3);
>>>> +		if (!ret)
>>>> +			ret = ov965x_write(client, REG_AECH,
>>>> +					   (exposure>>   2)&   0xff);
>>>> +		if (!ret)
>>>> +			ret = ov965x_write(client, REG_AECHM,
>>>> +					   (exposure>>   10)&   0x3f);
>>>> +		/* Update the value to minimize rounding errors */
>>>> +		ctrls->exposure->val = ((exposure * ov965x->exp_row_interval)
>>>> +							+ 50) / 100;
>>>> +		if (ret<   0)
>>>> +			return ret;
>>>> +	}
>>>> +
>>>> +	if (ctrls->ae_frame_area->is_new || init) {
>>>> +		ret = ov965x_read(client, REG_COM11,&reg);
>>>> +		if (ret<   0)
>>>> +			return ret;
>>>> +		reg&= ~COM11_AEC_REF_MASK;
>>>> +		reg |= ((ctrls->ae_frame_area->val&   0x3)<<   3);
>>>> +		ret = ov965x_write(client, REG_COM11, reg);
>>>> +		if (ret<   0)
>>>> +			return ret;
>>>> +	}
>>>> +
>>>> +	if (auto_exposure)
>>>> +		ctrls->exposure->flags |= CTRL_FLAG_READ_ONLY_VOLATILE;
>>>> +	else
>>>> +		ctrls->exposure->flags&= ~CTRL_FLAG_READ_ONLY_VOLATILE;
>>>> +
>>>> +	v4l2_ctrl_activate(ov965x->ctrls.brightness, !exp);
>>>> +	return 0;
>>>> +}
...
>>>> +static int ov965x_initialize_controls(struct ov965x *ov965x)
>>>> +{
>>>> +	const struct v4l2_ctrl_ops *ops =&ov965x_ctrl_ops;
>>>> +	struct ov965x_ctrls *ctrls =&ov965x->ctrls;
>>>> +	struct v4l2_ctrl_handler *hdl =&ctrls->handler;
>>>> +	int ret;
>>>> +
>>>> +	ret = v4l2_ctrl_handler_init(hdl, 13);
>>>> +	if (ret<   0)
>>>> +		return ret;
>>>> +
>>>> +	/* Auto/manual white balance */
>>>> +	ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops,
>>>> +				V4L2_CID_AUTO_WHITE_BALANCE,
>>>> +				0, 1, 1, 1);
>>>> +	ctrls->blue_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BLUE_BALANCE,
>>>> +						0, 0xff, 1, 0x80);
>>>> +	ctrls->red_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE,
>>>> +						0, 0xff, 1, 0x80);
>>>> +	/* Auto/manual exposure */
>>>> +	ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops,
>>>> +				V4L2_CID_EXPOSURE_AUTO,
>>>> +				V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO);
>>>> +	/* Exposure time, in 100 us units. min/max is updated dynamically. */
>>>> +	ctrls->exposure = v4l2_ctrl_new_std(hdl, ops,
>>>> +				V4L2_CID_EXPOSURE_ABSOLUTE,
>>>> +				2, 1500, 1, 500);
>>>> +	/* Auto exposure reference frame area */
>>>> +	ctrls->ae_frame_area = v4l2_ctrl_new_custom(hdl,
>>>> +						&ov965x_ctrls[1], NULL);
>>>> +	/* Auto/manual gain */
>>>> +	ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN,
>>>> +						0, 1, 1, 1);
>>>> +	ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN,
>>>> +						16, 64 * (16 + 15), 1, 64 * 16);
>>>> +	ctrls->gain_ceiling = v4l2_ctrl_new_custom(hdl,&ov965x_ctrls[0], NULL);
>>>> +
>>>> +	ctrls->saturation = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION,
>>>> +						-2, 2, 1, 0);
>>>> +	ctrls->brightness = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS,
>>>> +						-3, 3, 1, 0);
>>>> +	ctrls->contrast = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST,
>>>> +						-2, 2, 1, 0);
>>>> +	ctrls->sharpness = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SHARPNESS,
>>>> +						0, 32, 1, 6);
>>>> +
>>>> +	ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
>>>> +	ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
>>>> +
>>>> +	ctrls->light_freq = v4l2_ctrl_new_std_menu(hdl, ops,
>>>> +				V4L2_CID_POWER_LINE_FREQUENCY,
>>>> +				V4L2_CID_POWER_LINE_FREQUENCY_60HZ, ~0x7,
>>>> +				V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
>>>> +
>>>> +	v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN,
>>>> +				ARRAY_SIZE(test_pattern_menu) - 1, 0, 0,
>>>> +				test_pattern_menu);
>>>> +	if (hdl->error) {
>>>> +		ret = hdl->error;
>>>> +		v4l2_ctrl_handler_free(hdl);
>>>> +		return ret;
>>>> +	}
>>>> +
>>>> +	ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE;
>>>> +	ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE;
>>>> +
>>>> +	v4l2_ctrl_auto_cluster(3,&ctrls->auto_wb, 0, false);
>>>> +	v4l2_ctrl_cluster(3,&ctrls->auto_exp);
>>>> +	v4l2_ctrl_cluster(2,&ctrls->hflip);
>>>> +	v4l2_ctrl_cluster(3,&ctrls->auto_gain);
>>>
>>> Why don't you use auto_cluster for gain and exposure? It should simplify your
>>> code quite a bit.
>>
>> I tried, but it didn't work in these use cases.
>>
>> Note there are 3 controls in each cluster, e.g. auto/manual gain,
>> manual_gain,
>> gain_ceiling (max auto gain limit). gain_ceiling is only valid for automatic
>> gain, and the manual_gain value of course only for manual gain mode. With
>> auto_cluster gain_ceiling would be deactivated when gain is set to auto
>> mode,
>
> Does gain_ceiling have to be part of a cluster? Isn't it a standalone control?
> It seems to be set independent of the other gain related controls.

I thought it's cleaner that way. gain_ceiling is only effective with AGC 
enabled.
Now I see I missed to set relevant control flags, so user space is aware 
of that.

> Ditto for ae_frame_area AFAICT.

Yeah, similar situation here.


Thanks,
Sylwester

      reply	other threads:[~2013-01-14 22:26 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-01-04 23:10 [PATCH RFC v1 0/2] Omnivision OV9650/52 sensor driver Sylwester Nawrocki
2013-01-04 23:10 ` [PATCH RFC v1 1/2] [media] Add header file defining standard image sizes Sylwester Nawrocki
2013-01-04 23:10 ` [PATCH RFC v1 2/2] V4L: Add driver for OV9650/52 image sensors Sylwester Nawrocki
2013-01-07 13:38   ` Hans Verkuil
2013-01-13 23:14     ` Sylwester Nawrocki
2013-01-14  9:45       ` Hans Verkuil
2013-01-14 22:26         ` Sylwester Nawrocki [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=50F48622.8060704@gmail.com \
    --to=sylvester.nawrocki@gmail.com \
    --cc=hverkuil@xs4all.nl \
    --cc=linux-media@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.