xref: /haiku/src/add-ons/media/media-add-ons/usb_webcam/addons/sonix/SonixCamDevice.cpp (revision edb484677944d3243760cea487fc8c9dbbd032eb)
1 /*
2  * Copyright 2004-2008, François Revol, <revol@free.fr>.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 #include "SonixCamDevice.h"
7 #include "CamDebug.h"
8 #include "CamSensor.h"
9 #include "CamBufferingDeframer.h"
10 #include "CamStreamingDeframer.h"
11 
12 #include <ParameterWeb.h>
13 #include <interface/Bitmap.h>
14 #include <media/Buffer.h>
15 
16 const usb_webcam_support_descriptor kSupportedDevices[] = {
17 {{ 0, 0, 0, 0x0c45, 0x6005 }, "Sonix", "Sonix", "tas5110c1b" }, // mine
18 {{ 0, 0, 0, 0x0c45, 0x6007 }, "Sonix", "macally ICECAM", "tas5110c1b" }, // Rajah's cam - SN9C101R
19 {{ 0, 0, 0, 0x0c45, 0x6009 }, "Trust", "spacec@m 120", NULL },
20 {{ 0, 0, 0, 0x0c45, 0x600d }, "Trust", "spacec@m 120", NULL },
21 
22 /* other devices that should be supported,
23  * cf. sn9c102-1.15 linux driver, sn9c102_sensor.h
24  * for IDs and sensors
25  */
26 {{ 0, 0, 0, 0x0c45, 0x6001 }, "Sonix", "Sonix generic", "tas5110c1b" },
27 {{ 0, 0, 0, 0x0c45, 0x6024 }, "Sonix", "Sonix generic", NULL },
28 {{ 0, 0, 0, 0x0c45, 0x6025 }, "Sonix", "Sonix generic", "tas5110c1b,XXX" },
29 {{ 0, 0, 0, 0x0c45, 0x6028 }, "Sonix", "Sonix generic", NULL },
30 {{ 0, 0, 0, 0x0c45, 0x6029 }, "Sonix", "Sonix generic", NULL },
31 {{ 0, 0, 0, 0x0c45, 0x602a }, "Sonix", "Sonix generic", NULL },
32 {{ 0, 0, 0, 0x0c45, 0x602b }, "Sonix", "Sonix generic", NULL },
33 {{ 0, 0, 0, 0x0c45, 0x602c }, "Sonix", "Sonix generic", NULL },
34 {{ 0, 0, 0, 0x0c45, 0x6030 }, "Sonix", "Sonix generic", NULL },
35 {{ 0, 0, 0, 0x0c45, 0x6080 }, "Sonix", "Sonix generic", NULL },
36 {{ 0, 0, 0, 0x0c45, 0x6082 }, "Sonix", "Sonix generic", NULL },
37 {{ 0, 0, 0, 0x0c45, 0x6083 }, "Sonix", "Sonix generic", NULL },
38 {{ 0, 0, 0, 0x0c45, 0x6088 }, "Sonix", "Sonix generic", NULL },
39 {{ 0, 0, 0, 0x0c45, 0x608a }, "Sonix", "Sonix generic", NULL },
40 {{ 0, 0, 0, 0x0c45, 0x608b }, "Sonix", "Sonix generic", NULL },
41 {{ 0, 0, 0, 0x0c45, 0x608c }, "Sonix", "Sonix generic", NULL },
42 {{ 0, 0, 0, 0x0c45, 0x608e }, "Sonix", "Sonix generic", NULL },
43 {{ 0, 0, 0, 0x0c45, 0x608f }, "Sonix", "Sonix generic", NULL },
44 {{ 0, 0, 0, 0x0c45, 0x60a0 }, "Sonix", "Sonix generic", NULL },
45 {{ 0, 0, 0, 0x0c45, 0x60a2 }, "Sonix", "Sonix generic", NULL },
46 {{ 0, 0, 0, 0x0c45, 0x60a3 }, "Sonix", "Sonix generic", NULL },
47 {{ 0, 0, 0, 0x0c45, 0x60a8 }, "Sonix", "Sonix generic", NULL },
48 {{ 0, 0, 0, 0x0c45, 0x60aa }, "Sonix", "Sonix generic", NULL },
49 {{ 0, 0, 0, 0x0c45, 0x60ab }, "Sonix", "Sonix generic", "tas5110c1b" },
50 {{ 0, 0, 0, 0x0c45, 0x60ac }, "Sonix", "Sonix generic", NULL },
51 {{ 0, 0, 0, 0x0c45, 0x60ae }, "Sonix", "Sonix generic", NULL },
52 {{ 0, 0, 0, 0x0c45, 0x60af }, "Sonix", "Sonix generic", NULL },
53 {{ 0, 0, 0, 0x0c45, 0x60b0 }, "Sonix", "Sonix generic", NULL },
54 {{ 0, 0, 0, 0x0c45, 0x60b2 }, "Sonix", "Sonix generic", NULL },
55 {{ 0, 0, 0, 0x0c45, 0x60b3 }, "Sonix", "Sonix generic", NULL },
56 {{ 0, 0, 0, 0x0c45, 0x60b8 }, "Sonix", "Sonix generic", NULL },
57 {{ 0, 0, 0, 0x0c45, 0x60ba }, "Sonix", "Sonix generic", NULL },
58 {{ 0, 0, 0, 0x0c45, 0x60bb }, "Sonix", "Sonix generic", NULL },
59 {{ 0, 0, 0, 0x0c45, 0x60bc }, "Sonix", "Sonix generic", NULL },
60 {{ 0, 0, 0, 0x0c45, 0x60be }, "Sonix", "Sonix generic", NULL },
61 {{ 0, 0, 0, 0, 0}, NULL, NULL, NULL }
62 };
63 
64 // 12 bytes actually
65 static const uint8 sof_mark_1[] = { 0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x00 };
66 static const uint8 sof_mark_2[] = { 0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x01 };
67 static const uint8 *sof_marks[] = { sof_mark_1, sof_mark_2 };
68 
69 static const uint8 eof_mark_1[] = { 0x00, 0x00, 0x00, 0x00 };
70 static const uint8 eof_mark_2[] = { 0x40, 0x00, 0x00, 0x00 };
71 static const uint8 eof_mark_3[] = { 0x80, 0x00, 0x00, 0x00 };
72 static const uint8 eof_mark_4[] = { 0xc0, 0x00, 0x00, 0x00 };
73 static const uint8 *eof_marks[] = { eof_mark_1, eof_mark_2, eof_mark_3, eof_mark_4 };
74 
75 void bayer2rgb24(unsigned char *dst, unsigned char *src, long int WIDTH, long int HEIGHT);
76 void bayer2rgb32le(unsigned char *dst, unsigned char *src, long int WIDTH, long int HEIGHT);
77 
78 
SonixCamDevice(CamDeviceAddon & _addon,BUSBDevice * _device)79 SonixCamDevice::SonixCamDevice(CamDeviceAddon &_addon, BUSBDevice* _device)
80           :CamDevice(_addon, _device)
81 {
82 	uchar data[8]; /* store bytes returned from sonix commands */
83 	status_t err;
84 	fFrameTagState = 0;
85 
86 	fRGain = fGGain = fBGain = 0;
87 	// unknown
88 	fBrightness = 0.5;
89 
90 	memset(fCachedRegs, 0, SN9C102_REG_COUNT);
91 	fChipVersion = 2;
92 	if ((GetDevice()->ProductID() & ~0x3F) == 0x6080) {
93 		fChipVersion = 3; // says V4L2
94 	}
95 	err = ProbeSensor();
96 
97 //	fDeframer = new CamBufferingDeframer(this);
98 	fDeframer = new CamStreamingDeframer(this);
99 	fDeframer->RegisterSOFTags(sof_marks, 2, sizeof(sof_mark_1), 12);
100 	fDeframer->RegisterEOFTags(eof_marks, 4, sizeof(eof_mark_1), sizeof(eof_mark_1));
101 	SetDataInput(fDeframer);
102 
103 	/* init hw */
104 
105 	const BUSBConfiguration *config = GetDevice()->ConfigurationAt(0);
106 	if (config) {
107 		const BUSBInterface *inter = config->InterfaceAt(0);
108 		uint32 i;
109 
110 		GetDevice()->SetConfiguration(config);
111 
112 		for (i = 0; inter && (i < inter->CountEndpoints()); i++) {
113 			const BUSBEndpoint *e = inter->EndpointAt(i);
114 			if (e && e->IsBulk() && e->IsInput()) {
115 				fBulkIn = e;
116 				PRINT((CH ": Using inter[0].endpoint[%d]; maxpktsz: %d" CT, i, e->MaxPacketSize()));
117 				break;
118 			}
119 		}
120 
121 	}
122 
123 	/* sanity check */
124 	err = ReadReg(SN9C102_ASIC_ID, data);
125 	if (err < 0 || data[0] != 0x10) {
126 		PRINT((CH ": BAD ASIC signature! (%u != %u)" CT, data[0], 0x10));
127 		return;
128 	}
129 
130 		//XXX: the XP driver sends this to the ICECAM... need to investigate.
131 #if 1
132 	uint8 tmp_3[] = {
133 		0x44, 0x44, 0x00, 0x00, 0x00, 0x04, 0x00, 0xa0,
134 		0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
135 		0x00, 0x41, 0x09, 0x00, 0x16, 0x12, 0x60, 0x86,
136 		0x3b, 0x0f, 0x0e, 0x06, 0x00, 0x00, 0x03 };
137 	WriteReg(SN9C102_CHIP_CTRL, tmp_3, 0x1f);
138 #endif
139 		//XXX:DEBUG
140 
141 #if 0
142 		//XXX: the XP driver sends all this... investigate.
143 	uint8 tmp_1[] = {0x09, 0x44};
144 	WriteReg(SN9C102_CHIP_CTRL, tmp_1, sizeof(tmp_1));
145 	WriteReg8(SN9C102_CHIP_CTRL, 0x44);
146 
147 	WriteReg8(SN9C102_CLOCK_SEL /*0x17*/, 0x29);
148 
149 	uint8 tmp_2[] = {0x44, 0x44};
150 	WriteReg(SN9C102_CHIP_CTRL, tmp_2, 2);
151 		//URB_FUNCTION_VENDOR_INTERFACE:
152 		//(USBD_TRANSFER_DIRECTION_OUT, ~USBD_SHORT_TRANSFER_OK)
153 
154 	uint8 tmp_3[] = {
155 		0x44, 0x44, 0x00, 0x00, 0x00, 0x04, 0x00, 0xa0,
156 		0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
157 		0x00, 0x41, 0x09, 0x00, 0x16, 0x12, 0x60, 0x86,
158 		0x3b, 0x0f, 0x0e, 0x06, 0x00, 0x00, 0x03 };
159 	WriteReg(SN9C102_CHIP_CTRL, tmp_3, 0x1f);
160 
161 	uint8 tmp_4[] = {0x01, 0x01, 0x07, 0x06};
162 	//WriteReg(SN9C102_AE_STRX, tmp_4, 4);
163 
164 	uint8 tmp_5[] = {0x14, 0x0f};
165 	//WriteReg(SN9C102_H_SIZE, tmp_5, 2);
166 
167 	WriteReg8(SN9C102_SYNC_N_SCALE, 0x86);
168 
169 	WriteReg8(SN9C102_CHIP_CTRL, 0x44);	// again ??
170 
171 	uint8 tmp_6[] = { 0x60, 0x86 };
172 	WriteReg(SN9C102_CLOCK_SEL /*0x17*/, tmp_6, 2);
173 
174 	WriteReg8(SN9C102_PIX_CLK, 0x2b);
175 
176 	// some IIC stuff for the sensor
177 	uint8 tmp_7[] = { 0xb0, 0x61, 0x1c, 0xf8, 0x10, 0x00, 0x00, 0x16 };
178 	//WriteReg(SN9C102_I2C_SETUP, tmp_7, 8);
179 
180 	WriteReg8(SN9C102_PIX_CLK, 0x4b);
181 
182 	uint8 tmp_8[] = { 0xa0, 0x61, 0x1c, 0x0f, 0x10, 0x00, 0x00, 0x16 };
183 	//WriteReg(SN9C102_I2C_SETUP, tmp_8, 8);
184 
185 #endif
186 #if 0
187 	// some IIC stuff for the sensor
188 	uint8 tmp_7[] = { 0xb0, 0x61, 0x1c, 0xf8, 0x10, 0x00, 0x00, 0x16 };
189 	WriteReg(SN9C102_I2C_SETUP, tmp_7, 8);
190 
191 	WriteReg8(SN9C102_PIX_CLK, 0x4b);
192 
193 	uint8 tmp_8[] = { 0xa0, 0x61, 0x1c, 0x0f, 0x10, 0x00, 0x00, 0x16};
194 	WriteReg(SN9C102_I2C_SETUP, tmp_8, 8);
195 #endif
196 
197 	//WriteReg8(SN9C102_PIX_CLK, 0x4b);
198 
199 //###############
200 
201 	if (Sensor()) {
202 		PRINT((CH ": CamSensor: %s" CT, Sensor()->Name()));
203 		fInitStatus = Sensor()->Setup();
204 
205 		fVideoFrame = BRect(0, 0, Sensor()->MaxWidth()-1, Sensor()->MaxHeight()-1);
206 //		SetVideoFrame(BRect(0, 0, Sensor()->MaxWidth()-1, Sensor()->MaxHeight()-1));
207 //		SetVideoFrame(BRect(0, 0, 320-1, 240-1));
208 	}
209 	//SetScale(1);
210 }
211 
212 
~SonixCamDevice()213 SonixCamDevice::~SonixCamDevice()
214 {
215 	if (Sensor())
216 		delete fSensor;
217 	fSensor = NULL;
218 }
219 
220 
221 bool
SupportsBulk()222 SonixCamDevice::SupportsBulk()
223 {
224 	return true;
225 }
226 
227 
228 bool
SupportsIsochronous()229 SonixCamDevice::SupportsIsochronous()
230 {
231 	return true;
232 }
233 
234 
235 status_t
StartTransfer()236 SonixCamDevice::StartTransfer()
237 {
238 	status_t err;
239 	uint8 r;
240 
241 	SetScale(1);
242 	if (Sensor())
243 		SetVideoFrame(fVideoFrame);
244 
245 	//SetVideoFrame(BRect(0, 0, 320-1, 240-1));
246 
247 DumpRegs();
248 	err = ReadReg(SN9C102_CHIP_CTRL, &r, 1, true);
249 	if (err < 0)
250 		return err;
251 	r |= 0x04;
252 	err = WriteReg8(SN9C102_CHIP_CTRL, r);
253 	if (err < 0)
254 		return err;
255 	return CamDevice::StartTransfer();
256 }
257 
258 
259 status_t
StopTransfer()260 SonixCamDevice::StopTransfer()
261 {
262 	status_t err;
263 	uint8 r;
264 
265 DumpRegs();
266 	err = CamDevice::StopTransfer();
267 //	if (err < 0)
268 //		return err;
269 	err = ReadReg(SN9C102_CHIP_CTRL, &r, 1, true);
270 	if (err < 0)
271 		return err;
272 	r &= ~0x04;
273 	err = WriteReg8(SN9C102_CHIP_CTRL, r);
274 	if (err < 0)
275 		return err;
276 	return err;
277 }
278 
279 
280 status_t
PowerOnSensor(bool on)281 SonixCamDevice::PowerOnSensor(bool on)
282 {
283 	if (OrReg8(SN9C102_CHIP_CTRL, on ? 0x01 : 0x00) < 0)
284 		return EIO;
285 	return B_OK;
286 }
287 
288 
289 ssize_t
WriteReg(uint16 address,uint8 * data,size_t count)290 SonixCamDevice::WriteReg(uint16 address, uint8 *data, size_t count)
291 {
292 	PRINT((CH "(%u, @%p, %" B_PRIuSIZE ")" CT, address, data, count));
293 	status_t err;
294 	if (address + count > SN9C102_REG_COUNT) {
295 		PRINT((CH ": Invalid register range [%u;%" B_PRIuSIZE "]" CT, address,
296 			address + count));
297 		return EINVAL;
298 	}
299 	memcpy(&fCachedRegs[address], data, count);
300 	err = SendCommand(USB_REQTYPE_DEVICE_OUT, 0x08, address, 0, count, data);
301 	if (err < B_OK)
302 		return err;
303 	return count;
304 }
305 
306 
307 ssize_t
ReadReg(uint16 address,uint8 * data,size_t count,bool cached)308 SonixCamDevice::ReadReg(uint16 address, uint8 *data, size_t count, bool cached)
309 {
310 	PRINT((CH "(%u, @%p, %" B_PRIuSIZE ", %d)" CT, address, data, count,
311 		cached));
312 	status_t err;
313 	if (address + count > SN9C102_REG_COUNT) {
314 		PRINT((CH ": Invalid register range [%u;%" B_PRIuSIZE "]" CT, address,
315 			address + count));
316 		return EINVAL;
317 	}
318 	if (cached) {
319 		memcpy(data, &fCachedRegs[address], count);
320 		return count;
321 	}
322 	err = SendCommand(USB_REQTYPE_DEVICE_IN, 0x00, address, 0, count, data);
323 	if (err < B_OK)
324 		return err;
325 	return count;
326 }
327 
328 
329 status_t
GetStatusIIC()330 SonixCamDevice::GetStatusIIC()
331 {
332 	status_t err;
333 	uint8 status = 0;
334 	err = ReadReg(SN9C102_I2C_SETUP, &status);
335 	//dprintf(ID "i2c_status: error 0x%08lx, status = %02x\n", err, status);
336 	if (err < 0)
337 		return err;
338 	return (status&0x08)?EIO:0;
339 }
340 
341 
342 status_t
WaitReadyIIC()343 SonixCamDevice::WaitReadyIIC()
344 {
345 	status_t err;
346 	uint8 status = 0;
347 	int tries = 5;
348 	if (!Sensor())
349 		return B_NO_INIT;
350 	while (tries--) {
351 		err = ReadReg(SN9C102_I2C_SETUP, &status);
352 		//dprintf(ID "i2c_wait_ready: error 0x%08lx, status = %02x\n", err, status);
353 		if (err < 0) return err;
354 		if (status & 0x04) return B_OK;
355 		//XXX:FIXME:spin((1+5+11*dev->sensor->use_400kHz)*8);
356 		snooze((1+5+11*Sensor()->Use400kHz())*8);
357 	}
358 	return EBUSY;
359 }
360 
361 
362 ssize_t
WriteIIC(uint8 address,uint8 * data,size_t count)363 SonixCamDevice::WriteIIC(uint8 address, uint8 *data, size_t count)
364 {
365 	status_t err;
366 	uint8 buffer[8];
367 	PRINT((CH "(%u, @%p, %" B_PRIuSIZE ")" CT, address, data, count));
368 
369 	if (!Sensor())
370 		return B_NO_INIT;
371 	//dprintf(ID "sonix_i2c_write_multi(, %02x, %d, {%02x, %02x, %02x, %02x, %02x})\n", slave, count, d0, d1, d2, d3, d4);
372 	count++; // includes address
373 	if (count > 5)
374 		return EINVAL;
375 	buffer[0] = ((count) << 4) | (Sensor()->Use400kHz()?0x01:0)
376 							 | (Sensor()->UseRealIIC()?0x80:0);
377 	buffer[1] = Sensor()->IICWriteAddress();
378 	buffer[2] = address;
379 	memset(&buffer[3], 0, 5);
380 	memcpy(&buffer[3], data, count-1);
381 	buffer[7] = 0x16; /* V4L2 driver uses 0x10, XP driver uses 0x16 ? */
382 	for (int i = 0; i < 8; i++) {
383 		PRINT(("[%d] = %02x\n", i, buffer[i]));
384 	}
385 	err = WriteReg(SN9C102_I2C_SETUP, buffer, 8);
386 	//dprintf(ID "sonix_i2c_write_multi: set_regs error 0x%08lx\n", err);
387 	//PRINT((CH ": WriteReg: %s" CT, strerror(err)));
388 	if (err < 0) return err;
389 	err = WaitReadyIIC();
390 	//dprintf(ID "sonix_i2c_write_multi: sonix_i2c_wait_ready error 0x%08lx\n", err);
391 	//PRINT((CH ": Wait: %s" CT, strerror(err)));
392 	if (err) return err;
393 	err = GetStatusIIC();
394 	//dprintf(ID "sonix_i2c_write_multi: sonix_i2c_status error 0x%08lx\n", err);
395 	//PRINT((CH ": Status: %s" CT, strerror(err)));
396 	if (err) return err;
397 	//dprintf(ID "sonix_i2c_write_multi: succeeded\n");
398 	PRINT((CH ": success" CT));
399 	return B_OK;
400 }
401 
402 
403 ssize_t
ReadIIC(uint8 address,uint8 * data)404 SonixCamDevice::ReadIIC(uint8 address, uint8 *data)
405 {
406 	status_t err, lasterr = B_OK;
407 	uint8 buffer[8];
408 	PRINT((CH "(%u, @%p)" CT, address, data));
409 
410 	if (!Sensor())
411 		return B_NO_INIT;
412 	//dprintf(ID "sonix_i2c_write_multi(, %02x, %d, {%02x, %02x, %02x, %02x, %02x})\n", slave, count, d0, d1, d2, d3, d4);
413 	buffer[0] = (1 << 4) | (Sensor()->Use400kHz()?0x01:0)
414 						| (Sensor()->UseRealIIC()?0x80:0);
415 	buffer[1] = Sensor()->IICWriteAddress();
416 	buffer[2] = address;
417 	buffer[7] = 0x10; /* absolutely no idea why V4L2 driver use that value */
418 	err = WriteReg(SN9C102_I2C_SETUP, buffer, 8);
419 	//dprintf(ID "sonix_i2c_write_multi: set_regs error 0x%08lx\n", err);
420 	if (err < 8) return EIO;
421 	err = WaitReadyIIC();
422 	//dprintf(ID "sonix_i2c_write_multi: sonix_i2c_wait_ready error 0x%08lx\n", err);
423 	//if (err) return err;
424 
425 
426 	//dprintf(ID "sonix_i2c_write_multi(, %02x, %d, {%02x, %02x, %02x, %02x, %02x})\n", slave, count, d0, d1, d2, d3, d4);
427 	buffer[0] = (1 << 4) | (Sensor()->Use400kHz()?0x01:0)
428 				  | 0x02 | (Sensor()->UseRealIIC()?0x80:0); /* read 1 byte */
429 	buffer[1] = Sensor()->IICReadAddress();//IICWriteAddress
430 	buffer[7] = 0x10; /* absolutely no idea why V4L2 driver use that value */
431 	err = WriteReg(SN9C102_I2C_SETUP, buffer, 8);
432 	//dprintf(ID "sonix_i2c_write_multi: set_regs error 0x%08lx\n", err);
433 	if (err < 8) return EIO;
434 	err = WaitReadyIIC();
435 	//dprintf(ID "sonix_i2c_write_multi: sonix_i2c_wait_ready error 0x%08lx\n", err);
436 	if (err < B_OK) return err;
437 
438 	err = ReadReg(SN9C102_I2C_DATA0, buffer, 5);
439 	if (err < 5) return EIO;
440 
441 	err = GetStatusIIC();
442 	//dprintf(ID "sonix_i2c_write_multi: sonix_i2c_status error 0x%08lx\n", err);
443 	if (err < B_OK) return err;
444 	//dprintf(ID "sonix_i2c_write_multi: succeeded\n");
445 	if (lasterr) return err;
446 
447 	/* we should get what we want in buffer[4] according to the V4L2 driver...
448 	 * probably because the 5 bytes are a bit shift register?
449 	 */
450 	*data = buffer[4];
451 
452 	return 1;
453 }
454 
455 
456 status_t
SetVideoFrame(BRect frame)457 SonixCamDevice::SetVideoFrame(BRect frame)
458 {
459 	uint16 x, y, width, height;
460 	x = (uint16)frame.left;
461 	y = (uint16)frame.top;
462 	width = (uint16)(frame.right - frame.left + 1) / 16;
463 	height = (uint16)(frame.bottom - frame.top + 1) / 16;
464 	PRINT((CH "(%u, %u, %u, %u)" CT, x, y, width, height));
465 
466 	WriteReg8(SN9C102_H_START, x);
467 	WriteReg8(SN9C102_V_START, y);
468 	WriteReg8(SN9C102_H_SIZE, width);
469 	WriteReg8(SN9C102_V_SIZE, height);
470 	if (Sensor()) {
471 		Sensor()->SetVideoFrame(frame);
472 	}
473 	return CamDevice::SetVideoFrame(frame);
474 }
475 
476 
477 status_t
SetScale(float scale)478 SonixCamDevice::SetScale(float scale)
479 {
480 	status_t err;
481 	uint8 r;
482 	int iscale = (int)scale;
483 
484 	PRINT((CH "(%u)" CT, iscale));
485 	err = ReadReg(SN9C102_SYNC_N_SCALE, &r, 1, true);
486 	if (err < 0)
487 		return err;
488 	r &= ~0x30;
489 	switch (iscale) {
490 	case 1:
491 	case 2:
492 	case 4:
493 		r |= ((iscale-1) << 4);
494 		break;
495 	default:
496 		return EINVAL;
497 	}
498 	err = WriteReg8(SN9C102_SYNC_N_SCALE, r);
499 	return err;
500 }
501 
502 
503 status_t
SetVideoParams(float brightness,float contrast,float hue,float red,float green,float blue)504 SonixCamDevice::SetVideoParams(float brightness, float contrast, float hue, float red, float green, float blue)
505 {
506 	return B_OK;
507 }
508 
509 void
AddParameters(BParameterGroup * group,int32 & index)510 SonixCamDevice::AddParameters(BParameterGroup *group, int32 &index)
511 {
512 	BParameterGroup *g;
513 	BContinuousParameter *p;
514 	CamDevice::AddParameters(group, index);
515 
516 	// R,G,B gains
517 	g = group->MakeGroup("RGB gain");
518 	p = g->MakeContinuousParameter(index++,
519 		B_MEDIA_RAW_VIDEO, "RGB gain",
520 		B_GAIN, "", 1.0, 1.0+(float)(SN9C102_RGB_GAIN_MAX)/8, (float)1.0/8);
521 
522 	p->SetChannelCount(3);
523 #if 0
524 	// Contrast - NON FUNCTIONAL
525 	g = group->MakeGroup("Contrast");
526 	p = g->MakeContinuousParameter(index++,
527 		B_MEDIA_RAW_VIDEO, "Contrast",
528 		B_GAIN, "", 0.0, 1.0, 1.0/256);
529 
530 	// Brightness - NON FUNCTIONAL
531 	g = group->MakeGroup("Brightness");
532 	p = g->MakeContinuousParameter(index++,
533 		B_MEDIA_RAW_VIDEO, "Brightness",
534 		B_GAIN, "", 0.0, 1.0, 1.0/256);
535 
536 #endif
537 }
538 
539 status_t
GetParameterValue(int32 id,bigtime_t * last_change,void * value,size_t * size)540 SonixCamDevice::GetParameterValue(int32 id, bigtime_t *last_change, void *value, size_t *size)
541 {
542 	float *gains;
543 	switch (id - fFirstParameterID) {
544 		case 0:
545 			*size = 3 * sizeof(float);
546 			gains = ((float *)value);
547 			gains[0] = 1.0 + (float)fRGain / 8;
548 			gains[1] = 1.0 + (float)fGGain / 8;
549 			gains[2] = 1.0 + (float)fBGain / 8;
550 			*last_change = fLastParameterChanges;
551 			return B_OK;
552 #if 0
553 		case 1:
554 			*size = sizeof(float);
555 			gains = ((float *)value);
556 			gains[0] = fContrast;
557 			*last_change = fLastParameterChanges;
558 			return B_OK;
559 		case 2:
560 			*size = sizeof(float);
561 			gains = ((float *)value);
562 			gains[0] = fBrightness;
563 			*last_change = fLastParameterChanges;
564 			return B_OK;
565 #endif
566 	}
567 	return B_BAD_VALUE;
568 }
569 
570 status_t
SetParameterValue(int32 id,bigtime_t when,const void * value,size_t size)571 SonixCamDevice::SetParameterValue(int32 id, bigtime_t when, const void *value, size_t size)
572 {
573 	float *gains;
574 	switch (id - fFirstParameterID) {
575 		case 0:
576 			if (!value || (size != 3 * sizeof(float)))
577 				return B_BAD_VALUE;
578 			gains = ((float *)value);
579 			if ((gains[0] == 1.0 + (float)fRGain / 8)
580 				&& (gains[1] == 1.0 + (float)fGGain / 8)
581 				&& (gains[2] == 1.0 + (float)fBGain / 8))
582 				return B_OK;
583 
584 			fRGain = (int)(8 * (gains[0] - 1.0)) & SN9C102_RGB_GAIN_MAX;
585 			fGGain = (int)(8 * (gains[1] - 1.0)) & SN9C102_RGB_GAIN_MAX;
586 			fBGain = (int)(8 * (gains[2] - 1.0)) & SN9C102_RGB_GAIN_MAX;
587 			fLastParameterChanges = when;
588 			PRINT((CH ": gain: %d,%d,%d" CT, fRGain, fGGain, fBGain));
589 			//WriteReg8(SN9C102_R_B_GAIN, (fBGain << 4) | fRGain);	/* red, blue gain = 1+0/8 = 1 */
590 			/* Datasheet says:
591 			 * reg 0x10 [0:3] is rgain, [4:7] is bgain and 0x11 [0:3] is ggain
592 			 * according to sn9c102-1.15 linux driver it's wrong for reg 0x10,
593 			 * but it doesn't seem to work any better for a rev 2 chip.
594 			 * XXX
595 			 */
596 #if 1
597 			WriteReg8(SN9C102_R_GAIN, fRGain);
598 			WriteReg8(SN9C102_B_GAIN, fBGain);
599 			if (fChipVersion >= 3)
600 				WriteReg8(SN9C103_G_GAIN, fGGain);
601 			else
602 				WriteReg8(SN9C102_G_GAIN, (fGGain / 16));
603 #endif
604 #if 0
605 			uint8 buf[2];
606 			buf[0] = (fBGain << 4) | fRGain;
607 			buf[1] = fGGain;
608 			WriteReg(SN9C102_R_B_GAIN, buf, 2);
609 #endif
610 			return B_OK;
611 #if 0
612 		case 1:
613 			if (!value || (size != sizeof(float)))
614 				return B_BAD_VALUE;
615 			gains = ((float *)value);
616 			fContrast = gains[0];
617 			WriteReg8(SN9C10x_CONTRAST, ((uint8)(fContrast * 256)));
618 			return B_OK;
619 		case 2:
620 			if (!value || (size != sizeof(float)))
621 				return B_BAD_VALUE;
622 			gains = ((float *)value);
623 			fBrightness = gains[0];
624 			// it actually ends up writing to SN9C102_V_SIZE...
625 			WriteReg8(SN9C10x_BRIGHTNESS, ((uint8)(fBrightness * 256)));
626 
627 			return B_OK;
628 #endif
629 	}
630 	return B_BAD_VALUE;
631 }
632 
633 
634 
635 size_t
MinRawFrameSize()636 SonixCamDevice::MinRawFrameSize()
637 {
638 	// if (fCompressionEnabled) { ... return ; }
639 	BRect vf(VideoFrame());
640 	size_t w = vf.IntegerWidth()+1;
641 	size_t h = vf.IntegerHeight()+1;
642 	// 1 byte/pixel
643 	return w * h;
644 }
645 
646 
647 size_t
MaxRawFrameSize()648 SonixCamDevice::MaxRawFrameSize()
649 {
650 	// if (fCompressionEnabled) { ... return ; }
651 	return MinRawFrameSize()+1024*0; // fixed size frame (uncompressed)
652 }
653 
654 
655 bool
ValidateStartOfFrameTag(const uint8 * tag,size_t taglen)656 SonixCamDevice::ValidateStartOfFrameTag(const uint8 *tag, size_t taglen)
657 {
658 	// SOF come with an 00, 40, 80, C0 sequence,
659 	// supposedly corresponding with an equal byte in the end tag
660 	fFrameTagState = tag[7] & 0xC0;
661 	PRINT((CH "(, %" B_PRIuSIZE ") state %x" CT, taglen, fFrameTagState));
662 
663 	// which seems to be the same as of the EOF tag
664 	return true;
665 }
666 
667 
668 bool
ValidateEndOfFrameTag(const uint8 * tag,size_t taglen,size_t datalen)669 SonixCamDevice::ValidateEndOfFrameTag(const uint8 *tag, size_t taglen, size_t datalen)
670 {
671 	//PRINT((CH "(, %d) %x == %x" CT, taglen, (tag[0] & 0xC0), fFrameTagState));
672 	// make sure the tag corresponds to the SOF we refer to
673 	if ((tag[0] & 0xC0) != fFrameTagState) {
674 		PRINT((CH ": discarded EOF %x != %x" CT, fFrameTagState, tag[0] & 0xC0));
675 		return false;
676 	}
677 	//PRINT((CH ": validated EOF %x, len %d" CT, fFrameTagState, datalen));
678 	return true;
679 }
680 
681 
682 status_t
GetFrameBitmap(BBitmap ** bm,bigtime_t * stamp)683 SonixCamDevice::GetFrameBitmap(BBitmap **bm, bigtime_t *stamp /* = NULL */)
684 {
685 	BBitmap *b;
686 	CamFrame *f;
687 	status_t err;
688 	PRINT((CH "()" CT));
689 	err = fDeframer->WaitFrame(200000);
690 	if (err < B_OK) { PRINT((CH ": WaitFrame: %s" CT, strerror(err))); }
691 	if (err < B_OK)
692 		return err;
693 	err = fDeframer->GetFrame(&f, stamp);
694 	if (err < B_OK) { PRINT((CH ": GetFrame: %s" CT, strerror(err))); }
695 	if (err < B_OK)
696 		return err;
697 	PRINT((CH ": VideoFrame = %fx%f,%fx%f" CT, VideoFrame().left, VideoFrame().top, VideoFrame().right, VideoFrame().bottom));
698 
699 	long int w = (long)(VideoFrame().right - VideoFrame().left + 1);
700 	long int h = (long)(VideoFrame().bottom - VideoFrame().top + 1);
701 	b = new BBitmap(VideoFrame().OffsetToSelf(0,0), 0, B_RGB32, w*4);
702 	PRINT((CH ": Frame: %ldx%ld" CT, w, h));
703 
704 	bayer2rgb24((unsigned char *)b->Bits(), (unsigned char *)f->Buffer(), w, h);
705 
706 	PRINT((CH ": got 1 frame (len %d)" CT, b->BitsLength()));
707 	*bm = b;
708 	return B_OK;
709 }
710 
711 
712 status_t
FillFrameBuffer(BBuffer * buffer,bigtime_t * stamp)713 SonixCamDevice::FillFrameBuffer(BBuffer *buffer, bigtime_t *stamp)
714 {
715 	CamFrame *f;
716 	status_t err;
717 	PRINT((CH "()" CT));
718 
719 	memset(buffer->Data(), 0, buffer->SizeAvailable());
720 	err = fDeframer->WaitFrame(2000000);
721 	if (err < B_OK) { PRINT((CH ": WaitFrame: %s" CT, strerror(err))); }
722 	if (err < B_OK)
723 		return err;
724 
725 	err = fDeframer->GetFrame(&f, stamp);
726 	if (err < B_OK) { PRINT((CH ": GetFrame: %s" CT, strerror(err))); }
727 	if (err < B_OK)
728 		return err;
729 
730 	long int w = (long)(VideoFrame().right - VideoFrame().left + 1);
731 	long int h = (long)(VideoFrame().bottom - VideoFrame().top + 1);
732 	PRINT((CH ": VideoFrame = %fx%f,%fx%f Frame: %ldx%ld" CT,
733 		VideoFrame().left, VideoFrame().top, VideoFrame().right,
734 		VideoFrame().bottom, w, h));
735 
736 	if (buffer->SizeAvailable() >= (size_t)w*h*4)
737 		bayer2rgb32le((unsigned char *)buffer->Data(), (unsigned char *)f->Buffer(), w, h);
738 
739 	delete f;
740 
741 	PRINT((CH ": available %" B_PRIuSIZE ", required %ld" CT,
742 		buffer->SizeAvailable(), w*h*4));
743 	if (buffer->SizeAvailable() < (size_t)w*h*4)
744 		return E2BIG;
745 	PRINT((CH ": got 1 frame (len %" B_PRIuSIZE ")" CT, buffer->SizeUsed()));
746 	return B_OK;
747 }
748 
749 
750 void
751 /* DEBUG: dump the SN regs */
DumpRegs()752 SonixCamDevice::DumpRegs()
753 {
754 	uint8 regs[SN9C102_REG_COUNT];
755 	status_t err;
756 
757 	//err = sonix_get_regs(dev, SN_ASIC_ID, regs, SN_REG_COUNT);
758 	err = ReadReg(0, regs, SN9C102_REG_COUNT);
759 	if (err < 0)
760 		return;
761 	printf("REG1: %02x %02x %02x %02x  %02x %02x %02x %02x\n",
762 			regs[0], regs[1], regs[2], regs[3], regs[4], regs[5], regs[6], regs[7]);
763 	printf("   2: %02x %02x %02x %02x  %02x %02x %02x %02x\n",
764 			regs[8], regs[9], regs[10], regs[11], regs[12], regs[13], regs[14], regs[15]);
765 	printf("   3: %02x %02x %02x %02x  %02x %02x %02x %02x\n",
766 			regs[16], regs[17], regs[18], regs[19], regs[20], regs[21], regs[22], regs[23]);
767 	printf("   4: %02x %02x %02x %02x  %02x %02x %02x %02x\n",
768 			regs[24], regs[25], regs[26], regs[27], regs[28], regs[29], regs[30], regs[31]);
769 }
770 
771 #if 0
772 
773 status_t
774 SonixCamDevice::SendCommand(uint8 dir, uint8 request, uint16 value,
775 							uint16 index, uint16 length, void* data)
776 {
777 	size_t ret;
778 	if (!GetDevice())
779 		return ENODEV;
780 	if (length > GetDevice()->MaxEndpoint0PacketSize())
781 		return EINVAL;
782 	ret = GetDevice()->ControlTransfer(
783 				USB_REQTYPE_VENDOR | USB_REQTYPE_INTERFACE_OUT | dir,
784 				request, value, index, length, data);
785 	return ret;
786 }
787 #endif
788 
789 
SonixCamDeviceAddon(WebCamMediaAddOn * webcam)790 SonixCamDeviceAddon::SonixCamDeviceAddon(WebCamMediaAddOn* webcam)
791 	: CamDeviceAddon(webcam)
792 {
793 	SetSupportedDevices(kSupportedDevices);
794 }
795 
796 
~SonixCamDeviceAddon()797 SonixCamDeviceAddon::~SonixCamDeviceAddon()
798 {
799 }
800 
801 
802 const char *
BrandName()803 SonixCamDeviceAddon::BrandName()
804 {
805 	return "Sonix";
806 }
807 
808 
809 SonixCamDevice *
Instantiate(CamRoster & roster,BUSBDevice * from)810 SonixCamDeviceAddon::Instantiate(CamRoster &roster, BUSBDevice *from)
811 {
812 	return new SonixCamDevice(*this, from);
813 }
814 
815 extern "C" status_t
B_WEBCAM_MKINTFUNC(sonix)816 B_WEBCAM_MKINTFUNC(sonix)
817 (WebCamMediaAddOn* webcam, CamDeviceAddon **addon)
818 {
819 	*addon = new SonixCamDeviceAddon(webcam);
820 	return B_OK;
821 }
822 
823 // XXX: REMOVE ME
824 
825 
826 
827 /*
828  * BAYER2RGB24 ROUTINE TAKEN FROM:
829  *
830  * Sonix SN9C101 based webcam basic I/F routines
831  * Copyright (C) 2004 Takafumi Mizuno <taka-qce@ls-a.jp>
832  *
833  * Redistribution and use in source and binary forms, with or without
834  * modification, are permitted provided that the following conditions
835  * are met:
836  * 1. Redistributions of source code must retain the above copyright
837  *    notice, this list of conditions and the following disclaimer.
838  * 2. Redistributions in binary form must reproduce the above copyright
839  *    notice, this list of conditions and the following disclaimer in the
840  *    documentation and/or other materials provided with the distribution.
841  *
842  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
843  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
844  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
845  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
846  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
847  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
848  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
849  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
850  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
851  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
852  * SUCH DAMAGE.
853  */
854 
bayer2rgb24(unsigned char * dst,unsigned char * src,long int WIDTH,long int HEIGHT)855 void bayer2rgb24(unsigned char *dst, unsigned char *src, long int WIDTH, long int HEIGHT)
856 {
857     long int i;
858     unsigned char *rawpt, *scanpt;
859     long int size;
860 
861     rawpt = src;
862     scanpt = dst;
863     size = WIDTH*HEIGHT;
864 
865     for ( i = 0; i < size; i++ ) {
866 	if ( (i/WIDTH) % 2 == 0 ) {
867 	    if ( (i % 2) == 0 ) {
868 		/* B */
869 		if ( (i > WIDTH) && ((i % WIDTH) > 0) ) {
870 		    *scanpt++ = (*(rawpt-WIDTH-1)+*(rawpt-WIDTH+1)+
871 				 *(rawpt+WIDTH-1)+*(rawpt+WIDTH+1))/4;	/* R */
872 		    *scanpt++ = (*(rawpt-1)+*(rawpt+1)+
873 				 *(rawpt+WIDTH)+*(rawpt-WIDTH))/4;	/* G */
874 		    *scanpt++ = *rawpt;					/* B */
875 		} else {
876 		    /* first line or left column */
877 		    *scanpt++ = *(rawpt+WIDTH+1);		/* R */
878 		    *scanpt++ = (*(rawpt+1)+*(rawpt+WIDTH))/2;	/* G */
879 		    *scanpt++ = *rawpt;				/* B */
880 		}
881 	    } else {
882 		/* (B)G */
883 		if ( (i > WIDTH) && ((i % WIDTH) < (WIDTH-1)) ) {
884 		    *scanpt++ = (*(rawpt+WIDTH)+*(rawpt-WIDTH))/2;	/* R */
885 		    *scanpt++ = *rawpt;					/* G */
886 		    *scanpt++ = (*(rawpt-1)+*(rawpt+1))/2;		/* B */
887 		} else {
888 		    /* first line or right column */
889 		    *scanpt++ = *(rawpt+WIDTH);	/* R */
890 		    *scanpt++ = *rawpt;		/* G */
891 		    *scanpt++ = *(rawpt-1);	/* B */
892 		}
893 	    }
894 	} else {
895 	    if ( (i % 2) == 0 ) {
896 		/* G(R) */
897 		if ( (i < (WIDTH*(HEIGHT-1))) && ((i % WIDTH) > 0) ) {
898 		    *scanpt++ = (*(rawpt-1)+*(rawpt+1))/2;		/* R */
899 		    *scanpt++ = *rawpt;					/* G */
900 		    *scanpt++ = (*(rawpt+WIDTH)+*(rawpt-WIDTH))/2;	/* B */
901 		} else {
902 		    /* bottom line or left column */
903 		    *scanpt++ = *(rawpt+1);		/* R */
904 		    *scanpt++ = *rawpt;			/* G */
905 		    *scanpt++ = *(rawpt-WIDTH);		/* B */
906 		}
907 	    } else {
908 		/* R */
909 		if ( i < (WIDTH*(HEIGHT-1)) && ((i % WIDTH) < (WIDTH-1)) ) {
910 		    *scanpt++ = *rawpt;					/* R */
911 		    *scanpt++ = (*(rawpt-1)+*(rawpt+1)+
912 				 *(rawpt-WIDTH)+*(rawpt+WIDTH))/4;	/* G */
913 		    *scanpt++ = (*(rawpt-WIDTH-1)+*(rawpt-WIDTH+1)+
914 				 *(rawpt+WIDTH-1)+*(rawpt+WIDTH+1))/4;	/* B */
915 		} else {
916 		    /* bottom line or right column */
917 		    *scanpt++ = *rawpt;				/* R */
918 		    *scanpt++ = (*(rawpt-1)+*(rawpt-WIDTH))/2;	/* G */
919 		    *scanpt++ = *(rawpt-WIDTH-1);		/* B */
920 		}
921 	    }
922 	}
923 	rawpt++;
924     }
925 
926 }
927 
928 /* modified bayer2rgb24 to output rgb-32 little endian (B_RGB32)
929  * François Revol
930  */
931 
bayer2rgb32le(unsigned char * dst,unsigned char * src,long int WIDTH,long int HEIGHT)932 void bayer2rgb32le(unsigned char *dst, unsigned char *src, long int WIDTH, long int HEIGHT)
933 {
934     long int i;
935     unsigned char *rawpt, *scanpt;
936     long int size;
937 
938     rawpt = src;
939     scanpt = dst;
940     size = WIDTH*HEIGHT;
941 
942     for ( i = 0; i < size; i++ ) {
943 	if ( (i/WIDTH) % 2 == 0 ) {
944 	    if ( (i % 2) == 0 ) {
945 		/* B */
946 		if ( (i > WIDTH) && ((i % WIDTH) > 0) ) {
947 		    *scanpt++ = *rawpt;					/* B */
948 		    *scanpt++ = (*(rawpt-1)+*(rawpt+1)+
949 				 *(rawpt+WIDTH)+*(rawpt-WIDTH))/4;	/* G */
950 		    *scanpt++ = (*(rawpt-WIDTH-1)+*(rawpt-WIDTH+1)+
951 				 *(rawpt+WIDTH-1)+*(rawpt+WIDTH+1))/4;	/* R */
952 		} else {
953 		    /* first line or left column */
954 		    *scanpt++ = *rawpt;				/* B */
955 		    *scanpt++ = (*(rawpt+1)+*(rawpt+WIDTH))/2;	/* G */
956 		    *scanpt++ = *(rawpt+WIDTH+1);		/* R */
957 		}
958 	    } else {
959 		/* (B)G */
960 		if ( (i > WIDTH) && ((i % WIDTH) < (WIDTH-1)) ) {
961 		    *scanpt++ = (*(rawpt-1)+*(rawpt+1))/2;		/* B */
962 		    *scanpt++ = *rawpt;					/* G */
963 		    *scanpt++ = (*(rawpt+WIDTH)+*(rawpt-WIDTH))/2;	/* R */
964 		} else {
965 		    /* first line or right column */
966 		    *scanpt++ = *(rawpt-1);	/* B */
967 		    *scanpt++ = *rawpt;		/* G */
968 		    *scanpt++ = *(rawpt+WIDTH);	/* R */
969 		}
970 	    }
971 	} else {
972 	    if ( (i % 2) == 0 ) {
973 		/* G(R) */
974 		if ( (i < (WIDTH*(HEIGHT-1))) && ((i % WIDTH) > 0) ) {
975 		    *scanpt++ = (*(rawpt+WIDTH)+*(rawpt-WIDTH))/2;	/* B */
976 		    *scanpt++ = *rawpt;					/* G */
977 		    *scanpt++ = (*(rawpt-1)+*(rawpt+1))/2;		/* R */
978 		} else {
979 		    /* bottom line or left column */
980 		    *scanpt++ = *(rawpt-WIDTH);		/* B */
981 		    *scanpt++ = *rawpt;			/* G */
982 		    *scanpt++ = *(rawpt+1);		/* R */
983 		}
984 	    } else {
985 		/* R */
986 		if ( i < (WIDTH*(HEIGHT-1)) && ((i % WIDTH) < (WIDTH-1)) ) {
987 		    *scanpt++ = (*(rawpt-WIDTH-1)+*(rawpt-WIDTH+1)+
988 				 *(rawpt+WIDTH-1)+*(rawpt+WIDTH+1))/4;	/* B */
989 		    *scanpt++ = (*(rawpt-1)+*(rawpt+1)+
990 				 *(rawpt-WIDTH)+*(rawpt+WIDTH))/4;	/* G */
991 		    *scanpt++ = *rawpt;					/* R */
992 		} else {
993 		    /* bottom line or right column */
994 		    *scanpt++ = *(rawpt-WIDTH-1);		/* B */
995 		    *scanpt++ = (*(rawpt-1)+*(rawpt-WIDTH))/2;	/* G */
996 		    *scanpt++ = *rawpt;				/* R */
997 		}
998 	    }
999 	}
1000 	rawpt++;
1001 	scanpt++;
1002     }
1003 
1004 }
1005