diff options
author | Guennadi Liakhovetski <lg@denx.de> | 2009-02-23 10:13:23 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2009-03-30 11:42:53 -0400 |
commit | 70e1d353e5d70551c5f69d464c4d194afae62f63 (patch) | |
tree | 454eb518304ab884cf35e249758fcf436d4289b5 /drivers/media/video/mt9t031.c | |
parent | c8329accf7e75a8a8fbe4ad0a15a3eacf221f380 (diff) |
V4L/DVB (10673): mt9t031: fix gain and hflip controls, register update, and scaling
Multiple fixes:
1. allow register update by setting the Output Control register to 2 and not 3
2. fix scaling factor calculations
3. recover lost HFLIP control
4. fix Global Gain calculation
Signed-off-by: Guennadi Liakhovetski <lg@denx.de>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/mt9t031.c')
-rw-r--r-- | drivers/media/video/mt9t031.c | 127 |
1 files changed, 84 insertions, 43 deletions
diff --git a/drivers/media/video/mt9t031.c b/drivers/media/video/mt9t031.c index 349d8e365530..acc1fa9db67d 100644 --- a/drivers/media/video/mt9t031.c +++ b/drivers/media/video/mt9t031.c | |||
@@ -150,7 +150,7 @@ static int mt9t031_init(struct soc_camera_device *icd) | |||
150 | if (ret >= 0) | 150 | if (ret >= 0) |
151 | ret = reg_write(icd, MT9T031_RESET, 0); | 151 | ret = reg_write(icd, MT9T031_RESET, 0); |
152 | if (ret >= 0) | 152 | if (ret >= 0) |
153 | ret = reg_clear(icd, MT9T031_OUTPUT_CONTROL, 3); | 153 | ret = reg_clear(icd, MT9T031_OUTPUT_CONTROL, 2); |
154 | 154 | ||
155 | return ret >= 0 ? 0 : -EIO; | 155 | return ret >= 0 ? 0 : -EIO; |
156 | } | 156 | } |
@@ -158,14 +158,14 @@ static int mt9t031_init(struct soc_camera_device *icd) | |||
158 | static int mt9t031_release(struct soc_camera_device *icd) | 158 | static int mt9t031_release(struct soc_camera_device *icd) |
159 | { | 159 | { |
160 | /* Disable the chip */ | 160 | /* Disable the chip */ |
161 | reg_clear(icd, MT9T031_OUTPUT_CONTROL, 3); | 161 | reg_clear(icd, MT9T031_OUTPUT_CONTROL, 2); |
162 | return 0; | 162 | return 0; |
163 | } | 163 | } |
164 | 164 | ||
165 | static int mt9t031_start_capture(struct soc_camera_device *icd) | 165 | static int mt9t031_start_capture(struct soc_camera_device *icd) |
166 | { | 166 | { |
167 | /* Switch to master "normal" mode */ | 167 | /* Switch to master "normal" mode */ |
168 | if (reg_set(icd, MT9T031_OUTPUT_CONTROL, 3) < 0) | 168 | if (reg_set(icd, MT9T031_OUTPUT_CONTROL, 2) < 0) |
169 | return -EIO; | 169 | return -EIO; |
170 | return 0; | 170 | return 0; |
171 | } | 171 | } |
@@ -173,7 +173,7 @@ static int mt9t031_start_capture(struct soc_camera_device *icd) | |||
173 | static int mt9t031_stop_capture(struct soc_camera_device *icd) | 173 | static int mt9t031_stop_capture(struct soc_camera_device *icd) |
174 | { | 174 | { |
175 | /* Stop sensor readout */ | 175 | /* Stop sensor readout */ |
176 | if (reg_clear(icd, MT9T031_OUTPUT_CONTROL, 3) < 0) | 176 | if (reg_clear(icd, MT9T031_OUTPUT_CONTROL, 2) < 0) |
177 | return -EIO; | 177 | return -EIO; |
178 | return 0; | 178 | return 0; |
179 | } | 179 | } |
@@ -201,6 +201,18 @@ static unsigned long mt9t031_query_bus_param(struct soc_camera_device *icd) | |||
201 | return soc_camera_apply_sensor_flags(icl, MT9T031_BUS_PARAM); | 201 | return soc_camera_apply_sensor_flags(icl, MT9T031_BUS_PARAM); |
202 | } | 202 | } |
203 | 203 | ||
204 | /* Round up minima and round down maxima */ | ||
205 | static void recalculate_limits(struct soc_camera_device *icd, | ||
206 | u16 xskip, u16 yskip) | ||
207 | { | ||
208 | icd->x_min = (MT9T031_COLUMN_SKIP + xskip - 1) / xskip; | ||
209 | icd->y_min = (MT9T031_ROW_SKIP + yskip - 1) / yskip; | ||
210 | icd->width_min = (MT9T031_MIN_WIDTH + xskip - 1) / xskip; | ||
211 | icd->height_min = (MT9T031_MIN_HEIGHT + yskip - 1) / yskip; | ||
212 | icd->width_max = MT9T031_MAX_WIDTH / xskip; | ||
213 | icd->height_max = MT9T031_MAX_HEIGHT / yskip; | ||
214 | } | ||
215 | |||
204 | static int mt9t031_set_fmt(struct soc_camera_device *icd, | 216 | static int mt9t031_set_fmt(struct soc_camera_device *icd, |
205 | __u32 pixfmt, struct v4l2_rect *rect) | 217 | __u32 pixfmt, struct v4l2_rect *rect) |
206 | { | 218 | { |
@@ -208,54 +220,70 @@ static int mt9t031_set_fmt(struct soc_camera_device *icd, | |||
208 | int ret; | 220 | int ret; |
209 | const u16 hblank = MT9T031_HORIZONTAL_BLANK, | 221 | const u16 hblank = MT9T031_HORIZONTAL_BLANK, |
210 | vblank = MT9T031_VERTICAL_BLANK; | 222 | vblank = MT9T031_VERTICAL_BLANK; |
211 | u16 xbin, xskip = mt9t031->xskip, ybin, yskip = mt9t031->yskip, | 223 | u16 xbin, xskip, ybin, yskip, width, height, left, top; |
212 | width = rect->width * xskip, height = rect->height * yskip; | ||
213 | 224 | ||
214 | if (pixfmt) { | 225 | if (pixfmt) { |
215 | /* S_FMT - use binning and skipping for scaling, recalculate */ | 226 | /* |
227 | * try_fmt has put rectangle within limits. | ||
228 | * S_FMT - use binning and skipping for scaling, recalculate | ||
229 | * limits, used for cropping | ||
230 | */ | ||
216 | /* Is this more optimal than just a division? */ | 231 | /* Is this more optimal than just a division? */ |
217 | for (xskip = 8; xskip > 1; xskip--) | 232 | for (xskip = 8; xskip > 1; xskip--) |
218 | if (rect->width * xskip <= icd->width_max) | 233 | if (rect->width * xskip <= MT9T031_MAX_WIDTH) |
219 | break; | 234 | break; |
220 | 235 | ||
221 | for (yskip = 8; yskip > 1; yskip--) | 236 | for (yskip = 8; yskip > 1; yskip--) |
222 | if (rect->height * yskip <= icd->height_max) | 237 | if (rect->height * yskip <= MT9T031_MAX_HEIGHT) |
223 | break; | 238 | break; |
224 | 239 | ||
225 | width = rect->width * xskip; | 240 | recalculate_limits(icd, xskip, yskip); |
226 | height = rect->height * yskip; | 241 | } else { |
227 | 242 | /* CROP - no change in scaling, or in limits */ | |
228 | dev_dbg(&icd->dev, "xskip %u, width %u, yskip %u, height %u\n", | 243 | xskip = mt9t031->xskip; |
229 | xskip, width, yskip, height); | 244 | yskip = mt9t031->yskip; |
230 | } | 245 | } |
231 | 246 | ||
247 | /* Make sure we don't exceed sensor limits */ | ||
248 | if (rect->left + rect->width > icd->width_max) | ||
249 | rect->left = (icd->width_max - rect->width) / 2 + icd->x_min; | ||
250 | |||
251 | if (rect->top + rect->height > icd->height_max) | ||
252 | rect->top = (icd->height_max - rect->height) / 2 + icd->y_min; | ||
253 | |||
254 | width = rect->width * xskip; | ||
255 | height = rect->height * yskip; | ||
256 | left = rect->left * xskip; | ||
257 | top = rect->top * yskip; | ||
258 | |||
232 | xbin = min(xskip, (u16)3); | 259 | xbin = min(xskip, (u16)3); |
233 | ybin = min(yskip, (u16)3); | 260 | ybin = min(yskip, (u16)3); |
234 | 261 | ||
235 | /* Make sure we don't exceed frame limits */ | 262 | dev_dbg(&icd->dev, "xskip %u, width %u/%u, yskip %u, height %u/%u\n", |
236 | if (rect->left + width > icd->width_max) | 263 | xskip, width, rect->width, yskip, height, rect->height); |
237 | rect->left = (icd->width_max - width) / 2; | ||
238 | 264 | ||
239 | if (rect->top + height > icd->height_max) | 265 | /* Could just do roundup(rect->left, [xy]bin * 2); but this is cheaper */ |
240 | rect->top = (icd->height_max - height) / 2; | ||
241 | |||
242 | /* Could just do roundup(rect->left, [xy]bin); but this is cheaper */ | ||
243 | switch (xbin) { | 266 | switch (xbin) { |
244 | case 2: | 267 | case 2: |
245 | rect->left = (rect->left + 1) & ~1; | 268 | left = (left + 3) & ~3; |
246 | break; | 269 | break; |
247 | case 3: | 270 | case 3: |
248 | rect->left = roundup(rect->left, 3); | 271 | left = roundup(left, 6); |
249 | } | 272 | } |
250 | 273 | ||
251 | switch (ybin) { | 274 | switch (ybin) { |
252 | case 2: | 275 | case 2: |
253 | rect->top = (rect->top + 1) & ~1; | 276 | top = (top + 3) & ~3; |
254 | break; | 277 | break; |
255 | case 3: | 278 | case 3: |
256 | rect->top = roundup(rect->top, 3); | 279 | top = roundup(top, 6); |
257 | } | 280 | } |
258 | 281 | ||
282 | /* Disable register update, reconfigure atomically */ | ||
283 | ret = reg_set(icd, MT9T031_OUTPUT_CONTROL, 1); | ||
284 | if (ret < 0) | ||
285 | return ret; | ||
286 | |||
259 | /* Blanking and start values - default... */ | 287 | /* Blanking and start values - default... */ |
260 | ret = reg_write(icd, MT9T031_HORIZONTAL_BLANKING, hblank); | 288 | ret = reg_write(icd, MT9T031_HORIZONTAL_BLANKING, hblank); |
261 | if (ret >= 0) | 289 | if (ret >= 0) |
@@ -270,14 +298,14 @@ static int mt9t031_set_fmt(struct soc_camera_device *icd, | |||
270 | ret = reg_write(icd, MT9T031_ROW_ADDRESS_MODE, | 298 | ret = reg_write(icd, MT9T031_ROW_ADDRESS_MODE, |
271 | ((ybin - 1) << 4) | (yskip - 1)); | 299 | ((ybin - 1) << 4) | (yskip - 1)); |
272 | } | 300 | } |
273 | dev_dbg(&icd->dev, "new left %u, top %u\n", rect->left, rect->top); | 301 | dev_dbg(&icd->dev, "new physical left %u, top %u\n", left, top); |
274 | 302 | ||
275 | /* The caller provides a supported format, as guaranteed by | 303 | /* The caller provides a supported format, as guaranteed by |
276 | * icd->try_fmt_cap(), soc_camera_s_crop() and soc_camera_cropcap() */ | 304 | * icd->try_fmt_cap(), soc_camera_s_crop() and soc_camera_cropcap() */ |
277 | if (ret >= 0) | 305 | if (ret >= 0) |
278 | ret = reg_write(icd, MT9T031_COLUMN_START, rect->left); | 306 | ret = reg_write(icd, MT9T031_COLUMN_START, left); |
279 | if (ret >= 0) | 307 | if (ret >= 0) |
280 | ret = reg_write(icd, MT9T031_ROW_START, rect->top); | 308 | ret = reg_write(icd, MT9T031_ROW_START, top); |
281 | if (ret >= 0) | 309 | if (ret >= 0) |
282 | ret = reg_write(icd, MT9T031_WINDOW_WIDTH, width - 1); | 310 | ret = reg_write(icd, MT9T031_WINDOW_WIDTH, width - 1); |
283 | if (ret >= 0) | 311 | if (ret >= 0) |
@@ -302,6 +330,9 @@ static int mt9t031_set_fmt(struct soc_camera_device *icd, | |||
302 | mt9t031->yskip = yskip; | 330 | mt9t031->yskip = yskip; |
303 | } | 331 | } |
304 | 332 | ||
333 | /* Re-enable register update, commit all changes */ | ||
334 | reg_clear(icd, MT9T031_OUTPUT_CONTROL, 1); | ||
335 | |||
305 | return ret < 0 ? ret : 0; | 336 | return ret < 0 ? ret : 0; |
306 | } | 337 | } |
307 | 338 | ||
@@ -310,14 +341,14 @@ static int mt9t031_try_fmt(struct soc_camera_device *icd, | |||
310 | { | 341 | { |
311 | struct v4l2_pix_format *pix = &f->fmt.pix; | 342 | struct v4l2_pix_format *pix = &f->fmt.pix; |
312 | 343 | ||
313 | if (pix->height < icd->height_min) | 344 | if (pix->height < MT9T031_MIN_HEIGHT) |
314 | pix->height = icd->height_min; | 345 | pix->height = MT9T031_MIN_HEIGHT; |
315 | if (pix->height > icd->height_max) | 346 | if (pix->height > MT9T031_MAX_HEIGHT) |
316 | pix->height = icd->height_max; | 347 | pix->height = MT9T031_MAX_HEIGHT; |
317 | if (pix->width < icd->width_min) | 348 | if (pix->width < MT9T031_MIN_WIDTH) |
318 | pix->width = icd->width_min; | 349 | pix->width = MT9T031_MIN_WIDTH; |
319 | if (pix->width > icd->width_max) | 350 | if (pix->width > MT9T031_MAX_WIDTH) |
320 | pix->width = icd->width_max; | 351 | pix->width = MT9T031_MAX_WIDTH; |
321 | 352 | ||
322 | pix->width &= ~0x01; /* has to be even */ | 353 | pix->width &= ~0x01; /* has to be even */ |
323 | pix->height &= ~0x01; /* has to be even */ | 354 | pix->height &= ~0x01; /* has to be even */ |
@@ -390,6 +421,14 @@ static const struct v4l2_queryctrl mt9t031_controls[] = { | |||
390 | .step = 1, | 421 | .step = 1, |
391 | .default_value = 0, | 422 | .default_value = 0, |
392 | }, { | 423 | }, { |
424 | .id = V4L2_CID_HFLIP, | ||
425 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
426 | .name = "Flip Horizontally", | ||
427 | .minimum = 0, | ||
428 | .maximum = 1, | ||
429 | .step = 1, | ||
430 | .default_value = 0, | ||
431 | }, { | ||
393 | .id = V4L2_CID_GAIN, | 432 | .id = V4L2_CID_GAIN, |
394 | .type = V4L2_CTRL_TYPE_INTEGER, | 433 | .type = V4L2_CTRL_TYPE_INTEGER, |
395 | .name = "Gain", | 434 | .name = "Gain", |
@@ -513,21 +552,23 @@ static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_contro | |||
513 | if (data < 0) | 552 | if (data < 0) |
514 | return -EIO; | 553 | return -EIO; |
515 | } else { | 554 | } else { |
516 | /* Pack it into 1.125..15 variable step, register values 9..67 */ | 555 | /* Pack it into 1.125..128 variable step, register values 9..0x7860 */ |
517 | /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */ | 556 | /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */ |
518 | unsigned long range = qctrl->maximum - qctrl->default_value - 1; | 557 | unsigned long range = qctrl->maximum - qctrl->default_value - 1; |
558 | /* calculated gain: map 65..127 to 9..1024 step 0.125 */ | ||
519 | unsigned long gain = ((ctrl->value - qctrl->default_value - 1) * | 559 | unsigned long gain = ((ctrl->value - qctrl->default_value - 1) * |
520 | 111 + range / 2) / range + 9; | 560 | 1015 + range / 2) / range + 9; |
521 | 561 | ||
522 | if (gain <= 32) | 562 | if (gain <= 32) /* calculated gain 9..32 -> 9..32 */ |
523 | data = gain; | 563 | data = gain; |
524 | else if (gain <= 64) | 564 | else if (gain <= 64) /* calculated gain 33..64 -> 0x51..0x60 */ |
525 | data = ((gain - 32) * 16 + 16) / 32 + 80; | 565 | data = ((gain - 32) * 16 + 16) / 32 + 80; |
526 | else | 566 | else |
527 | data = ((gain - 64) * 7 + 28) / 56 + 96; | 567 | /* calculated gain 65..1024 -> (1..120) << 8 + 0x60 */ |
568 | data = (((gain - 64 + 7) * 32) & 0xff00) | 0x60; | ||
528 | 569 | ||
529 | dev_dbg(&icd->dev, "Setting gain from %d to %d\n", | 570 | dev_dbg(&icd->dev, "Setting gain from 0x%x to 0x%x\n", |
530 | reg_read(icd, MT9T031_GLOBAL_GAIN), data); | 571 | reg_read(icd, MT9T031_GLOBAL_GAIN), data); |
531 | data = reg_write(icd, MT9T031_GLOBAL_GAIN, data); | 572 | data = reg_write(icd, MT9T031_GLOBAL_GAIN, data); |
532 | if (data < 0) | 573 | if (data < 0) |
533 | return -EIO; | 574 | return -EIO; |