diff options
author | Guennadi Liakhovetski <g.liakhovetski@gmx.de> | 2008-12-01 07:45:27 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2008-12-30 06:38:24 -0500 |
commit | c2786ad27104c558b92343e8816e18654aae1759 (patch) | |
tree | 45d36ef3dd9f8182477a6386c5063aa3f4177dbb /drivers/media/video/soc_camera.c | |
parent | d2e3dce083bc96b3bfb71603eaa1726181f7184b (diff) |
V4L/DVB (9790): soc-camera: pixel format negotiation - core support
Allocate and fill a list of formats, supported by this specific
camera-host combination. Use it for format enumeration. Take care to stay
backwards-compatible.
Camera hosts rely on sensor formats available, as well as
host specific translations. We add a structure so that hosts
can define a translation table and use it for format check
and setup.
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/soc_camera.c')
-rw-r--r-- | drivers/media/video/soc_camera.c | 93 |
1 files changed, 81 insertions, 12 deletions
diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index 9db66a4fd1a3..5e48c2cc1a44 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c | |||
@@ -47,6 +47,18 @@ const struct soc_camera_data_format *soc_camera_format_by_fourcc( | |||
47 | } | 47 | } |
48 | EXPORT_SYMBOL(soc_camera_format_by_fourcc); | 48 | EXPORT_SYMBOL(soc_camera_format_by_fourcc); |
49 | 49 | ||
50 | const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc( | ||
51 | struct soc_camera_device *icd, unsigned int fourcc) | ||
52 | { | ||
53 | unsigned int i; | ||
54 | |||
55 | for (i = 0; i < icd->num_user_formats; i++) | ||
56 | if (icd->user_formats[i].host_fmt->fourcc == fourcc) | ||
57 | return icd->user_formats + i; | ||
58 | return NULL; | ||
59 | } | ||
60 | EXPORT_SYMBOL(soc_camera_xlate_by_fourcc); | ||
61 | |||
50 | static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv, | 62 | static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv, |
51 | struct v4l2_format *f) | 63 | struct v4l2_format *f) |
52 | { | 64 | { |
@@ -161,6 +173,59 @@ static int soc_camera_dqbuf(struct file *file, void *priv, | |||
161 | return videobuf_dqbuf(&icf->vb_vidq, p, file->f_flags & O_NONBLOCK); | 173 | return videobuf_dqbuf(&icf->vb_vidq, p, file->f_flags & O_NONBLOCK); |
162 | } | 174 | } |
163 | 175 | ||
176 | static int soc_camera_init_user_formats(struct soc_camera_device *icd) | ||
177 | { | ||
178 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | ||
179 | int i, fmts = 0; | ||
180 | |||
181 | if (!ici->ops->get_formats) | ||
182 | /* | ||
183 | * Fallback mode - the host will have to serve all | ||
184 | * sensor-provided formats one-to-one to the user | ||
185 | */ | ||
186 | fmts = icd->num_formats; | ||
187 | else | ||
188 | /* | ||
189 | * First pass - only count formats this host-sensor | ||
190 | * configuration can provide | ||
191 | */ | ||
192 | for (i = 0; i < icd->num_formats; i++) | ||
193 | fmts += ici->ops->get_formats(icd, i, NULL); | ||
194 | |||
195 | if (!fmts) | ||
196 | return -ENXIO; | ||
197 | |||
198 | icd->user_formats = | ||
199 | vmalloc(fmts * sizeof(struct soc_camera_format_xlate)); | ||
200 | if (!icd->user_formats) | ||
201 | return -ENOMEM; | ||
202 | |||
203 | icd->num_user_formats = fmts; | ||
204 | fmts = 0; | ||
205 | |||
206 | dev_dbg(&icd->dev, "Found %d supported formats.\n", fmts); | ||
207 | |||
208 | /* Second pass - actually fill data formats */ | ||
209 | for (i = 0; i < icd->num_formats; i++) | ||
210 | if (!ici->ops->get_formats) { | ||
211 | icd->user_formats[i].host_fmt = icd->formats + i; | ||
212 | icd->user_formats[i].cam_fmt = icd->formats + i; | ||
213 | icd->user_formats[i].buswidth = icd->formats[i].depth; | ||
214 | } else { | ||
215 | fmts += ici->ops->get_formats(icd, i, | ||
216 | &icd->user_formats[fmts]); | ||
217 | } | ||
218 | |||
219 | icd->current_fmt = icd->user_formats[0].host_fmt; | ||
220 | |||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | static void soc_camera_free_user_formats(struct soc_camera_device *icd) | ||
225 | { | ||
226 | vfree(icd->user_formats); | ||
227 | } | ||
228 | |||
164 | static int soc_camera_open(struct inode *inode, struct file *file) | 229 | static int soc_camera_open(struct inode *inode, struct file *file) |
165 | { | 230 | { |
166 | struct video_device *vdev; | 231 | struct video_device *vdev; |
@@ -197,10 +262,12 @@ static int soc_camera_open(struct inode *inode, struct file *file) | |||
197 | 262 | ||
198 | /* Now we really have to activate the camera */ | 263 | /* Now we really have to activate the camera */ |
199 | if (icd->use_count == 1) { | 264 | if (icd->use_count == 1) { |
265 | ret = soc_camera_init_user_formats(icd); | ||
266 | if (ret < 0) | ||
267 | goto eiufmt; | ||
200 | ret = ici->ops->add(icd); | 268 | ret = ici->ops->add(icd); |
201 | if (ret < 0) { | 269 | if (ret < 0) { |
202 | dev_err(&icd->dev, "Couldn't activate the camera: %d\n", ret); | 270 | dev_err(&icd->dev, "Couldn't activate the camera: %d\n", ret); |
203 | icd->use_count--; | ||
204 | goto eiciadd; | 271 | goto eiciadd; |
205 | } | 272 | } |
206 | } | 273 | } |
@@ -216,6 +283,9 @@ static int soc_camera_open(struct inode *inode, struct file *file) | |||
216 | 283 | ||
217 | /* All errors are entered with the video_lock held */ | 284 | /* All errors are entered with the video_lock held */ |
218 | eiciadd: | 285 | eiciadd: |
286 | soc_camera_free_user_formats(icd); | ||
287 | eiufmt: | ||
288 | icd->use_count--; | ||
219 | module_put(ici->ops->owner); | 289 | module_put(ici->ops->owner); |
220 | emgi: | 290 | emgi: |
221 | module_put(icd->ops->owner); | 291 | module_put(icd->ops->owner); |
@@ -234,8 +304,10 @@ static int soc_camera_close(struct inode *inode, struct file *file) | |||
234 | 304 | ||
235 | mutex_lock(&video_lock); | 305 | mutex_lock(&video_lock); |
236 | icd->use_count--; | 306 | icd->use_count--; |
237 | if (!icd->use_count) | 307 | if (!icd->use_count) { |
238 | ici->ops->remove(icd); | 308 | ici->ops->remove(icd); |
309 | soc_camera_free_user_formats(icd); | ||
310 | } | ||
239 | module_put(icd->ops->owner); | 311 | module_put(icd->ops->owner); |
240 | module_put(ici->ops->owner); | 312 | module_put(ici->ops->owner); |
241 | mutex_unlock(&video_lock); | 313 | mutex_unlock(&video_lock); |
@@ -311,6 +383,7 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, | |||
311 | struct soc_camera_device *icd = icf->icd; | 383 | struct soc_camera_device *icd = icf->icd; |
312 | struct soc_camera_host *ici = | 384 | struct soc_camera_host *ici = |
313 | to_soc_camera_host(icd->dev.parent); | 385 | to_soc_camera_host(icd->dev.parent); |
386 | __u32 pixfmt = f->fmt.pix.pixelformat; | ||
314 | int ret; | 387 | int ret; |
315 | struct v4l2_rect rect; | 388 | struct v4l2_rect rect; |
316 | 389 | ||
@@ -328,14 +401,12 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, | |||
328 | if (ret < 0) { | 401 | if (ret < 0) { |
329 | return ret; | 402 | return ret; |
330 | } else if (!icd->current_fmt || | 403 | } else if (!icd->current_fmt || |
331 | icd->current_fmt->fourcc != f->fmt.pix.pixelformat) { | 404 | icd->current_fmt->fourcc != pixfmt) { |
332 | dev_err(&ici->dev, "Host driver hasn't set up current " | 405 | dev_err(&ici->dev, |
333 | "format correctly!\n"); | 406 | "Host driver hasn't set up current format correctly!\n"); |
334 | return -EINVAL; | 407 | return -EINVAL; |
335 | } | 408 | } |
336 | 409 | ||
337 | /* buswidth may be further adjusted by the ici */ | ||
338 | icd->buswidth = icd->current_fmt->depth; | ||
339 | icd->width = rect.width; | 410 | icd->width = rect.width; |
340 | icd->height = rect.height; | 411 | icd->height = rect.height; |
341 | icf->vb_vidq.field = f->fmt.pix.field; | 412 | icf->vb_vidq.field = f->fmt.pix.field; |
@@ -347,7 +418,7 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, | |||
347 | icd->width, icd->height); | 418 | icd->width, icd->height); |
348 | 419 | ||
349 | /* set physical bus parameters */ | 420 | /* set physical bus parameters */ |
350 | return ici->ops->set_bus_param(icd, f->fmt.pix.pixelformat); | 421 | return ici->ops->set_bus_param(icd, pixfmt); |
351 | } | 422 | } |
352 | 423 | ||
353 | static int soc_camera_enum_fmt_vid_cap(struct file *file, void *priv, | 424 | static int soc_camera_enum_fmt_vid_cap(struct file *file, void *priv, |
@@ -359,10 +430,10 @@ static int soc_camera_enum_fmt_vid_cap(struct file *file, void *priv, | |||
359 | 430 | ||
360 | WARN_ON(priv != file->private_data); | 431 | WARN_ON(priv != file->private_data); |
361 | 432 | ||
362 | if (f->index >= icd->num_formats) | 433 | if (f->index >= icd->num_user_formats) |
363 | return -EINVAL; | 434 | return -EINVAL; |
364 | 435 | ||
365 | format = &icd->formats[f->index]; | 436 | format = icd->user_formats[f->index].host_fmt; |
366 | 437 | ||
367 | strlcpy(f->description, format->name, sizeof(f->description)); | 438 | strlcpy(f->description, format->name, sizeof(f->description)); |
368 | f->pixelformat = format->fourcc; | 439 | f->pixelformat = format->fourcc; |
@@ -919,8 +990,6 @@ int soc_camera_video_start(struct soc_camera_device *icd) | |||
919 | vdev->minor = -1; | 990 | vdev->minor = -1; |
920 | vdev->tvnorms = V4L2_STD_UNKNOWN, | 991 | vdev->tvnorms = V4L2_STD_UNKNOWN, |
921 | 992 | ||
922 | icd->current_fmt = &icd->formats[0]; | ||
923 | |||
924 | err = video_register_device(vdev, VFL_TYPE_GRABBER, vdev->minor); | 993 | err = video_register_device(vdev, VFL_TYPE_GRABBER, vdev->minor); |
925 | if (err < 0) { | 994 | if (err < 0) { |
926 | dev_err(vdev->parent, "video_register_device failed\n"); | 995 | dev_err(vdev->parent, "video_register_device failed\n"); |