aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/gspca/sq905.c
diff options
context:
space:
mode:
authorAdam Baker <linux@baker-net.org.uk>2009-03-03 18:20:47 -0500
committerMauro Carvalho Chehab <mchehab@redhat.com>2009-03-30 11:43:07 -0400
commit3b27591d4e0b455865969d9f9bfae0d19fd6e5c3 (patch)
tree17ba4fda33d907255125dc62abb1d8f77a67e570 /drivers/media/video/gspca/sq905.c
parent0c5db425519487d06a5a14eb369268f4a2b32677 (diff)
V4L/DVB (10829): Support alternate resolutions for sq905
Add support for the alternate resolutions offered by SQ-905 based cameras. As well as 320x240 all cameras can do 160x120 and some can do 640x480. Signed-off-by: Adam Baker <linux@baker-net.org.uk> Signed-off-by: Theodore Kilgore <kilgota@auburn.edu> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/gspca/sq905.c')
-rw-r--r--drivers/media/video/gspca/sq905.c100
1 files changed, 59 insertions, 41 deletions
diff --git a/drivers/media/video/gspca/sq905.c b/drivers/media/video/gspca/sq905.c
index dafaed69e9d0..da60eea51e44 100644
--- a/drivers/media/video/gspca/sq905.c
+++ b/drivers/media/video/gspca/sq905.c
@@ -60,23 +60,29 @@ MODULE_LICENSE("GPL");
60#define SQ905_PING 0x07 /* when reading an "idling" command */ 60#define SQ905_PING 0x07 /* when reading an "idling" command */
61#define SQ905_READ_DONE 0xc0 /* ack bulk read completed */ 61#define SQ905_READ_DONE 0xc0 /* ack bulk read completed */
62 62
63/* Any non-zero value in the bottom 2 bits of the 2nd byte of
64 * the ID appears to indicate the camera can do 640*480. If the
65 * LSB of that byte is set the image is just upside down, otherwise
66 * it is rotated 180 degrees. */
67#define SQ905_HIRES_MASK 0x00000300
68#define SQ905_ORIENTATION_MASK 0x00000100
69
63/* Some command codes. These go in the "index" slot. */ 70/* Some command codes. These go in the "index" slot. */
64 71
65#define SQ905_ID 0xf0 /* asks for model string */ 72#define SQ905_ID 0xf0 /* asks for model string */
66#define SQ905_CONFIG 0x20 /* gets photo alloc. table, not used here */ 73#define SQ905_CONFIG 0x20 /* gets photo alloc. table, not used here */
67#define SQ905_DATA 0x30 /* accesses photo data, not used here */ 74#define SQ905_DATA 0x30 /* accesses photo data, not used here */
68#define SQ905_CLEAR 0xa0 /* clear everything */ 75#define SQ905_CLEAR 0xa0 /* clear everything */
69#define SQ905_CAPTURE_LOW 0x60 /* Starts capture at 160x120 */ 76#define SQ905_CAPTURE_LOW 0x60 /* Starts capture at 160x120 */
70#define SQ905_CAPTURE_MED 0x61 /* Starts capture at 320x240 */ 77#define SQ905_CAPTURE_MED 0x61 /* Starts capture at 320x240 */
78#define SQ905_CAPTURE_HIGH 0x62 /* Starts capture at 640x480 (some cams only) */
71/* note that the capture command also controls the output dimensions */ 79/* note that the capture command also controls the output dimensions */
72/* 0x60 -> 160x120, 0x61 -> 320x240 0x62 -> 640x480 depends on camera */
73/* 0x62 is not correct, at least for some cams. Should be 0x63 ? */
74 80
75/* Structure to hold all of our device specific stuff */ 81/* Structure to hold all of our device specific stuff */
76struct sd { 82struct sd {
77 struct gspca_dev gspca_dev; /* !! must be the first item */ 83 struct gspca_dev gspca_dev; /* !! must be the first item */
78 84
79 u8 cam_type; 85 const struct v4l2_pix_format *cap_mode;
80 86
81 /* 87 /*
82 * Driver stuff 88 * Driver stuff
@@ -85,33 +91,24 @@ struct sd {
85 struct workqueue_struct *work_thread; 91 struct workqueue_struct *work_thread;
86}; 92};
87 93
88/* The driver only supports 320x240 so far. */ 94static struct v4l2_pix_format sq905_mode[] = {
89static struct v4l2_pix_format sq905_mode[1] = { 95 { 160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
96 .bytesperline = 160,
97 .sizeimage = 160 * 120,
98 .colorspace = V4L2_COLORSPACE_SRGB,
99 .priv = 0},
90 { 320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, 100 { 320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
91 .bytesperline = 320, 101 .bytesperline = 320,
92 .sizeimage = 320 * 240, 102 .sizeimage = 320 * 240,
93 .colorspace = V4L2_COLORSPACE_SRGB, 103 .colorspace = V4L2_COLORSPACE_SRGB,
104 .priv = 0},
105 { 640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
106 .bytesperline = 640,
107 .sizeimage = 640 * 480,
108 .colorspace = V4L2_COLORSPACE_SRGB,
94 .priv = 0} 109 .priv = 0}
95}; 110};
96 111
97struct cam_type {
98 u32 ident_word;
99 char *name;
100 struct v4l2_pix_format *min_mode;
101 u8 num_modes;
102 u8 sensor_flags;
103};
104
105#define SQ905_FLIP_HORIZ (1 << 0)
106#define SQ905_FLIP_VERT (1 << 1)
107
108/* Last entry is default if nothing else matches */
109static struct cam_type cam_types[] = {
110 { 0x19010509, "PocketCam", &sq905_mode[0], 1, SQ905_FLIP_HORIZ },
111 { 0x32010509, "Magpix", &sq905_mode[0], 1, SQ905_FLIP_HORIZ },
112 { 0, "Default", &sq905_mode[0], 1, SQ905_FLIP_HORIZ | SQ905_FLIP_VERT }
113};
114
115/* 112/*
116 * Send a command to the camera. 113 * Send a command to the camera.
117 */ 114 */
@@ -240,7 +237,7 @@ static void sq905_dostream(struct work_struct *work)
240 237
241 /* request some data and then read it until we have 238 /* request some data and then read it until we have
242 * a complete frame. */ 239 * a complete frame. */
243 bytes_left = sq905_mode[0].sizeimage + FRAME_HEADER_LEN; 240 bytes_left = dev->cap_mode->sizeimage + FRAME_HEADER_LEN;
244 header_read = 0; 241 header_read = 0;
245 discarding = 0; 242 discarding = 0;
246 243
@@ -272,11 +269,18 @@ static void sq905_dostream(struct work_struct *work)
272 packet_type = INTER_PACKET; 269 packet_type = INTER_PACKET;
273 } 270 }
274 frame = gspca_get_i_frame(gspca_dev); 271 frame = gspca_get_i_frame(gspca_dev);
275 if (frame && !discarding) 272 if (frame && !discarding) {
276 gspca_frame_add(gspca_dev, packet_type, 273 gspca_frame_add(gspca_dev, packet_type,
277 frame, data, data_len); 274 frame, data, data_len);
278 else 275 /* If entire frame fits in one packet we still
276 need to add a LAST_PACKET */
277 if ((packet_type == FIRST_PACKET) &&
278 (bytes_left == 0))
279 gspca_frame_add(gspca_dev, LAST_PACKET,
280 frame, data, 0);
281 } else {
279 discarding = 1; 282 discarding = 1;
283 }
280 } 284 }
281 /* acknowledge the frame */ 285 /* acknowledge the frame */
282 mutex_lock(&gspca_dev->usb_lock); 286 mutex_lock(&gspca_dev->usb_lock);
@@ -301,8 +305,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
301 struct cam *cam = &gspca_dev->cam; 305 struct cam *cam = &gspca_dev->cam;
302 struct sd *dev = (struct sd *) gspca_dev; 306 struct sd *dev = (struct sd *) gspca_dev;
303 307
304 cam->cam_mode = sq905_mode;
305 cam->nmodes = 1;
306 /* We don't use the buffer gspca allocates so make it small. */ 308 /* We don't use the buffer gspca allocates so make it small. */
307 cam->bulk_size = 64; 309 cam->bulk_size = 64;
308 310
@@ -328,7 +330,6 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
328/* this function is called at probe and resume time */ 330/* this function is called at probe and resume time */
329static int sd_init(struct gspca_dev *gspca_dev) 331static int sd_init(struct gspca_dev *gspca_dev)
330{ 332{
331 struct sd *dev = (struct sd *) gspca_dev;
332 u32 ident; 333 u32 ident;
333 int ret; 334 int ret;
334 335
@@ -344,17 +345,18 @@ static int sd_init(struct gspca_dev *gspca_dev)
344 ret = sq905_read_data(gspca_dev, gspca_dev->usb_buf, 4); 345 ret = sq905_read_data(gspca_dev, gspca_dev->usb_buf, 4);
345 if (ret < 0) 346 if (ret < 0)
346 return ret; 347 return ret;
347 /* usb_buf is allocated with kmalloc so is aligned. */ 348 /* usb_buf is allocated with kmalloc so is aligned.
348 ident = le32_to_cpup((u32 *)gspca_dev->usb_buf); 349 * Camera model number is the right way round if we assume this
350 * reverse engineered ID is supposed to be big endian. */
351 ident = be32_to_cpup((__be32 *)gspca_dev->usb_buf);
349 ret = sq905_command(gspca_dev, SQ905_CLEAR); 352 ret = sq905_command(gspca_dev, SQ905_CLEAR);
350 if (ret < 0) 353 if (ret < 0)
351 return ret; 354 return ret;
352 dev->cam_type = 0; 355 PDEBUG(D_CONF, "SQ905 camera ID %08x detected", ident);
353 while (dev->cam_type < ARRAY_SIZE(cam_types) - 1 && 356 gspca_dev->cam.cam_mode = sq905_mode;
354 ident != cam_types[dev->cam_type].ident_word) 357 gspca_dev->cam.nmodes = ARRAY_SIZE(sq905_mode);
355 dev->cam_type++; 358 if (!(ident & SQ905_HIRES_MASK))
356 PDEBUG(D_CONF, "SQ905 camera %s, ID %08x detected", 359 gspca_dev->cam.nmodes--;
357 cam_types[dev->cam_type].name, ident);
358 return 0; 360 return 0;
359} 361}
360 362
@@ -364,13 +366,29 @@ static int sd_start(struct gspca_dev *gspca_dev)
364 struct sd *dev = (struct sd *) gspca_dev; 366 struct sd *dev = (struct sd *) gspca_dev;
365 int ret; 367 int ret;
366 368
369 /* Set capture mode based on selected resolution. */
370 dev->cap_mode = gspca_dev->cam.cam_mode;
367 /* "Open the shutter" and set size, to start capture */ 371 /* "Open the shutter" and set size, to start capture */
368 ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_MED); 372 switch (gspca_dev->width) {
373 case 640:
374 PDEBUG(D_STREAM, "Start streaming at high resolution");
375 dev->cap_mode += 2;
376 ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_HIGH);
377 break;
378 case 320:
379 PDEBUG(D_STREAM, "Start streaming at medium resolution");
380 dev->cap_mode++;
381 ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_MED);
382 break;
383 default:
384 PDEBUG(D_STREAM, "Start streaming at low resolution");
385 ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_LOW);
386 }
387
369 if (ret < 0) { 388 if (ret < 0) {
370 PDEBUG(D_ERR, "Start streaming command failed"); 389 PDEBUG(D_ERR, "Start streaming command failed");
371 return ret; 390 return ret;
372 } 391 }
373
374 /* Start the workqueue function to do the streaming */ 392 /* Start the workqueue function to do the streaming */
375 dev->work_thread = create_singlethread_workqueue(MODULE_NAME); 393 dev->work_thread = create_singlethread_workqueue(MODULE_NAME);
376 queue_work(dev->work_thread, &dev->work_struct); 394 queue_work(dev->work_thread, &dev->work_struct);