mirror of
https://github.com/torvalds/linux.git
synced 2024-12-28 22:02:28 +00:00
[media] ov6650: convert to the control framework
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com> [g.liakhovetski@gmx.de: simplified pointer arithmetic] [jkrzyszt@tis.icnet.pl: fix a typo in the register name] Acked-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
parent
f026671d2b
commit
afd9690c72
@ -32,6 +32,7 @@
|
||||
#include <media/soc_camera.h>
|
||||
#include <media/soc_mediabus.h>
|
||||
#include <media/v4l2-chip-ident.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
|
||||
/* Register definitions */
|
||||
#define REG_GAIN 0x00 /* range 00 - 3F */
|
||||
@ -177,20 +178,23 @@ struct ov6650_reg {
|
||||
|
||||
struct ov6650 {
|
||||
struct v4l2_subdev subdev;
|
||||
|
||||
int gain;
|
||||
int blue;
|
||||
int red;
|
||||
int saturation;
|
||||
int hue;
|
||||
int brightness;
|
||||
int exposure;
|
||||
int gamma;
|
||||
int aec;
|
||||
bool vflip;
|
||||
bool hflip;
|
||||
bool awb;
|
||||
bool agc;
|
||||
struct v4l2_ctrl_handler hdl;
|
||||
struct {
|
||||
/* exposure/autoexposure cluster */
|
||||
struct v4l2_ctrl *autoexposure;
|
||||
struct v4l2_ctrl *exposure;
|
||||
};
|
||||
struct {
|
||||
/* gain/autogain cluster */
|
||||
struct v4l2_ctrl *autogain;
|
||||
struct v4l2_ctrl *gain;
|
||||
};
|
||||
struct {
|
||||
/* blue/red/autowhitebalance cluster */
|
||||
struct v4l2_ctrl *autowb;
|
||||
struct v4l2_ctrl *blue;
|
||||
struct v4l2_ctrl *red;
|
||||
};
|
||||
bool half_scale; /* scale down output by 2 */
|
||||
struct v4l2_rect rect; /* sensor cropping window */
|
||||
unsigned long pclk_limit; /* from host */
|
||||
@ -210,126 +214,6 @@ static enum v4l2_mbus_pixelcode ov6650_codes[] = {
|
||||
V4L2_MBUS_FMT_Y8_1X8,
|
||||
};
|
||||
|
||||
static const struct v4l2_queryctrl ov6650_controls[] = {
|
||||
{
|
||||
.id = V4L2_CID_AUTOGAIN,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "AGC",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 1,
|
||||
},
|
||||
{
|
||||
.id = V4L2_CID_GAIN,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Gain",
|
||||
.minimum = 0,
|
||||
.maximum = 0x3f,
|
||||
.step = 1,
|
||||
.default_value = DEF_GAIN,
|
||||
},
|
||||
{
|
||||
.id = V4L2_CID_AUTO_WHITE_BALANCE,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "AWB",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 1,
|
||||
},
|
||||
{
|
||||
.id = V4L2_CID_BLUE_BALANCE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Blue",
|
||||
.minimum = 0,
|
||||
.maximum = 0xff,
|
||||
.step = 1,
|
||||
.default_value = DEF_BLUE,
|
||||
},
|
||||
{
|
||||
.id = V4L2_CID_RED_BALANCE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Red",
|
||||
.minimum = 0,
|
||||
.maximum = 0xff,
|
||||
.step = 1,
|
||||
.default_value = DEF_RED,
|
||||
},
|
||||
{
|
||||
.id = V4L2_CID_SATURATION,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Saturation",
|
||||
.minimum = 0,
|
||||
.maximum = 0xf,
|
||||
.step = 1,
|
||||
.default_value = 0x8,
|
||||
},
|
||||
{
|
||||
.id = V4L2_CID_HUE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Hue",
|
||||
.minimum = 0,
|
||||
.maximum = HUE_MASK,
|
||||
.step = 1,
|
||||
.default_value = DEF_HUE,
|
||||
},
|
||||
{
|
||||
.id = V4L2_CID_BRIGHTNESS,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Brightness",
|
||||
.minimum = 0,
|
||||
.maximum = 0xff,
|
||||
.step = 1,
|
||||
.default_value = 0x80,
|
||||
},
|
||||
{
|
||||
.id = V4L2_CID_EXPOSURE_AUTO,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "AEC",
|
||||
.minimum = 0,
|
||||
.maximum = 3,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
},
|
||||
{
|
||||
.id = V4L2_CID_EXPOSURE,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Exposure",
|
||||
.minimum = 0,
|
||||
.maximum = 0xff,
|
||||
.step = 1,
|
||||
.default_value = DEF_AECH,
|
||||
},
|
||||
{
|
||||
.id = V4L2_CID_GAMMA,
|
||||
.type = V4L2_CTRL_TYPE_INTEGER,
|
||||
.name = "Gamma",
|
||||
.minimum = 0,
|
||||
.maximum = 0xff,
|
||||
.step = 1,
|
||||
.default_value = 0x12,
|
||||
},
|
||||
{
|
||||
.id = V4L2_CID_VFLIP,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Flip Vertically",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
},
|
||||
{
|
||||
.id = V4L2_CID_HFLIP,
|
||||
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||||
.name = "Flip Horizontally",
|
||||
.minimum = 0,
|
||||
.maximum = 1,
|
||||
.step = 1,
|
||||
.default_value = 0,
|
||||
},
|
||||
};
|
||||
|
||||
/* read a register */
|
||||
static int ov6650_reg_read(struct i2c_client *client, u8 reg, u8 *val)
|
||||
{
|
||||
@ -420,166 +304,91 @@ static int ov6650_s_stream(struct v4l2_subdev *sd, int enable)
|
||||
}
|
||||
|
||||
/* Get status of additional camera capabilities */
|
||||
static int ov6650_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
static int ov6550_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct ov6650 *priv = container_of(ctrl->handler, struct ov6650, hdl);
|
||||
struct v4l2_subdev *sd = &priv->subdev;
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct ov6650 *priv = to_ov6650(client);
|
||||
uint8_t reg;
|
||||
uint8_t reg, reg2;
|
||||
int ret = 0;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_AUTOGAIN:
|
||||
ctrl->value = priv->agc;
|
||||
break;
|
||||
case V4L2_CID_GAIN:
|
||||
if (priv->agc) {
|
||||
ret = ov6650_reg_read(client, REG_GAIN, ®);
|
||||
ctrl->value = reg;
|
||||
} else {
|
||||
ctrl->value = priv->gain;
|
||||
}
|
||||
break;
|
||||
ret = ov6650_reg_read(client, REG_GAIN, ®);
|
||||
if (!ret)
|
||||
priv->gain->val = reg;
|
||||
return ret;
|
||||
case V4L2_CID_AUTO_WHITE_BALANCE:
|
||||
ctrl->value = priv->awb;
|
||||
break;
|
||||
case V4L2_CID_BLUE_BALANCE:
|
||||
if (priv->awb) {
|
||||
ret = ov6650_reg_read(client, REG_BLUE, ®);
|
||||
ctrl->value = reg;
|
||||
} else {
|
||||
ctrl->value = priv->blue;
|
||||
ret = ov6650_reg_read(client, REG_BLUE, ®);
|
||||
if (!ret)
|
||||
ret = ov6650_reg_read(client, REG_RED, ®2);
|
||||
if (!ret) {
|
||||
priv->blue->val = reg;
|
||||
priv->red->val = reg2;
|
||||
}
|
||||
break;
|
||||
case V4L2_CID_RED_BALANCE:
|
||||
if (priv->awb) {
|
||||
ret = ov6650_reg_read(client, REG_RED, ®);
|
||||
ctrl->value = reg;
|
||||
} else {
|
||||
ctrl->value = priv->red;
|
||||
}
|
||||
break;
|
||||
case V4L2_CID_SATURATION:
|
||||
ctrl->value = priv->saturation;
|
||||
break;
|
||||
case V4L2_CID_HUE:
|
||||
ctrl->value = priv->hue;
|
||||
break;
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
ctrl->value = priv->brightness;
|
||||
break;
|
||||
return ret;
|
||||
case V4L2_CID_EXPOSURE_AUTO:
|
||||
ctrl->value = priv->aec;
|
||||
break;
|
||||
case V4L2_CID_EXPOSURE:
|
||||
if (priv->aec) {
|
||||
ret = ov6650_reg_read(client, REG_AECH, ®);
|
||||
ctrl->value = reg;
|
||||
} else {
|
||||
ctrl->value = priv->exposure;
|
||||
}
|
||||
break;
|
||||
case V4L2_CID_GAMMA:
|
||||
ctrl->value = priv->gamma;
|
||||
break;
|
||||
case V4L2_CID_VFLIP:
|
||||
ctrl->value = priv->vflip;
|
||||
break;
|
||||
case V4L2_CID_HFLIP:
|
||||
ctrl->value = priv->hflip;
|
||||
break;
|
||||
ret = ov6650_reg_read(client, REG_AECH, ®);
|
||||
if (!ret)
|
||||
priv->exposure->val = reg;
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Set status of additional camera capabilities */
|
||||
static int ov6650_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
|
||||
static int ov6550_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct ov6650 *priv = container_of(ctrl->handler, struct ov6650, hdl);
|
||||
struct v4l2_subdev *sd = &priv->subdev;
|
||||
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
||||
struct ov6650 *priv = to_ov6650(client);
|
||||
int ret = 0;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_AUTOGAIN:
|
||||
ret = ov6650_reg_rmw(client, REG_COMB,
|
||||
ctrl->value ? COMB_AGC : 0, COMB_AGC);
|
||||
if (!ret)
|
||||
priv->agc = ctrl->value;
|
||||
break;
|
||||
case V4L2_CID_GAIN:
|
||||
ret = ov6650_reg_write(client, REG_GAIN, ctrl->value);
|
||||
if (!ret)
|
||||
priv->gain = ctrl->value;
|
||||
break;
|
||||
ctrl->val ? COMB_AGC : 0, COMB_AGC);
|
||||
if (!ret && !ctrl->val)
|
||||
ret = ov6650_reg_write(client, REG_GAIN, priv->gain->val);
|
||||
return ret;
|
||||
case V4L2_CID_AUTO_WHITE_BALANCE:
|
||||
ret = ov6650_reg_rmw(client, REG_COMB,
|
||||
ctrl->value ? COMB_AWB : 0, COMB_AWB);
|
||||
if (!ret)
|
||||
priv->awb = ctrl->value;
|
||||
break;
|
||||
case V4L2_CID_BLUE_BALANCE:
|
||||
ret = ov6650_reg_write(client, REG_BLUE, ctrl->value);
|
||||
if (!ret)
|
||||
priv->blue = ctrl->value;
|
||||
break;
|
||||
case V4L2_CID_RED_BALANCE:
|
||||
ret = ov6650_reg_write(client, REG_RED, ctrl->value);
|
||||
if (!ret)
|
||||
priv->red = ctrl->value;
|
||||
break;
|
||||
case V4L2_CID_SATURATION:
|
||||
ret = ov6650_reg_rmw(client, REG_SAT, SET_SAT(ctrl->value),
|
||||
SAT_MASK);
|
||||
if (!ret)
|
||||
priv->saturation = ctrl->value;
|
||||
break;
|
||||
case V4L2_CID_HUE:
|
||||
ret = ov6650_reg_rmw(client, REG_HUE, SET_HUE(ctrl->value),
|
||||
HUE_MASK);
|
||||
if (!ret)
|
||||
priv->hue = ctrl->value;
|
||||
break;
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
ret = ov6650_reg_write(client, REG_BRT, ctrl->value);
|
||||
if (!ret)
|
||||
priv->brightness = ctrl->value;
|
||||
break;
|
||||
case V4L2_CID_EXPOSURE_AUTO:
|
||||
switch (ctrl->value) {
|
||||
case V4L2_EXPOSURE_AUTO:
|
||||
ret = ov6650_reg_rmw(client, REG_COMB, COMB_AEC, 0);
|
||||
break;
|
||||
default:
|
||||
ret = ov6650_reg_rmw(client, REG_COMB, 0, COMB_AEC);
|
||||
break;
|
||||
ctrl->val ? COMB_AWB : 0, COMB_AWB);
|
||||
if (!ret && !ctrl->val) {
|
||||
ret = ov6650_reg_write(client, REG_BLUE, priv->blue->val);
|
||||
if (!ret)
|
||||
ret = ov6650_reg_write(client, REG_RED,
|
||||
priv->red->val);
|
||||
}
|
||||
if (!ret)
|
||||
priv->aec = ctrl->value;
|
||||
break;
|
||||
case V4L2_CID_EXPOSURE:
|
||||
ret = ov6650_reg_write(client, REG_AECH, ctrl->value);
|
||||
if (!ret)
|
||||
priv->exposure = ctrl->value;
|
||||
break;
|
||||
return ret;
|
||||
case V4L2_CID_SATURATION:
|
||||
return ov6650_reg_rmw(client, REG_SAT, SET_SAT(ctrl->val),
|
||||
SAT_MASK);
|
||||
case V4L2_CID_HUE:
|
||||
return ov6650_reg_rmw(client, REG_HUE, SET_HUE(ctrl->val),
|
||||
HUE_MASK);
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
return ov6650_reg_write(client, REG_BRT, ctrl->val);
|
||||
case V4L2_CID_EXPOSURE_AUTO:
|
||||
if (ctrl->val == V4L2_EXPOSURE_AUTO)
|
||||
ret = ov6650_reg_rmw(client, REG_COMB, COMB_AEC, 0);
|
||||
else
|
||||
ret = ov6650_reg_rmw(client, REG_COMB, 0, COMB_AEC);
|
||||
if (!ret && ctrl->val == V4L2_EXPOSURE_MANUAL)
|
||||
ret = ov6650_reg_write(client, REG_AECH,
|
||||
priv->exposure->val);
|
||||
return ret;
|
||||
case V4L2_CID_GAMMA:
|
||||
ret = ov6650_reg_write(client, REG_GAM1, ctrl->value);
|
||||
if (!ret)
|
||||
priv->gamma = ctrl->value;
|
||||
break;
|
||||
return ov6650_reg_write(client, REG_GAM1, ctrl->val);
|
||||
case V4L2_CID_VFLIP:
|
||||
ret = ov6650_reg_rmw(client, REG_COMB,
|
||||
ctrl->value ? COMB_FLIP_V : 0, COMB_FLIP_V);
|
||||
if (!ret)
|
||||
priv->vflip = ctrl->value;
|
||||
break;
|
||||
return ov6650_reg_rmw(client, REG_COMB,
|
||||
ctrl->val ? COMB_FLIP_V : 0, COMB_FLIP_V);
|
||||
case V4L2_CID_HFLIP:
|
||||
ret = ov6650_reg_rmw(client, REG_COMB,
|
||||
ctrl->value ? COMB_FLIP_H : 0, COMB_FLIP_H);
|
||||
if (!ret)
|
||||
priv->hflip = ctrl->value;
|
||||
break;
|
||||
return ov6650_reg_rmw(client, REG_COMB,
|
||||
ctrl->val ? COMB_FLIP_H : 0, COMB_FLIP_H);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Get chip identification */
|
||||
@ -1048,14 +857,12 @@ static int ov6650_video_probe(struct soc_camera_device *icd,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct soc_camera_ops ov6650_ops = {
|
||||
.controls = ov6650_controls,
|
||||
.num_controls = ARRAY_SIZE(ov6650_controls),
|
||||
static const struct v4l2_ctrl_ops ov6550_ctrl_ops = {
|
||||
.g_volatile_ctrl = ov6550_g_volatile_ctrl,
|
||||
.s_ctrl = ov6550_s_ctrl,
|
||||
};
|
||||
|
||||
static struct v4l2_subdev_core_ops ov6650_core_ops = {
|
||||
.g_ctrl = ov6650_g_ctrl,
|
||||
.s_ctrl = ov6650_s_ctrl,
|
||||
.g_chip_ident = ov6650_g_chip_ident,
|
||||
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
||||
.g_register = ov6650_get_register,
|
||||
@ -1164,8 +971,46 @@ static int ov6650_probe(struct i2c_client *client,
|
||||
}
|
||||
|
||||
v4l2_i2c_subdev_init(&priv->subdev, client, &ov6650_subdev_ops);
|
||||
v4l2_ctrl_handler_init(&priv->hdl, 13);
|
||||
v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
|
||||
V4L2_CID_VFLIP, 0, 1, 1, 0);
|
||||
v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
|
||||
V4L2_CID_HFLIP, 0, 1, 1, 0);
|
||||
priv->autogain = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
|
||||
V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
|
||||
priv->gain = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
|
||||
V4L2_CID_GAIN, 0, 0x3f, 1, DEF_GAIN);
|
||||
priv->autowb = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
|
||||
V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
|
||||
priv->blue = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
|
||||
V4L2_CID_BLUE_BALANCE, 0, 0xff, 1, DEF_BLUE);
|
||||
priv->red = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
|
||||
V4L2_CID_RED_BALANCE, 0, 0xff, 1, DEF_RED);
|
||||
v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
|
||||
V4L2_CID_SATURATION, 0, 0xf, 1, 0x8);
|
||||
v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
|
||||
V4L2_CID_HUE, 0, HUE_MASK, 1, DEF_HUE);
|
||||
v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
|
||||
V4L2_CID_BRIGHTNESS, 0, 0xff, 1, 0x80);
|
||||
priv->autoexposure = v4l2_ctrl_new_std_menu(&priv->hdl,
|
||||
&ov6550_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
|
||||
V4L2_EXPOSURE_AUTO);
|
||||
priv->exposure = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
|
||||
V4L2_CID_EXPOSURE, 0, 0xff, 1, DEF_AECH);
|
||||
v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
|
||||
V4L2_CID_GAMMA, 0, 0xff, 1, 0x12);
|
||||
|
||||
icd->ops = &ov6650_ops;
|
||||
priv->subdev.ctrl_handler = &priv->hdl;
|
||||
if (priv->hdl.error) {
|
||||
int err = priv->hdl.error;
|
||||
|
||||
kfree(priv);
|
||||
return err;
|
||||
}
|
||||
v4l2_ctrl_auto_cluster(2, &priv->autogain, 0, true);
|
||||
v4l2_ctrl_auto_cluster(3, &priv->autowb, 0, true);
|
||||
v4l2_ctrl_auto_cluster(2, &priv->autoexposure,
|
||||
V4L2_EXPOSURE_MANUAL, true);
|
||||
|
||||
priv->rect.left = DEF_HSTRT << 1;
|
||||
priv->rect.top = DEF_VSTRT << 1;
|
||||
@ -1176,9 +1021,11 @@ static int ov6650_probe(struct i2c_client *client,
|
||||
priv->colorspace = V4L2_COLORSPACE_JPEG;
|
||||
|
||||
ret = ov6650_video_probe(icd, client);
|
||||
if (!ret)
|
||||
ret = v4l2_ctrl_handler_setup(&priv->hdl);
|
||||
|
||||
if (ret) {
|
||||
icd->ops = NULL;
|
||||
v4l2_ctrl_handler_free(&priv->hdl);
|
||||
kfree(priv);
|
||||
}
|
||||
|
||||
@ -1189,6 +1036,8 @@ static int ov6650_remove(struct i2c_client *client)
|
||||
{
|
||||
struct ov6650 *priv = to_ov6650(client);
|
||||
|
||||
v4l2_device_unregister_subdev(&priv->subdev);
|
||||
v4l2_ctrl_handler_free(&priv->hdl);
|
||||
kfree(priv);
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user